From 610a699c955cb696558c26fa65112b0a635b5f28 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 21 Jan 2018 20:47:29 +0100 Subject: [PATCH 001/155] Refactor message handlers into injectable components (preparation for #1467) --- .../executable/authme/MessagesCommand.java | 4 +- .../command/help/HelpMessagesService.java | 39 ++-- .../message/AbstractMessageFileHandler.java | 141 ++++++++++++++ .../message/HelpMessagesFileHandler.java | 23 +++ .../authme/message/MessageFileHandler.java | 95 ---------- .../message/MessageFileHandlerProvider.java | 81 -------- .../fr/xephi/authme/message/Messages.java | 23 ++- .../authme/message/MessagesFileHandler.java | 23 +++ .../fr/xephi/authme/ReflectionTestUtils.java | 31 ++++ .../command/help/HelpMessagesServiceTest.java | 68 +++---- .../MessageFileHandlerProviderTest.java | 175 ------------------ .../message/MessagesIntegrationTest.java | 43 +++-- ...lpTranslationGeneratorIntegrationTest.java | 4 +- 13 files changed, 317 insertions(+), 433 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java create mode 100644 src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java delete mode 100644 src/main/java/fr/xephi/authme/message/MessageFileHandler.java delete mode 100644 src/main/java/fr/xephi/authme/message/MessageFileHandlerProvider.java create mode 100644 src/main/java/fr/xephi/authme/message/MessagesFileHandler.java delete mode 100644 src/test/java/fr/xephi/authme/message/MessageFileHandlerProviderTest.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java index bf778d9e9..873281e13 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java @@ -49,7 +49,7 @@ public class MessagesCommand implements ExecutableCommand { try { helpTranslationGenerator.updateHelpFile(); sender.sendMessage("Successfully updated the help file"); - helpMessagesService.reload(); + helpMessagesService.reloadMessagesFile(); } catch (IOException e) { sender.sendMessage("Could not update help file: " + e.getMessage()); ConsoleLogger.logException("Could not update help file:", e); @@ -65,7 +65,7 @@ public class MessagesCommand implements ExecutableCommand { getMessagePath(DEFAULT_LANGUAGE)) .executeCopy(sender); if (isFileUpdated) { - messages.reload(); + messages.reloadMessagesFile(); } } catch (Exception e) { sender.sendMessage("Could not update messages: " + e.getMessage()); diff --git a/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java b/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java index 2a94b675b..994d967b2 100644 --- a/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java +++ b/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java @@ -4,9 +4,7 @@ import com.google.common.base.CaseFormat; import fr.xephi.authme.command.CommandArgumentDescription; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.CommandUtils; -import fr.xephi.authme.initialization.Reloadable; -import fr.xephi.authme.message.MessageFileHandlerProvider; -import fr.xephi.authme.message.MessageFileHandler; +import fr.xephi.authme.message.HelpMessagesFileHandler; import fr.xephi.authme.permission.DefaultPermission; import javax.inject.Inject; @@ -16,20 +14,18 @@ import java.util.stream.Collectors; /** * Manages translatable help messages. */ -public class HelpMessagesService implements Reloadable { +public class HelpMessagesService { private static final String COMMAND_PREFIX = "commands."; private static final String DESCRIPTION_SUFFIX = ".description"; private static final String DETAILED_DESCRIPTION_SUFFIX = ".detailedDescription"; private static final String DEFAULT_PERMISSIONS_PATH = "common.defaultPermissions."; - private final MessageFileHandlerProvider messageFileHandlerProvider; - private MessageFileHandler messageFileHandler; + private final HelpMessagesFileHandler helpMessagesFileHandler; @Inject - HelpMessagesService(MessageFileHandlerProvider messageFileHandlerProvider) { - this.messageFileHandlerProvider = messageFileHandlerProvider; - reload(); + HelpMessagesService(HelpMessagesFileHandler helpMessagesFileHandler) { + this.helpMessagesFileHandler = helpMessagesFileHandler; } /** @@ -40,7 +36,7 @@ public class HelpMessagesService implements Reloadable { */ public CommandDescription buildLocalizedDescription(CommandDescription command) { final String path = COMMAND_PREFIX + getCommandSubPath(command); - if (!messageFileHandler.hasSection(path)) { + if (!helpMessagesFileHandler.hasSection(path)) { // Messages file does not have a section for this command - return the provided command return command; } @@ -72,36 +68,39 @@ public class HelpMessagesService implements Reloadable { } public String getMessage(HelpMessage message) { - return messageFileHandler.getMessage(message.getKey()); + return helpMessagesFileHandler.getMessage(message.getKey()); } public String getMessage(HelpSection section) { - return messageFileHandler.getMessage(section.getKey()); + return helpMessagesFileHandler.getMessage(section.getKey()); } public String getMessage(DefaultPermission defaultPermission) { // e.g. {default_permissions_path}.opOnly for DefaultPermission.OP_ONLY String path = DEFAULT_PERMISSIONS_PATH + getDefaultPermissionsSubPath(defaultPermission); - return messageFileHandler.getMessage(path); + return helpMessagesFileHandler.getMessage(path); } public static String getDefaultPermissionsSubPath(DefaultPermission defaultPermission) { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, defaultPermission.name()); } - @Override - public void reload() { - messageFileHandler = messageFileHandlerProvider.initializeHandler( - lang -> "messages/help_" + lang + ".yml"); - } - private String getText(String path, Supplier defaultTextGetter) { - String message = messageFileHandler.getMessageIfExists(path); + String message = helpMessagesFileHandler.getMessageIfExists(path); return message == null ? defaultTextGetter.get() : message; } + + /** + * Triggers a reload of the help messages file. Note that this method is not needed + * to be called for /authme reload. + */ + public void reloadMessagesFile() { + helpMessagesFileHandler.reload(); + } + /** * Returns the command subpath for the given command (i.e. the path to the translations for the given * command under "commands"). diff --git a/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java b/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java new file mode 100644 index 000000000..c5ff01b44 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java @@ -0,0 +1,141 @@ +package fr.xephi.authme.message; + +import com.google.common.annotations.VisibleForTesting; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.initialization.Reloadable; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.util.FileUtils; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Handles a YAML message file with a default file fallback. + */ +public abstract class AbstractMessageFileHandler implements Reloadable { + + private static final String DEFAULT_LANGUAGE = "en"; + + @DataFolder + @Inject + private File dataFolder; + + @Inject + private Settings settings; + + private String filename; + private FileConfiguration configuration; + private final String defaultFile; + private FileConfiguration defaultConfiguration; + + protected AbstractMessageFileHandler() { + this.defaultFile = createFilePath(DEFAULT_LANGUAGE); + } + + @Override + @PostConstruct + public void reload() { + String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); + filename = createFilePath(language); + File messagesFile = initializeFile(filename); + configuration = YamlConfiguration.loadConfiguration(messagesFile); + } + + /** + * Returns whether the message file configuration has an entry at the given path. + * + * @param path the path to verify + * @return true if an entry exists for the path in the messages file, false otherwise + */ + public boolean hasSection(String path) { + return configuration.get(path) != null; + } + + /** + * Returns the message for the given key. + * + * @param key the key to retrieve the message for + * @return the message + */ + public String getMessage(String key) { + String message = configuration.getString(key); + + if (message == null) { + ConsoleLogger.warning("Error getting message with key '" + key + "'. " + + "Please update your config file '" + filename + "' or run " + getUpdateCommand()); + return getDefault(key); + } + return message; + } + + /** + * Returns the message for the given key only if it exists, + * i.e. without falling back to the default file. + * + * @param key the key to retrieve the message for + * @return the message, or {@code null} if not available + */ + public String getMessageIfExists(String key) { + return configuration.getString(key); + } + + /** + * Gets the message from the default file. + * + * @param key the key to retrieve the message for + * @return the message from the default file + */ + private String getDefault(String key) { + if (defaultConfiguration == null) { + InputStream stream = FileUtils.getResourceFromJar(defaultFile); + defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream)); + } + String message = defaultConfiguration.getString(key); + return message == null + ? "Error retrieving message '" + key + "'" + : message; + } + + /** + * Creates the path to the messages file for the given language code. + * + * @param language the language code + * @return path to the message file for the given language + */ + protected abstract String createFilePath(String language); + + /** + * @return command with which the messages file can be updated; output when a message is missing from the file + */ + protected abstract String getUpdateCommand(); + + /** + * Copies the messages file from the JAR to the local messages/ folder if it doesn't exist. + * + * @param filePath path to the messages file to use + * @return the messages file to use + */ + @VisibleForTesting + File initializeFile(String filePath) { + File file = new File(dataFolder, filePath); + // Check that JAR file exists to avoid logging an error + if (FileUtils.getResourceFromJar(filePath) != null && FileUtils.copyFileFromResource(file, filePath)) { + return file; + } + + if (FileUtils.copyFileFromResource(file, defaultFile)) { + return file; + } else { + ConsoleLogger.warning("Wanted to copy default messages file '" + defaultFile + + "' from JAR but it didn't exist"); + return null; + } + } +} diff --git a/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java b/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java new file mode 100644 index 000000000..18eed4ae8 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java @@ -0,0 +1,23 @@ +package fr.xephi.authme.message; + +import javax.inject.Inject; + +/** + * File handler for the help_xx.yml resource. + */ +public class HelpMessagesFileHandler extends AbstractMessageFileHandler { + + @Inject // Trigger injection in the superclass + HelpMessagesFileHandler() { + } + + @Override + protected String createFilePath(String language) { + return "messages/help_" + language + ".yml"; + } + + @Override + protected String getUpdateCommand() { + return "/authme messages help"; + } +} diff --git a/src/main/java/fr/xephi/authme/message/MessageFileHandler.java b/src/main/java/fr/xephi/authme/message/MessageFileHandler.java deleted file mode 100644 index 5d19e8f40..000000000 --- a/src/main/java/fr/xephi/authme/message/MessageFileHandler.java +++ /dev/null @@ -1,95 +0,0 @@ -package fr.xephi.authme.message; - -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.util.FileUtils; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; - -/** - * Handles a YAML message file with a default file fallback. - */ -public class MessageFileHandler { - - // regular file - private final String filename; - private final FileConfiguration configuration; - private final String updateAddition; - // default file - private final String defaultFile; - private FileConfiguration defaultConfiguration; - - /** - * Constructor. - * - * @param file the file to use for messages - * @param defaultFile the default file from the JAR to use if no message is found - * @param updateCommand command to update the messages file (nullable) to show in error messages - */ - public MessageFileHandler(File file, String defaultFile, String updateCommand) { - this.filename = file.getName(); - this.configuration = YamlConfiguration.loadConfiguration(file); - this.defaultFile = defaultFile; - this.updateAddition = updateCommand == null - ? "" - : " (or run " + updateCommand + ")"; - } - - /** - * Returns whether the message file configuration has an entry at the given path. - * - * @param path the path to verify - * @return true if an entry exists for the path in the messages file, false otherwise - */ - public boolean hasSection(String path) { - return configuration.get(path) != null; - } - - /** - * Returns the message for the given key. - * - * @param key the key to retrieve the message for - * @return the message - */ - public String getMessage(String key) { - String message = configuration.getString(key); - - if (message == null) { - ConsoleLogger.warning("Error getting message with key '" + key + "'. " - + "Please update your config file '" + filename + "'" + updateAddition); - return getDefault(key); - } - return message; - } - - /** - * Returns the message for the given key only if it exists, - * i.e. without falling back to the default file. - * - * @param key the key to retrieve the message for - * @return the message, or {@code null} if not available - */ - public String getMessageIfExists(String key) { - return configuration.getString(key); - } - - /** - * Gets the message from the default file. - * - * @param key the key to retrieve the message for - * @return the message from the default file - */ - private String getDefault(String key) { - if (defaultConfiguration == null) { - InputStream stream = FileUtils.getResourceFromJar(defaultFile); - defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream)); - } - String message = defaultConfiguration.getString(key); - return message == null - ? "Error retrieving message '" + key + "'" - : message; - } -} diff --git a/src/main/java/fr/xephi/authme/message/MessageFileHandlerProvider.java b/src/main/java/fr/xephi/authme/message/MessageFileHandlerProvider.java deleted file mode 100644 index 5b809b2c9..000000000 --- a/src/main/java/fr/xephi/authme/message/MessageFileHandlerProvider.java +++ /dev/null @@ -1,81 +0,0 @@ -package fr.xephi.authme.message; - -import com.google.common.annotations.VisibleForTesting; -import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.properties.PluginSettings; -import fr.xephi.authme.util.FileUtils; - -import javax.inject.Inject; -import java.io.File; -import java.util.function.Function; - -/** - * Injectable creator of {@link MessageFileHandler} instances. - * - * @see MessageFileHandler - */ -public class MessageFileHandlerProvider { - - private static final String DEFAULT_LANGUAGE = "en"; - - @Inject - @DataFolder - private File dataFolder; - @Inject - private Settings settings; - - MessageFileHandlerProvider() { - } - - /** - * Initializes a message file handler with the messages file of the configured language. - * Ensures beforehand that the messages file exists or creates it otherwise. - * - * @param pathBuilder function taking the configured language code as argument and returning the messages file - * @return the message file handler - */ - public MessageFileHandler initializeHandler(Function pathBuilder) { - return initializeHandler(pathBuilder, null); - } - - /** - * Initializes a message file handler with the messages file of the configured language. - * Ensures beforehand that the messages file exists or creates it otherwise. - * - * @param pathBuilder function taking the configured language code as argument and returning the messages file - * @param updateCommand command to run to update the languages file (nullable) - * @return the message file handler - */ - public MessageFileHandler initializeHandler(Function pathBuilder, String updateCommand) { - String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); - return new MessageFileHandler( - initializeFile(language, pathBuilder), - pathBuilder.apply(DEFAULT_LANGUAGE), - updateCommand); - } - - /** - * Copies the messages file from the JAR if it doesn't exist. - * - * @param language the configured language code - * @param pathBuilder function returning message file name with language as argument - * @return the messages file to use - */ - @VisibleForTesting - File initializeFile(String language, Function pathBuilder) { - String filePath = pathBuilder.apply(language); - File file = new File(dataFolder, filePath); - // Check that JAR file exists to avoid logging an error - if (FileUtils.getResourceFromJar(filePath) != null && FileUtils.copyFileFromResource(file, filePath)) { - return file; - } - - String defaultFilePath = pathBuilder.apply(DEFAULT_LANGUAGE); - if (FileUtils.copyFileFromResource(file, defaultFilePath)) { - return file; - } - return null; - } - -} diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index d7fdcec89..8c9f2f42e 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -2,7 +2,6 @@ package fr.xephi.authme.message; import com.google.common.collect.ImmutableMap; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.util.expiring.Duration; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -14,7 +13,7 @@ import java.util.concurrent.TimeUnit; /** * Class for retrieving and sending translatable messages to players. */ -public class Messages implements Reloadable { +public class Messages { // Custom Authme tag replaced to new line private static final String NEWLINE_TAG = "%nl%"; @@ -33,16 +32,14 @@ public class Messages implements Reloadable { .put(TimeUnit.HOURS, MessageKey.HOURS) .put(TimeUnit.DAYS, MessageKey.DAYS).build(); - private final MessageFileHandlerProvider messageFileHandlerProvider; - private MessageFileHandler messageFileHandler; + private MessagesFileHandler messagesFileHandler; /* * Constructor. */ @Inject - Messages(MessageFileHandlerProvider messageFileHandlerProvider) { - this.messageFileHandlerProvider = messageFileHandlerProvider; - reload(); + Messages(MessagesFileHandler messagesFileHandler) { + this.messagesFileHandler = messagesFileHandler; } /** @@ -113,7 +110,7 @@ public class Messages implements Reloadable { */ private String retrieveMessage(MessageKey key) { return formatMessage( - messageFileHandler.getMessage(key.getKey())); + messagesFileHandler.getMessage(key.getKey())); } /** @@ -138,10 +135,12 @@ public class Messages implements Reloadable { return message; } - @Override - public void reload() { - this.messageFileHandler = messageFileHandlerProvider - .initializeHandler(lang -> "messages/messages_" + lang + ".yml", "/authme messages"); + /** + * Triggers a reload of the messages file. Note that this method is not necessary + * to be called for /authme reload. + */ + public void reloadMessagesFile() { + messagesFileHandler.reload(); } private static String formatMessage(String message) { diff --git a/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java b/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java new file mode 100644 index 000000000..febe26c52 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java @@ -0,0 +1,23 @@ +package fr.xephi.authme.message; + +import javax.inject.Inject; + +/** + * File handler for the messages_xx.yml resource. + */ +public class MessagesFileHandler extends AbstractMessageFileHandler { + + @Inject // Trigger injection in the superclass + MessagesFileHandler() { + } + + @Override + protected String createFilePath(String language) { + return "messages/messages_" + language + ".yml"; + } + + @Override + protected String getUpdateCommand() { + return "/authme messages"; + } +} diff --git a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java index b48f5efc7..033bed276 100644 --- a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java +++ b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java @@ -1,5 +1,8 @@ package fr.xephi.authme; +import ch.jalu.injector.handlers.postconstruct.PostConstructMethodInvoker; + +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -102,4 +105,32 @@ public final class ReflectionTestUtils { throw new UnsupportedOperationException("Could not invoke method '" + method + "'", e); } } + + /** + * Runs all methods annotated with {@link javax.annotation.PostConstruct} on the given instance + * (including such methods on superclasses). + * + * @param instance the instance to process + */ + public static void invokePostConstructMethods(Object instance) { + // Use the implementation of the injector to invoke all @PostConstruct methods the same way + new PostConstructMethodInvoker().postProcess(instance, null, null); + } + + /** + * Creates a new instance of the given class, using a no-args constructor (which may be hidden). + * + * @param clazz the class to instantiate + * @param the class' type + * @return the created instance + */ + public static T newInstance(Class clazz) { + try { + Constructor constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (ReflectiveOperationException e) { + throw new UnsupportedOperationException("Could not invoke no-args constructor of class " + clazz, e); + } + } } diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java index bef86fca4..c7202a92f 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java @@ -1,64 +1,55 @@ package fr.xephi.authme.command.help; -import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; -import ch.jalu.injector.testing.InjectDelayed; +import com.google.common.io.Files; +import fr.xephi.authme.ReflectionTestUtils; +import fr.xephi.authme.TestHelper; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.TestCommandsUtil; -import fr.xephi.authme.message.MessageFileHandler; -import fr.xephi.authme.message.MessageFileHandlerProvider; +import fr.xephi.authme.message.AbstractMessageFileHandler; +import fr.xephi.authme.message.HelpMessagesFileHandler; import fr.xephi.authme.permission.DefaultPermission; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.PluginSettings; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; +import org.junit.rules.TemporaryFolder; +import java.io.File; +import java.io.IOException; import java.util.Collection; -import java.util.function.Function; -import static fr.xephi.authme.TestHelper.getJarFile; import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.mock; /** * Test for {@link HelpMessagesService}. */ -@RunWith(DelayedInjectionRunner.class) public class HelpMessagesServiceTest { private static final String TEST_FILE = "/fr/xephi/authme/command/help/help_test.yml"; private static final Collection COMMANDS = TestCommandsUtil.generateCommands(); - @InjectDelayed private HelpMessagesService helpMessagesService; - @Mock - private MessageFileHandlerProvider messageFileHandlerProvider; + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + private File dataFolder; - @BeforeInjecting - @SuppressWarnings("unchecked") - public void initializeHandler() { - MessageFileHandler handler = new MessageFileHandler(getJarFile(TEST_FILE), "messages/messages_en.yml", null); - given(messageFileHandlerProvider.initializeHandler(any(Function.class))).willReturn(handler); - } + @Before + public void initializeHandler() throws IOException, InstantiationException, IllegalAccessException { + dataFolder = temporaryFolder.newFolder(); + new File(dataFolder, "messages").mkdirs(); + File messagesFile = new File(dataFolder, "messages/help_test.yml"); + Files.copy(TestHelper.getJarFile(TEST_FILE), messagesFile); - @Test - @SuppressWarnings("unchecked") - public void shouldUseExistingFileAsTextFile() { - // given / when / then - ArgumentCaptor> functionCaptor = ArgumentCaptor.forClass(Function.class); - verify(messageFileHandlerProvider).initializeHandler(functionCaptor.capture()); - Function helpFilePathBuilder = functionCaptor.getValue(); - String defaultFilePath = helpFilePathBuilder.apply("en"); - assertThat(getClass().getClassLoader().getResource(defaultFilePath), not(nullValue())); + HelpMessagesFileHandler helpMessagesFileHandler = createMessagesFileHandler(); + helpMessagesService = new HelpMessagesService(helpMessagesFileHandler); } @Test @@ -154,4 +145,15 @@ public class HelpMessagesServiceTest { // then assertThat(description, equalTo(command.getDescription())); } + + private HelpMessagesFileHandler createMessagesFileHandler() throws IllegalAccessException, InstantiationException { + Settings settings = mock(Settings.class); + given(settings.getProperty(PluginSettings.MESSAGES_LANGUAGE)).willReturn("test"); + + HelpMessagesFileHandler messagesFileHandler = ReflectionTestUtils.newInstance(HelpMessagesFileHandler.class); + ReflectionTestUtils.setField(AbstractMessageFileHandler.class, messagesFileHandler, "settings", settings); + ReflectionTestUtils.setField(AbstractMessageFileHandler.class, messagesFileHandler, "dataFolder", dataFolder); + ReflectionTestUtils.invokePostConstructMethods(messagesFileHandler); + return messagesFileHandler; + } } diff --git a/src/test/java/fr/xephi/authme/message/MessageFileHandlerProviderTest.java b/src/test/java/fr/xephi/authme/message/MessageFileHandlerProviderTest.java deleted file mode 100644 index 044b3411d..000000000 --- a/src/test/java/fr/xephi/authme/message/MessageFileHandlerProviderTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package fr.xephi.authme.message; - -import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; -import ch.jalu.injector.testing.InjectDelayed; -import com.google.common.io.Files; -import fr.xephi.authme.TestHelper; -import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.properties.PluginSettings; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; - -import java.io.File; -import java.io.IOException; -import java.util.function.Function; - -import static fr.xephi.authme.TestHelper.getJarFile; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; - -/** - * Test for {@link MessageFileHandlerProvider}. - */ -@RunWith(DelayedInjectionRunner.class) -public class MessageFileHandlerProviderTest { - - private static final Function MESSAGES_BUILDER = lang -> "messages/messages_" + lang + ".yml"; - - @InjectDelayed - private MessageFileHandlerProvider handlerProvider; - - @DataFolder - private File dataFolder; - @Mock - private Settings settings; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @BeforeClass - public static void initLogger() { - TestHelper.setupLogger(); - } - - @BeforeInjecting - public void initDataFolder() throws IOException { - this.dataFolder = temporaryFolder.newFolder(); - } - - @Test - public void shouldReturnExistingMessagesFile() { - // given - String language = "fr"; - // use another language file to make sure we won't copy over it - String jarFile = "/messages/messages_it.yml"; - File existingFile = copyFromJar(MESSAGES_BUILDER.apply(language), jarFile); - - // when - File result = handlerProvider.initializeFile(language, MESSAGES_BUILDER); - - // then - assertThat(result, equalTo(existingFile)); - assertThat(result.exists(), equalTo(true)); - assertThat(result, equalToFile(getJarFile(jarFile))); - } - - @Test - public void shouldCopyFromJarFile() { - // given - String language = "nl"; - - // when - File result = handlerProvider.initializeFile(language, MESSAGES_BUILDER); - - // then - File expectedFile = new File(dataFolder, MESSAGES_BUILDER.apply(language)); - assertThat(result, equalTo(expectedFile)); - assertThat(result.exists(), equalTo(true)); - assertThat(result, equalToFile(getJarFile("/messages/messages_nl.yml"))); - } - - @Test - public void shouldCopyDefaultFileForUnknownLanguage() { - // given - String language = "zxx"; - - // when - File result = handlerProvider.initializeFile(language, MESSAGES_BUILDER); - - // then - File expectedFile = new File(dataFolder, MESSAGES_BUILDER.apply(language)); - assertThat(result, equalTo(expectedFile)); - assertThat(result.exists(), equalTo(true)); - assertThat(result, equalToFile(getJarFile("/messages/messages_en.yml"))); - } - - @Test - public void shouldReturnNullForNonExistentDefault() { - // given - Function fileFunction = s -> "bogus"; - - // when - File result = handlerProvider.initializeFile("gsw", fileFunction); - - // then - assertThat(result, nullValue()); - } - - @Test - public void shouldCreateHandler() { - // given - String language = "fr"; - given(settings.getProperty(PluginSettings.MESSAGES_LANGUAGE)).willReturn(language); - - MessageFileHandlerProvider provider = Mockito.spy(handlerProvider); - Function fileFunction = lang -> "file_" + lang + ".txt"; - File file = new File(dataFolder, "some_file.txt"); - doReturn(file).when(provider).initializeFile(language, fileFunction); - - // when - MessageFileHandler handler = provider.initializeHandler(fileFunction); - - // then - assertThat(handler, not(nullValue())); - verify(settings).getProperty(PluginSettings.MESSAGES_LANGUAGE); - verify(provider).initializeFile(language, fileFunction); - } - - private File copyFromJar(String path, String jarPath) { - File file = new File(dataFolder, path); - if (!file.getParentFile().mkdirs()) { - throw new IllegalStateException("Could not create folders for '" + file + "'"); - } - try { - Files.copy(getJarFile(jarPath), file); - return file; - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - private static Matcher equalToFile(File file) { - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(File item) { - try { - return Files.equal(item, file); - } catch (IOException e) { - throw new IllegalStateException("IOException during matcher evaluation", e); - } - } - - @Override - public void describeTo(Description description) { - description.appendText("Equal to file '" + file + "'"); - } - }; - } - - -} diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 79e873176..5f9c80cae 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -1,21 +1,28 @@ package fr.xephi.authme.message; import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.expiring.Duration; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.io.File; +import java.io.IOException; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.logging.Logger; import static org.hamcrest.Matchers.arrayWithSize; @@ -23,7 +30,6 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -38,9 +44,12 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat; public class MessagesIntegrationTest { private static final String YML_TEST_FILE = TestHelper.PROJECT_ROOT + "message/messages_test.yml"; - private static final String YML_DEFAULT_TEST_FILE = "messages/messages_en.yml"; private Messages messages; + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + private File dataFolder; + @BeforeClass public static void setup() { TestHelper.setupLogger(); @@ -55,10 +64,15 @@ public class MessagesIntegrationTest { * file that should contain all messages, but again, for testing, it just contains a few. */ @Before - public void setUpMessages() { - File testFile = TestHelper.getJarFile(YML_TEST_FILE); - MessageFileHandlerProvider provider = providerReturning(testFile, YML_DEFAULT_TEST_FILE); - messages = new Messages(provider); + public void setUpMessages() throws IOException { + dataFolder = temporaryFolder.newFolder(); + File testFile = new File(dataFolder, "messages/messages_test.yml"); + new File(dataFolder, "messages").mkdirs(); + FileUtils.create(testFile); + Files.copy(TestHelper.getJarFile(YML_TEST_FILE), testFile); + + MessagesFileHandler fileHandler = createMessagesFileHandler(); + messages = new Messages(fileHandler); } @Test @@ -254,11 +268,14 @@ public class MessagesIntegrationTest { } } - @SuppressWarnings("unchecked") - private static MessageFileHandlerProvider providerReturning(File file, String defaultFile) { - MessageFileHandlerProvider handler = mock(MessageFileHandlerProvider.class); - given(handler.initializeHandler(any(Function.class), anyString())) - .willReturn(new MessageFileHandler(file, defaultFile, "/authme messages")); - return handler; + private MessagesFileHandler createMessagesFileHandler() { + Settings settings = mock(Settings.class); + given(settings.getProperty(PluginSettings.MESSAGES_LANGUAGE)).willReturn("test"); + + MessagesFileHandler messagesFileHandler = new MessagesFileHandler(); + ReflectionTestUtils.setField(AbstractMessageFileHandler.class, messagesFileHandler, "settings", settings); + ReflectionTestUtils.setField(AbstractMessageFileHandler.class, messagesFileHandler, "dataFolder", dataFolder); + ReflectionTestUtils.invokePostConstructMethods(messagesFileHandler); + return messagesFileHandler; } } diff --git a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java b/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java index 1ebd108f9..3e094cc2b 100644 --- a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java @@ -10,7 +10,7 @@ import fr.xephi.authme.command.help.HelpMessage; import fr.xephi.authme.command.help.HelpMessagesService; import fr.xephi.authme.command.help.HelpSection; import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.message.MessageFileHandlerProvider; +import fr.xephi.authme.message.HelpMessagesFileHandler; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import org.bukkit.configuration.MemorySection; @@ -44,7 +44,7 @@ public class HelpTranslationGeneratorIntegrationTest { @InjectDelayed private HelpMessagesService helpMessagesService; @InjectDelayed - private MessageFileHandlerProvider messageFileHandlerProvider; + private HelpMessagesFileHandler helpMessagesFileHandler; @InjectDelayed private CommandInitializer commandInitializer; From b3a191d7e282db813a5a5b10ed89d21da456b3b0 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 22 Jan 2018 19:52:39 +0100 Subject: [PATCH 002/155] Minor: simplify CheckMessageKeyUsages task and make check more strict --- .../xephi/authme/ClassesConsistencyTest.java | 20 ++---------- .../tools/messages/CheckMessageKeyUsages.java | 31 ++++++------------- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java index 38b2fbf43..db74ce6f2 100644 --- a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java @@ -123,29 +123,13 @@ public class ClassesConsistencyTest { } /** - * Prints out the field with (most of) its modifiers. + * Prints out the field with its modifiers. * * @param field the field to format * @return description of the field */ private static String formatField(Field field) { - String modifiersText = ""; - int modifiers = field.getModifiers(); - if (Modifier.isPublic(modifiers)) { - modifiersText += "public "; - } else if (Modifier.isProtected(modifiers)) { - modifiersText += "protected "; - } else if (Modifier.isPrivate(modifiers)) { - modifiersText += "private "; - } - - if (Modifier.isStatic(modifiers)) { - modifiersText += "static "; - } - if (Modifier.isFinal(modifiers)) { - modifiersText += "final "; - } - + String modifiersText = Modifier.toString(field.getModifiers()); return String.format("[%s] %s %s %s", field.getDeclaringClass().getSimpleName(), modifiersText.trim(), field.getType().getSimpleName(), field.getName()); } diff --git a/src/test/java/tools/messages/CheckMessageKeyUsages.java b/src/test/java/tools/messages/CheckMessageKeyUsages.java index 51b8eb12b..d43ad5d07 100644 --- a/src/test/java/tools/messages/CheckMessageKeyUsages.java +++ b/src/test/java/tools/messages/CheckMessageKeyUsages.java @@ -2,24 +2,22 @@ package tools.messages; import com.google.common.collect.Lists; import fr.xephi.authme.message.MessageKey; +import tools.utils.AutoToolTask; import tools.utils.FileIoUtils; -import tools.utils.ToolTask; import tools.utils.ToolsConstants; import java.io.File; import java.util.ArrayList; import java.util.List; -import java.util.Scanner; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Collectors; import static java.util.Arrays.asList; /** * Task which checks for {@link MessageKey} usages. */ -public class CheckMessageKeyUsages implements ToolTask { +public class CheckMessageKeyUsages implements AutoToolTask { private static final Predicate SHOULD_CHECK_FILE = file -> file.getName().endsWith(".java") && !file.getName().endsWith("MessageKey.java"); @@ -30,24 +28,13 @@ public class CheckMessageKeyUsages implements ToolTask { } @Override - public void execute(Scanner scanner) { - System.out.println("Enter a message key to find the files where it is used"); - System.out.println("Enter empty line to search for all unused message keys"); - String key = scanner.nextLine(); - - if (key.trim().isEmpty()) { - List unusedKeys = findUnusedKeys(); - if (unusedKeys.isEmpty()) { - System.out.println("No unused MessageKey entries found :)"); - } else { - System.out.println("Did not find usages for keys:\n- " + - String.join("\n- ", Lists.transform(unusedKeys, MessageKey::name))); - } + public void executeDefault() { + List unusedKeys = findUnusedKeys(); + if (unusedKeys.isEmpty()) { + System.out.println("No unused MessageKey entries found :)"); } else { - MessageKey messageKey = MessageKey.valueOf(key); - List filesUsingKey = findUsagesOfKey(messageKey); - System.out.println("The following files use '" + key + "':\n- " - + filesUsingKey.stream().map(File::getName).collect(Collectors.joining("\n- "))); + System.out.println("Did not find usages for keys:\n- " + + String.join("\n- ", Lists.transform(unusedKeys, MessageKey::name))); } } @@ -57,7 +44,7 @@ public class CheckMessageKeyUsages implements ToolTask { Consumer fileProcessor = file -> { String source = FileIoUtils.readFromFile(file.toPath()); - keys.removeIf(key -> source.contains(key.name())); + keys.removeIf(key -> source.contains("MessageKey." + key.name())); }; walkJavaFileTree(sourceFolder, fileProcessor); From 820e443b8111e884b97c2d0bd49ff33016f45b38 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Wed, 24 Jan 2018 22:19:25 +0100 Subject: [PATCH 003/155] #1467 Implement messages file migration - Create messages updater called when a messages YML file is loaded - Work in progress - Does not yet include changes to any message keys --- .../executable/authme/MessagesCommand.java | 38 ----- .../message/AbstractMessageFileHandler.java | 19 ++- .../authme/message/MessagesFileHandler.java | 30 +++- .../message/updater/JarMessageSource.java | 57 +++++++ .../message/updater/MessageUpdater.java | 90 +++++++++++ .../updater/OldMessageKeysMigrater.java | 140 ++++++++++++++++++ .../xephi/authme/service/MessageUpdater.java | 138 ----------------- .../message/MessagesIntegrationTest.java | 2 + .../message/updater/MessageUpdaterTest.java | 71 +++++++++ 9 files changed, 405 insertions(+), 180 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java create mode 100644 src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java create mode 100644 src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java delete mode 100644 src/main/java/fr/xephi/authme/service/MessageUpdater.java create mode 100644 src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java index 873281e13..6e55c4cbe 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java @@ -3,16 +3,10 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.help.HelpMessagesService; -import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.message.Messages; import fr.xephi.authme.service.HelpTranslationGenerator; -import fr.xephi.authme.service.MessageUpdater; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.properties.PluginSettings; import org.bukkit.command.CommandSender; import javax.inject.Inject; -import java.io.File; import java.io.IOException; import java.util.List; @@ -22,15 +16,6 @@ import java.util.List; */ public class MessagesCommand implements ExecutableCommand { - private static final String DEFAULT_LANGUAGE = "en"; - - @Inject - private Settings settings; - @Inject - @DataFolder - private File dataFolder; - @Inject - private Messages messages; @Inject private HelpTranslationGenerator helpTranslationGenerator; @Inject @@ -40,8 +25,6 @@ public class MessagesCommand implements ExecutableCommand { public void executeCommand(CommandSender sender, List arguments) { if (!arguments.isEmpty() && "help".equalsIgnoreCase(arguments.get(0))) { updateHelpFile(sender); - } else { - updateMessagesFile(sender); } } @@ -55,25 +38,4 @@ public class MessagesCommand implements ExecutableCommand { ConsoleLogger.logException("Could not update help file:", e); } } - - private void updateMessagesFile(CommandSender sender) { - final String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); - try { - boolean isFileUpdated = new MessageUpdater( - new File(dataFolder, getMessagePath(language)), - getMessagePath(language), - getMessagePath(DEFAULT_LANGUAGE)) - .executeCopy(sender); - if (isFileUpdated) { - messages.reloadMessagesFile(); - } - } catch (Exception e) { - sender.sendMessage("Could not update messages: " + e.getMessage()); - ConsoleLogger.logException("Could not update messages:", e); - } - } - - private static String getMessagePath(String code) { - return "messages/messages_" + code + ".yml"; - } } diff --git a/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java b/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java index c5ff01b44..f88c6f3f5 100644 --- a/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java +++ b/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java @@ -21,7 +21,7 @@ import java.io.InputStreamReader; */ public abstract class AbstractMessageFileHandler implements Reloadable { - private static final String DEFAULT_LANGUAGE = "en"; + protected static final String DEFAULT_LANGUAGE = "en"; @DataFolder @Inject @@ -34,20 +34,33 @@ public abstract class AbstractMessageFileHandler implements Reloadable { private FileConfiguration configuration; private final String defaultFile; private FileConfiguration defaultConfiguration; + private final String updateCommandAddition; protected AbstractMessageFileHandler() { this.defaultFile = createFilePath(DEFAULT_LANGUAGE); + String updateCommand = getUpdateCommand(); + this.updateCommandAddition = updateCommand == null + ? "" + : " or run " + updateCommand; } @Override @PostConstruct public void reload() { - String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); + String language = getLanguage(); filename = createFilePath(language); File messagesFile = initializeFile(filename); configuration = YamlConfiguration.loadConfiguration(messagesFile); } + protected String getLanguage() { + return settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); + } + + protected File getUserLanguageFile() { + return new File(dataFolder, filename); + } + /** * Returns whether the message file configuration has an entry at the given path. * @@ -69,7 +82,7 @@ public abstract class AbstractMessageFileHandler implements Reloadable { if (message == null) { ConsoleLogger.warning("Error getting message with key '" + key + "'. " - + "Please update your config file '" + filename + "' or run " + getUpdateCommand()); + + "Please update your config file '" + filename + "'" + updateCommandAddition); return getDefault(key); } return message; diff --git a/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java b/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java index febe26c52..716bf33ed 100644 --- a/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java +++ b/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java @@ -1,5 +1,8 @@ package fr.xephi.authme.message; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.message.updater.MessageUpdater; + import javax.inject.Inject; /** @@ -7,10 +10,35 @@ import javax.inject.Inject; */ public class MessagesFileHandler extends AbstractMessageFileHandler { - @Inject // Trigger injection in the superclass + // TODO #1467: With the migration the messages handler has become so different that it would be worth + // to remove the extension and extract common features into a helper class instead + + @Inject + private MessageUpdater messageUpdater; + MessagesFileHandler() { } + @Override + public void reload() { + reloadInternal(false); + } + + private void reloadInternal(boolean isFromReload) { + super.reload(); + + String language = getLanguage(); + boolean hasChange = messageUpdater.migrateAndSave( + getUserLanguageFile(), createFilePath(language), createFilePath(DEFAULT_LANGUAGE)); + if (hasChange) { + if (isFromReload) { + ConsoleLogger.warning("Migration after reload attempt"); + } else { + reloadInternal(true); + } + } + } + @Override protected String createFilePath(String language) { return "messages/messages_" + language + ".yml"; diff --git a/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java b/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java new file mode 100644 index 000000000..13858bfc2 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java @@ -0,0 +1,57 @@ +package fr.xephi.authme.message.updater; + +import ch.jalu.configme.properties.Property; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.util.FileUtils; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Returns messages from the JAR's message files. Favors a local JAR (e.g. messages_nl.yml) + * before falling back to the default language (messages_en.yml). + */ +public class JarMessageSource { + + private final FileConfiguration localJarConfiguration; + private final FileConfiguration defaultJarConfiguration; + + /** + * Constructor. + * + * @param localJarPath path to the messages file of the language the plugin is configured to use (may not exist) + * @param defaultJarPath path to the default messages file in the JAR (must exist) + */ + public JarMessageSource(String localJarPath, String defaultJarPath) { + localJarConfiguration = localJarPath.equals(defaultJarPath) ? null : loadJarFile(localJarPath); + defaultJarConfiguration = loadJarFile(defaultJarPath); + + if (defaultJarConfiguration == null) { + throw new IllegalStateException("Default JAR file '" + defaultJarPath + "' could not be loaded"); + } + } + + public String getMessageFromJar(Property property) { + String key = property.getPath(); + String message = localJarConfiguration == null ? null : localJarConfiguration.getString(key); + return message == null ? defaultJarConfiguration.getString(key) : message; + } + + private static YamlConfiguration loadJarFile(String jarPath) { + try (InputStream stream = FileUtils.getResourceFromJar(jarPath)) { + if (stream == null) { + ConsoleLogger.debug("Could not load '" + jarPath + "' from JAR"); + return null; + } + try (InputStreamReader isr = new InputStreamReader(stream)) { + return YamlConfiguration.loadConfiguration(isr); + } + } catch (IOException e) { + ConsoleLogger.logException("Exception while handling JAR path '" + jarPath + "'", e); + } + return null; + } +} diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java new file mode 100644 index 000000000..2fc6fb0f5 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -0,0 +1,90 @@ +package fr.xephi.authme.message.updater; + +import ch.jalu.configme.SettingsManager; +import ch.jalu.configme.properties.Property; +import ch.jalu.configme.properties.StringProperty; +import ch.jalu.configme.resource.YamlFileResource; +import com.google.common.collect.ImmutableList; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.message.MessageKey; + +import java.io.File; +import java.util.List; + +/** + * Migrates the used messages file to a complete, up-to-date version when necessary. + */ +public class MessageUpdater { + + private static final List> TEXT_PROPERTIES = buildPropertyEntriesForMessageKeys(); + + /** + * Applies any necessary migrations to the user's messages file and saves it if it has been modified. + * + * @param userFile the user's messages file (yml file in the plugin's folder) + * @param localJarPath path to the messages file in the JAR for the same language (may not exist) + * @param defaultJarPath path to the messages file in the JAR for the default language + * @return true if the file has been migrated and saved, false if it is up-to-date + */ + public boolean migrateAndSave(File userFile, String localJarPath, String defaultJarPath) { + JarMessageSource jarMessageSource = new JarMessageSource(localJarPath, defaultJarPath); + return migrateAndSave(userFile, jarMessageSource); + } + + /** + * Performs the migration. + * + * @param userFile the file to verify and migrate + * @param jarMessageSource jar message source to get texts from if missing + * @return true if the file has been migrated and saved, false if it is up-to-date + */ + boolean migrateAndSave(File userFile, JarMessageSource jarMessageSource) { + // YamlConfiguration escapes all special characters when saving, making the file hard to use, so use ConfigMe + YamlFileResource userResource = new YamlFileResource(userFile); + SettingsManager settingsManager = SettingsManager.createWithProperties(userResource, null, TEXT_PROPERTIES); + + // Step 1: Migrate any old keys in the file to the new paths + boolean movedOldKeys = migrateOldKeys(userResource); + // Step 2: Take any missing messages from the message files shipped in the AuthMe JAR + boolean addedMissingKeys = addMissingKeys(jarMessageSource, userResource, settingsManager); + + if (movedOldKeys || addedMissingKeys) { + settingsManager.save(); + ConsoleLogger.debug("Successfully saved {0}", userFile); + return true; + } + return false; + } + + private boolean migrateOldKeys(YamlFileResource userResource) { + boolean hasChange = OldMessageKeysMigrater.migrateOldPaths(userResource); + if (hasChange) { + ConsoleLogger.info("Old keys have been moved to the new ones in your messages_xx.yml file"); + } + return hasChange; + } + + private boolean addMissingKeys(JarMessageSource jarMessageSource, YamlFileResource userResource, + SettingsManager settingsManager) { + int addedKeys = 0; + for (Property property : TEXT_PROPERTIES) { + if (!property.isPresent(userResource)) { + settingsManager.setProperty(property, jarMessageSource.getMessageFromJar(property)); + ++addedKeys; + } + } + if (addedKeys > 0) { + ConsoleLogger.info("Added " + addedKeys + " missing keys to your messages_xx.yml file"); + return true; + } + return false; + } + + private static List> buildPropertyEntriesForMessageKeys() { + ImmutableList.Builder> listBuilder = ImmutableList.builder(); + for (MessageKey messageKey : MessageKey.values()) { + listBuilder.add(new StringProperty(messageKey.getKey(), "")); + } + return listBuilder.build(); + } +} diff --git a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java new file mode 100644 index 000000000..002b299d9 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java @@ -0,0 +1,140 @@ +package fr.xephi.authme.message.updater; + +import ch.jalu.configme.resource.PropertyResource; +import com.google.common.collect.ImmutableMap; +import fr.xephi.authme.message.MessageKey; + +import java.util.Map; + +/** + * Migrates message files from the old keys (before 5.5) to the new ones. + * + * @see Issue #1467 + */ +final class OldMessageKeysMigrater { + + private static final Map KEYS_TO_OLD_PATH = ImmutableMap.builder() + .put(MessageKey.DENIED_COMMAND, "denied_command") + .put(MessageKey.SAME_IP_ONLINE, "same_ip_online") + .put(MessageKey.DENIED_CHAT, "denied_chat") + .put(MessageKey.KICK_ANTIBOT, "kick_antibot") + .put(MessageKey.UNKNOWN_USER, "unknown_user") + .put(MessageKey.NOT_LOGGED_IN, "not_logged_in") + .put(MessageKey.USAGE_LOGIN, "usage_log") + .put(MessageKey.WRONG_PASSWORD, "wrong_pwd") + .put(MessageKey.UNREGISTERED_SUCCESS, "unregistered") + .put(MessageKey.REGISTRATION_DISABLED, "reg_disabled") + .put(MessageKey.SESSION_RECONNECTION, "valid_session") + .put(MessageKey.LOGIN_SUCCESS, "login") + .put(MessageKey.ACCOUNT_NOT_ACTIVATED, "vb_nonActiv") + .put(MessageKey.NAME_ALREADY_REGISTERED, "user_regged") + .put(MessageKey.NO_PERMISSION, "no_perm") + .put(MessageKey.ERROR, "error") + .put(MessageKey.LOGIN_MESSAGE, "login_msg") + .put(MessageKey.REGISTER_MESSAGE, "reg_msg") + .put(MessageKey.MAX_REGISTER_EXCEEDED, "max_reg") + .put(MessageKey.USAGE_REGISTER, "usage_reg") + .put(MessageKey.USAGE_UNREGISTER, "usage_unreg") + .put(MessageKey.PASSWORD_CHANGED_SUCCESS, "pwd_changed") + .put(MessageKey.PASSWORD_MATCH_ERROR, "password_error") + .put(MessageKey.PASSWORD_IS_USERNAME_ERROR, "password_error_nick") + .put(MessageKey.PASSWORD_UNSAFE_ERROR, "password_error_unsafe") + .put(MessageKey.PASSWORD_CHARACTERS_ERROR, "password_error_chars") + .put(MessageKey.SESSION_EXPIRED, "invalid_session") + .put(MessageKey.MUST_REGISTER_MESSAGE, "reg_only") + .put(MessageKey.ALREADY_LOGGED_IN_ERROR, "logged_in") + .put(MessageKey.LOGOUT_SUCCESS, "logout") + .put(MessageKey.USERNAME_ALREADY_ONLINE_ERROR, "same_nick") + .put(MessageKey.REGISTER_SUCCESS, "registered") + .put(MessageKey.INVALID_PASSWORD_LENGTH, "pass_len") + .put(MessageKey.CONFIG_RELOAD_SUCCESS, "reload") + .put(MessageKey.LOGIN_TIMEOUT_ERROR, "timeout") + .put(MessageKey.USAGE_CHANGE_PASSWORD, "usage_changepassword") + .put(MessageKey.INVALID_NAME_LENGTH, "name_len") + .put(MessageKey.INVALID_NAME_CHARACTERS, "regex") + .put(MessageKey.ADD_EMAIL_MESSAGE, "add_email") + .put(MessageKey.FORGOT_PASSWORD_MESSAGE, "recovery_email") + .put(MessageKey.USAGE_CAPTCHA, "usage_captcha") + .put(MessageKey.CAPTCHA_WRONG_ERROR, "wrong_captcha") + .put(MessageKey.CAPTCHA_SUCCESS, "valid_captcha") + .put(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, "captcha_for_registration") + .put(MessageKey.REGISTER_CAPTCHA_SUCCESS, "register_captcha_valid") + .put(MessageKey.KICK_FOR_VIP, "kick_forvip") + .put(MessageKey.KICK_FULL_SERVER, "kick_fullserver") + .put(MessageKey.USAGE_ADD_EMAIL, "usage_email_add") + .put(MessageKey.USAGE_CHANGE_EMAIL, "usage_email_change") + .put(MessageKey.USAGE_RECOVER_EMAIL, "usage_email_recovery") + .put(MessageKey.INVALID_NEW_EMAIL, "new_email_invalid") + .put(MessageKey.INVALID_OLD_EMAIL, "old_email_invalid") + .put(MessageKey.INVALID_EMAIL, "email_invalid") + .put(MessageKey.EMAIL_ADDED_SUCCESS, "email_added") + .put(MessageKey.CONFIRM_EMAIL_MESSAGE, "email_confirm") + .put(MessageKey.EMAIL_CHANGED_SUCCESS, "email_changed") + .put(MessageKey.EMAIL_SHOW, "email_show") + .put(MessageKey.SHOW_NO_EMAIL, "show_no_email") + .put(MessageKey.RECOVERY_EMAIL_SENT_MESSAGE, "email_send") + .put(MessageKey.COUNTRY_BANNED_ERROR, "country_banned") + .put(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE, "antibot_auto_enabled") + .put(MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE, "antibot_auto_disabled") + .put(MessageKey.EMAIL_ALREADY_USED_ERROR, "email_already_used") + .put(MessageKey.TWO_FACTOR_CREATE, "two_factor_create") + .put(MessageKey.NOT_OWNER_ERROR, "not_owner_error") + .put(MessageKey.INVALID_NAME_CASE, "invalid_name_case") + .put(MessageKey.TEMPBAN_MAX_LOGINS, "tempban_max_logins") + .put(MessageKey.ACCOUNTS_OWNED_SELF, "accounts_owned_self") + .put(MessageKey.ACCOUNTS_OWNED_OTHER, "accounts_owned_other") + .put(MessageKey.KICK_FOR_ADMIN_REGISTER, "kicked_admin_registered") + .put(MessageKey.INCOMPLETE_EMAIL_SETTINGS, "incomplete_email_settings") + .put(MessageKey.EMAIL_SEND_FAILURE, "email_send_failure") + .put(MessageKey.RECOVERY_CODE_SENT, "recovery_code_sent") + .put(MessageKey.INCORRECT_RECOVERY_CODE, "recovery_code_incorrect") + .put(MessageKey.RECOVERY_TRIES_EXCEEDED, "recovery_tries_exceeded") + .put(MessageKey.RECOVERY_CODE_CORRECT, "recovery_code_correct") + .put(MessageKey.RECOVERY_CHANGE_PASSWORD, "recovery_change_password") + .put(MessageKey.CHANGE_PASSWORD_EXPIRED, "change_password_expired") + .put(MessageKey.EMAIL_COOLDOWN_ERROR, "email_cooldown_error") + .put(MessageKey.VERIFICATION_CODE_REQUIRED, "verification_code_required") + .put(MessageKey.USAGE_VERIFICATION_CODE, "usage_verification_code") + .put(MessageKey.INCORRECT_VERIFICATION_CODE, "incorrect_verification_code") + .put(MessageKey.VERIFICATION_CODE_VERIFIED, "verification_code_verified") + .put(MessageKey.VERIFICATION_CODE_ALREADY_VERIFIED, "verification_code_already_verified") + .put(MessageKey.VERIFICATION_CODE_EXPIRED, "verification_code_expired") + .put(MessageKey.VERIFICATION_CODE_EMAIL_NEEDED, "verification_code_email_needed") + .put(MessageKey.SECOND, "second") + .put(MessageKey.SECONDS, "seconds") + .put(MessageKey.MINUTE, "minute") + .put(MessageKey.MINUTES, "minutes") + .put(MessageKey.HOUR, "hour") + .put(MessageKey.HOURS, "hours") + .put(MessageKey.DAY, "day") + .put(MessageKey.DAYS, "days") + .build(); + + private OldMessageKeysMigrater() { + } + + /** + * Migrates any existing old key paths to their new paths if no text has been defined for the new key. + * + * @param resource the resource to modify and read from + * @return true if at least one message could be migrated, false otherwise + */ + static boolean migrateOldPaths(PropertyResource resource) { + boolean wasPropertyMoved = false; + for (Map.Entry migrationEntry : KEYS_TO_OLD_PATH.entrySet()) { + wasPropertyMoved |= moveIfApplicable(resource, migrationEntry.getKey().getKey(), migrationEntry.getValue()); + } + return wasPropertyMoved; + } + + private static boolean moveIfApplicable(PropertyResource resource, String newPath, String oldPath) { + if (resource.getString(newPath) == null) { + String textAtOldPath = resource.getString(oldPath); + if (textAtOldPath != null) { + resource.setValue(newPath, textAtOldPath); + return true; + } + } + return false; + } +} diff --git a/src/main/java/fr/xephi/authme/service/MessageUpdater.java b/src/main/java/fr/xephi/authme/service/MessageUpdater.java deleted file mode 100644 index 67cfe617e..000000000 --- a/src/main/java/fr/xephi/authme/service/MessageUpdater.java +++ /dev/null @@ -1,138 +0,0 @@ -package fr.xephi.authme.service; - -import ch.jalu.configme.SettingsManager; -import ch.jalu.configme.configurationdata.ConfigurationData; -import ch.jalu.configme.properties.Property; -import ch.jalu.configme.properties.StringProperty; -import ch.jalu.configme.resource.YamlFileResource; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.util.FileUtils; -import fr.xephi.authme.util.StringUtils; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Updates a user's messages file with messages from the JAR files. - */ -public class MessageUpdater { - - private final FileConfiguration userConfiguration; - private final FileConfiguration localJarConfiguration; - private final FileConfiguration defaultJarConfiguration; - - private final List> properties; - private final SettingsManager settingsManager; - private boolean hasMissingMessages = false; - - /** - * Constructor. - * - * @param userFile messages file in the data folder - * @param localJarFile path to messages file in JAR in local language - * @param defaultJarFile path to messages file in JAR for default language - * @throws Exception if userFile does not exist or no JAR messages file can be loaded - */ - public MessageUpdater(File userFile, String localJarFile, String defaultJarFile) throws Exception { - if (!userFile.exists()) { - throw new Exception("Local messages file does not exist"); - } - - userConfiguration = YamlConfiguration.loadConfiguration(userFile); - localJarConfiguration = loadJarFileOrSendError(localJarFile); - defaultJarConfiguration = localJarFile.equals(defaultJarFile) ? null : loadJarFileOrSendError(defaultJarFile); - if (localJarConfiguration == null && defaultJarConfiguration == null) { - throw new Exception("Could not load any JAR messages file to copy from"); - } - - properties = buildPropertyEntriesForMessageKeys(); - settingsManager = new SettingsManager( - new YamlFileResource(userFile), null, new ConfigurationData(properties)); - } - - /** - * Copies missing messages to the messages file. - * - * @param sender sender starting the copy process - * @return true if the messages file was updated, false otherwise - * @throws Exception if an error occurs during saving - */ - public boolean executeCopy(CommandSender sender) throws Exception { - copyMissingMessages(); - - if (!hasMissingMessages) { - sender.sendMessage("No new messages to add"); - return false; - } - - // Save user configuration file - try { - settingsManager.save(); - sender.sendMessage("Message file updated with new messages"); - return true; - } catch (Exception e) { - throw new Exception("Could not save to messages file: " + StringUtils.formatException(e)); - } - } - - private void copyMissingMessages() { - for (Property property : properties) { - String message = userConfiguration.getString(property.getPath()); - if (message == null) { - hasMissingMessages = true; - message = getMessageFromJar(property.getPath()); - } - settingsManager.setProperty(property, message); - } - } - - private String getMessageFromJar(String key) { - String message = (localJarConfiguration == null ? null : localJarConfiguration.getString(key)); - if (message != null) { - return message; - } - return (defaultJarConfiguration == null) ? null : defaultJarConfiguration.getString(key); - } - - private static FileConfiguration loadJarFileOrSendError(String jarPath) { - try (InputStream stream = FileUtils.getResourceFromJar(jarPath)) { - if (stream == null) { - ConsoleLogger.info("Could not load '" + jarPath + "' from JAR"); - return null; - } - InputStreamReader isr = new InputStreamReader(stream); - FileConfiguration configuration = YamlConfiguration.loadConfiguration(isr); - close(isr); - return configuration; - } catch (IOException e) { - ConsoleLogger.logException("Exception while handling JAR path '" + jarPath + "'", e); - } - return null; - } - - private static List> buildPropertyEntriesForMessageKeys() { - return Arrays.stream(MessageKey.values()) - .map(key -> new StringProperty(key.getKey(), "")) - .collect(Collectors.toList()); - } - - private static void close(Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (IOException e) { - ConsoleLogger.info("Cannot close '" + closeable + "': " + StringUtils.formatException(e)); - } - } - } -} diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 5f9c80cae..1c67f4d0d 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -5,6 +5,7 @@ import com.google.common.io.Files; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; +import fr.xephi.authme.message.updater.MessageUpdater; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.util.FileUtils; @@ -275,6 +276,7 @@ public class MessagesIntegrationTest { MessagesFileHandler messagesFileHandler = new MessagesFileHandler(); ReflectionTestUtils.setField(AbstractMessageFileHandler.class, messagesFileHandler, "settings", settings); ReflectionTestUtils.setField(AbstractMessageFileHandler.class, messagesFileHandler, "dataFolder", dataFolder); + ReflectionTestUtils.setField(MessagesFileHandler.class, messagesFileHandler, "messageUpdater", mock(MessageUpdater.class)); ReflectionTestUtils.invokePostConstructMethods(messagesFileHandler); return messagesFileHandler; } diff --git a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java new file mode 100644 index 000000000..a43a5f299 --- /dev/null +++ b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java @@ -0,0 +1,71 @@ +package fr.xephi.authme.message.updater; + +import com.google.common.io.Files; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.message.MessageKey; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link MessageUpdater}. + */ +public class MessageUpdaterTest { + + private MessageUpdater messageUpdater = new MessageUpdater(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @BeforeClass + public static void setUpLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldNotUpdateDefaultFile() throws IOException { + // given + String messagesFilePath = "messages/messages_en.yml"; + File messagesFile = temporaryFolder.newFile(); + Files.copy(TestHelper.getJarFile("/" + messagesFilePath), messagesFile); + long modifiedDate = messagesFile.lastModified(); + + // when + boolean wasChanged = messageUpdater.migrateAndSave(messagesFile, messagesFilePath, messagesFilePath); + + // then + assertThat(wasChanged, equalTo(false)); + assertThat(messagesFile.lastModified(), equalTo(modifiedDate)); + } + + @Test + public void shouldAddMissingKeys() throws IOException { + // given + File messagesFile = temporaryFolder.newFile(); + Files.copy(TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "message/messages_test.yml"), messagesFile); + + // when + boolean wasChanged = messageUpdater.migrateAndSave(messagesFile, "does-not-exist", "messages/messages_en.yml"); + + // then + assertThat(wasChanged, equalTo(true)); + FileConfiguration configuration = YamlConfiguration.loadConfiguration(messagesFile); + // Existing keys should not be overridden + assertThat(configuration.getString(MessageKey.LOGIN_SUCCESS.getKey()), equalTo("&cHere we have&bdefined some colors &dand some other <hings")); + assertThat(configuration.getString(MessageKey.EMAIL_ALREADY_USED_ERROR.getKey()), equalTo("")); + // Check that new keys were added + assertThat(configuration.getString(MessageKey.SECOND.getKey()), equalTo("second")); + assertThat(configuration.getString(MessageKey.ERROR.getKey()), equalTo("&4An unexpected error occurred, please contact an administrator!")); + } + + // TODO #1467: Check migration of old keys +} From 1eaf321575f35cc7a42afdb7dcd922519e99dd4f Mon Sep 17 00:00:00 2001 From: ljacqu Date: Thu, 25 Jan 2018 21:48:48 +0100 Subject: [PATCH 004/155] #1467 Try to clean up abstract message file handler hierarchy - Move some handling with the default file configuration down to the help message file handler since it is the only one with such a behavior now --- .../message/AbstractMessageFileHandler.java | 46 +++-------------- .../message/HelpMessagesFileHandler.java | 49 +++++++++++++++++-- .../fr/xephi/authme/message/Messages.java | 8 --- .../authme/message/MessagesFileHandler.java | 8 --- .../message/MessagesIntegrationTest.java | 26 ++++------ 5 files changed, 61 insertions(+), 76 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java b/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java index f88c6f3f5..4dbbe33bf 100644 --- a/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java +++ b/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java @@ -13,8 +13,6 @@ import org.bukkit.configuration.file.YamlConfiguration; import javax.annotation.PostConstruct; import javax.inject.Inject; import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; /** * Handles a YAML message file with a default file fallback. @@ -33,21 +31,15 @@ public abstract class AbstractMessageFileHandler implements Reloadable { private String filename; private FileConfiguration configuration; private final String defaultFile; - private FileConfiguration defaultConfiguration; - private final String updateCommandAddition; protected AbstractMessageFileHandler() { this.defaultFile = createFilePath(DEFAULT_LANGUAGE); - String updateCommand = getUpdateCommand(); - this.updateCommandAddition = updateCommand == null - ? "" - : " or run " + updateCommand; } @Override @PostConstruct public void reload() { - String language = getLanguage(); + String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); filename = createFilePath(language); File messagesFile = initializeFile(filename); configuration = YamlConfiguration.loadConfiguration(messagesFile); @@ -61,6 +53,10 @@ public abstract class AbstractMessageFileHandler implements Reloadable { return new File(dataFolder, filename); } + protected String getFilename() { + return filename; + } + /** * Returns whether the message file configuration has an entry at the given path. * @@ -79,13 +75,9 @@ public abstract class AbstractMessageFileHandler implements Reloadable { */ public String getMessage(String key) { String message = configuration.getString(key); - - if (message == null) { - ConsoleLogger.warning("Error getting message with key '" + key + "'. " - + "Please update your config file '" + filename + "'" + updateCommandAddition); - return getDefault(key); - } - return message; + return message == null + ? "Error retrieving message '" + key + "'" + : message; } /** @@ -99,23 +91,6 @@ public abstract class AbstractMessageFileHandler implements Reloadable { return configuration.getString(key); } - /** - * Gets the message from the default file. - * - * @param key the key to retrieve the message for - * @return the message from the default file - */ - private String getDefault(String key) { - if (defaultConfiguration == null) { - InputStream stream = FileUtils.getResourceFromJar(defaultFile); - defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream)); - } - String message = defaultConfiguration.getString(key); - return message == null - ? "Error retrieving message '" + key + "'" - : message; - } - /** * Creates the path to the messages file for the given language code. * @@ -124,11 +99,6 @@ public abstract class AbstractMessageFileHandler implements Reloadable { */ protected abstract String createFilePath(String language); - /** - * @return command with which the messages file can be updated; output when a message is missing from the file - */ - protected abstract String getUpdateCommand(); - /** * Copies the messages file from the JAR to the local messages/ folder if it doesn't exist. * diff --git a/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java b/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java index 18eed4ae8..378887537 100644 --- a/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java +++ b/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java @@ -1,23 +1,62 @@ package fr.xephi.authme.message; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.util.FileUtils; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + import javax.inject.Inject; +import java.io.InputStream; +import java.io.InputStreamReader; /** * File handler for the help_xx.yml resource. */ public class HelpMessagesFileHandler extends AbstractMessageFileHandler { + private FileConfiguration defaultConfiguration; + @Inject // Trigger injection in the superclass HelpMessagesFileHandler() { } + /** + * Returns the message for the given key. + * + * @param key the key to retrieve the message for + * @return the message + */ + @Override + public String getMessage(String key) { + String message = getMessageIfExists(key); + + if (message == null) { + ConsoleLogger.warning("Error getting message with key '" + key + "'. " + + "Please update your config file '" + getFilename() + "' or run /authme messages help"); + return getDefault(key); + } + return message; + } + + /** + * Gets the message from the default file. + * + * @param key the key to retrieve the message for + * @return the message from the default file + */ + private String getDefault(String key) { + if (defaultConfiguration == null) { + InputStream stream = FileUtils.getResourceFromJar(createFilePath(DEFAULT_LANGUAGE)); + defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream)); + } + String message = defaultConfiguration.getString(key); + return message == null + ? "Error retrieving message '" + key + "'" + : message; + } + @Override protected String createFilePath(String language) { return "messages/help_" + language + ".yml"; } - - @Override - protected String getUpdateCommand() { - return "/authme messages help"; - } } diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 8c9f2f42e..11227affc 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -135,14 +135,6 @@ public class Messages { return message; } - /** - * Triggers a reload of the messages file. Note that this method is not necessary - * to be called for /authme reload. - */ - public void reloadMessagesFile() { - messagesFileHandler.reload(); - } - private static String formatMessage(String message) { return ChatColor.translateAlternateColorCodes('&', message) .replace(NEWLINE_TAG, "\n"); diff --git a/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java b/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java index 716bf33ed..7ca19249e 100644 --- a/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java +++ b/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java @@ -10,9 +10,6 @@ import javax.inject.Inject; */ public class MessagesFileHandler extends AbstractMessageFileHandler { - // TODO #1467: With the migration the messages handler has become so different that it would be worth - // to remove the extension and extract common features into a helper class instead - @Inject private MessageUpdater messageUpdater; @@ -43,9 +40,4 @@ public class MessagesFileHandler extends AbstractMessageFileHandler { protected String createFilePath(String language) { return "messages/messages_" + language + ".yml"; } - - @Override - protected String getUpdateCommand() { - return "/authme messages"; - } } diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 1c67f4d0d..7e854334d 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -46,6 +46,7 @@ public class MessagesIntegrationTest { private static final String YML_TEST_FILE = TestHelper.PROJECT_ROOT + "message/messages_test.yml"; private Messages messages; + private MessagesFileHandler messagesFileHandler; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -72,8 +73,8 @@ public class MessagesIntegrationTest { FileUtils.create(testFile); Files.copy(TestHelper.getJarFile(YML_TEST_FILE), testFile); - MessagesFileHandler fileHandler = createMessagesFileHandler(); - messages = new Messages(fileHandler); + messagesFileHandler = createMessagesFileHandler(); + messages = new Messages(messagesFileHandler); } @Test @@ -210,20 +211,6 @@ public class MessagesIntegrationTest { verify(logger).warning(argThat(containsString("Invalid number of replacements"))); } - @Test - public void shouldGetMessageFromDefaultFile() { - // given - // Key is only present in default file - MessageKey key = MessageKey.MUST_REGISTER_MESSAGE; - - // when - String message = messages.retrieveSingle(key); - - // then - assertThat(message, - equalTo("§4Only registered users can join the server! Please visit http://example.com to register yourself!")); - } - @Test public void shouldNotUseMessageFromDefaultFile() { // given @@ -250,8 +237,13 @@ public class MessagesIntegrationTest { } @Test - public void shouldFormatDurationObjects() { + public void shouldFormatDurationObjects() throws IOException { // given + // Use the JAR's messages_en.yml file for this, so copy to the file we're using and reload the file handler + File testFile = new File(dataFolder, "messages/messages_test.yml"); + Files.copy(TestHelper.getJarFile("/messages/messages_en.yml"), testFile); + messagesFileHandler.reload(); + Map expectedTexts = ImmutableMap.builder() .put(new Duration(1, TimeUnit.SECONDS), "1 second") .put(new Duration(12, TimeUnit.SECONDS), "12 seconds") From be55437287080f7c8221a3f55b84611e96c864cf Mon Sep 17 00:00:00 2001 From: games647 Date: Sat, 27 Jan 2018 22:56:42 +0100 Subject: [PATCH 005/155] Use the correct javadoc deprecated tag for the lastlogin method --- src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java b/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java index 25bdb209e..575c61ab1 100644 --- a/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java +++ b/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java @@ -167,7 +167,7 @@ public class AuthMeApi { * @param playerName The name of the player to process * * @return The date of the last login, or null if the player doesn't exist or has never logged in - * @Deprecated Use Java 8's Instant method {@link #getLastLoginTime(String)} + * @deprecated Use Java 8's Instant method {@link #getLastLoginTime(String)} */ @Deprecated public Date getLastLogin(String playerName) { From f250289a1ebcfe1539cbbdc783145aa80478d131 Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Sun, 28 Jan 2018 22:01:14 +0100 Subject: [PATCH 006/155] Pom cleanup, fix output filename --- pom.xml | 70 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/pom.xml b/pom.xml index 58ccc4293..e97e8b103 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ UTF-8 - 1.8 + 1.8 AuthMe @@ -104,7 +104,7 @@ - ${project.outputName}-${project.version} + original-${project.outputName}-${project.version} @@ -132,8 +132,8 @@ maven-compiler-plugin 3.7.0 - ${project.jdkVersion} - ${project.jdkVersion} + ${jdk.version} + ${jdk.version} @@ -165,7 +165,9 @@ false - true + true + ${project.outputName}-${project.version} + @@ -428,6 +434,8 @@ de.mkammerer argon2-jvm-nolibs 2.3 + compile + true @@ -462,6 +470,21 @@ + + + ch.jalu + configme + 0.4.1 + compile + true + + + org.yaml + snakeyaml + + + + org.bstats @@ -736,21 +759,6 @@ - - - ch.jalu - configme - 0.4.1 - compile - true - - - org.yaml - snakeyaml - - - - From e980c80ef8a7bc726a0ff65c663e66cf77473e4d Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Mon, 29 Jan 2018 01:14:31 +0100 Subject: [PATCH 007/155] Damn maven please deploy the right jar --- pom.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index e97e8b103..63045a89d 100644 --- a/pom.xml +++ b/pom.xml @@ -60,13 +60,12 @@ 1.8 - AuthMe CUSTOM false ${project.version}-b${project.buildNumber} - ${project.outputName} + AuthMe ${project.versionCode} ${project.groupId}.${project.artifactId}.${bukkitplugin.name} Xephi, sgdc3, DNx5, timvisee, games647, ljacqu, Gnat008 @@ -104,8 +103,6 @@ - original-${project.outputName}-${project.version} - . @@ -166,7 +163,6 @@ false true - ${project.outputName}-${project.version} UTF-8 + UTF-8 1.8 + AuthMe CUSTOM false ${project.version}-b${project.buildNumber} - AuthMe + ${project.outputName} ${project.versionCode} ${project.groupId}.${project.artifactId}.${bukkitplugin.name} Xephi, sgdc3, DNx5, timvisee, games647, ljacqu, Gnat008 @@ -92,6 +105,9 @@ + clean install + ${project.outputName}-${project.version} + . @@ -112,7 +128,16 @@ - + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + org.apache.maven.plugins maven-compiler-plugin @@ -122,7 +147,25 @@ ${jdk.version} - + + org.jacoco + jacoco-maven-plugin + 0.8.0 + + + pre-unit-test + + prepare-agent + + + + post-unit-test + + report + + + + org.apache.maven.plugins maven-surefire-plugin @@ -135,13 +178,47 @@ - + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + aggregate + jar + + + + + public + false + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar + + + + org.apache.maven.plugins maven-shade-plugin 3.1.0 - package @@ -206,48 +283,36 @@ - - org.jacoco - jacoco-maven-plugin - 0.8.0 - - - prepare-agent - - prepare-agent - - - + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + pom.xml + original-${project.outputName}.jar + ${project.outputName}-javadoc.jar + ${project.outputName}-sources.jar + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + pom.xml + original-${project.outputName}.jar + ${project.outputName}-javadoc.jar + ${project.outputName}-sources.jar + - org.eluder.coveralls coveralls-maven-plugin 4.3.0 + false - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - public - false - - - - attach-javadocs - deploy - - jar - - - diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 4924e0cc0..422d57b17 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -41,6 +41,7 @@ public class SQLite implements DataSource { * Constructor for SQLite. * * @param settings The settings instance + * @param dataFolder The data folder * * @throws SQLException when initialization of a SQL datasource failed */ diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java index 734331fd8..ef9d8bdde 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java @@ -50,14 +50,8 @@ public class PermissionsManager implements Reloadable { */ private PermissionHandler handler = null; - /** - * Constructor. - * - * @param server Server instance - * @param pluginManager Bukkit plugin manager - */ @Inject - public PermissionsManager(Server server, PluginManager pluginManager, Settings settings) { + PermissionsManager(Server server, PluginManager pluginManager, Settings settings) { this.server = server; this.pluginManager = pluginManager; this.settings = settings; From fbf1f29a0b385be5d1629be632b6cfc05edc780d Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Mon, 29 Jan 2018 15:15:55 +0100 Subject: [PATCH 010/155] Fix deploy execution --- pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index d1af1e89f..2a8bcc1ff 100644 --- a/pom.xml +++ b/pom.xml @@ -298,6 +298,14 @@ org.apache.maven.plugins maven-deploy-plugin 2.8.2 + + + deploy + + deploy-file + + + pom.xml original-${project.outputName}.jar From fca2a7809fcff749cdbd5a92e66dcf8bd4e7ac8c Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Mon, 29 Jan 2018 18:12:57 +0100 Subject: [PATCH 011/155] Override the default deploy strategy --- pom.xml | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 2a8bcc1ff..fa436ff8a 100644 --- a/pom.xml +++ b/pom.xml @@ -199,6 +199,8 @@ public false + 512m + 512m @@ -287,11 +289,24 @@ org.apache.maven.plugins maven-install-plugin 2.5.2 + + + default-install + none + + + install-custom + install + + install-file + + + pom.xml - original-${project.outputName}.jar - ${project.outputName}-javadoc.jar - ${project.outputName}-sources.jar + target/original-${project.build.finalName}.jar + target/${project.build.finalName}-javadoc.jar + target/${project.build.finalName}-sources.jar @@ -300,6 +315,11 @@ 2.8.2 + default-deploy + none + + + deploy-custom deploy deploy-file @@ -307,10 +327,13 @@ + + ${project.distributionManagement.snapshotRepository.id} + ${project.distributionManagement.snapshotRepository.url} pom.xml - original-${project.outputName}.jar - ${project.outputName}-javadoc.jar - ${project.outputName}-sources.jar + target/original-${project.build.finalName}.jar + target/${project.build.finalName}-javadoc.jar + target/${project.build.finalName}-sources.jar From db820b7d05136ad8d9e139d9644e4b1431ee1961 Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Mon, 29 Jan 2018 18:26:43 +0100 Subject: [PATCH 012/155] Fix duplicated artifacts --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index fa436ff8a..4a5292ce8 100644 --- a/pom.xml +++ b/pom.xml @@ -199,8 +199,6 @@ public false - 512m - 512m @@ -327,13 +325,15 @@ - + ${project.distributionManagement.snapshotRepository.id} ${project.distributionManagement.snapshotRepository.url} pom.xml target/original-${project.build.finalName}.jar + From f714e9d564eb400a36fe15914a50ef1c7e3e428a Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 29 Jan 2018 20:56:30 +0100 Subject: [PATCH 013/155] #1467 Change message keys and messages_en to new structure --- .../fr/xephi/authme/message/MessageKey.java | 198 ++++++++--------- .../updater/OldMessageKeysMigrater.java | 33 ++- src/main/resources/messages/messages_en.yml | 206 ++++++++++-------- .../xephi/authme/message/MessageKeyTest.java | 17 ++ 4 files changed, 257 insertions(+), 197 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index 43963dcb8..b6367c5b1 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -6,298 +6,298 @@ package fr.xephi.authme.message; public enum MessageKey { /** In order to use this command you must be authenticated! */ - DENIED_COMMAND("denied_command"), + DENIED_COMMAND("error.denied_command"), /** A player with the same IP is already in game! */ - SAME_IP_ONLINE("same_ip_online"), + SAME_IP_ONLINE("on_join_validation.same_ip_online"), /** In order to chat you must be authenticated! */ - DENIED_CHAT("denied_chat"), + DENIED_CHAT("error.denied_chat"), /** AntiBot protection mode is enabled! You have to wait some minutes before joining the server. */ - KICK_ANTIBOT("kick_antibot"), + KICK_ANTIBOT("antibot.kick_antibot"), /** This user isn't registered! */ - UNKNOWN_USER("unknown_user"), + UNKNOWN_USER("error.unregistered_user"), /** You're not logged in! */ - NOT_LOGGED_IN("not_logged_in"), + NOT_LOGGED_IN("error.not_logged_in"), /** Usage: /login <password> */ - USAGE_LOGIN("usage_log"), + USAGE_LOGIN("login.command_usage"), /** Wrong password! */ - WRONG_PASSWORD("wrong_pwd"), + WRONG_PASSWORD("login.wrong_password"), /** Successfully unregistered! */ - UNREGISTERED_SUCCESS("unregistered"), + UNREGISTERED_SUCCESS("unregister.success"), /** In-game registration is disabled! */ - REGISTRATION_DISABLED("reg_disabled"), + REGISTRATION_DISABLED("registration.disabled"), /** Logged-in due to Session Reconnection. */ - SESSION_RECONNECTION("valid_session"), + SESSION_RECONNECTION("session.valid_session"), /** Successful login! */ - LOGIN_SUCCESS("login"), + LOGIN_SUCCESS("login.success"), /** Your account isn't activated yet, please check your emails! */ - ACCOUNT_NOT_ACTIVATED("vb_nonActiv"), + ACCOUNT_NOT_ACTIVATED("misc.account_not_activated"), /** You already have registered this username! */ - NAME_ALREADY_REGISTERED("user_regged"), + NAME_ALREADY_REGISTERED("registration.name_taken"), /** You don't have the permission to perform this action! */ - NO_PERMISSION("no_perm"), + NO_PERMISSION("error.no_permission"), /** An unexpected error occurred, please contact an administrator! */ - ERROR("error"), + ERROR("error.unexpected_error"), /** Please, login with the command: /login <password> */ - LOGIN_MESSAGE("login_msg"), + LOGIN_MESSAGE("login.login_request"), /** Please, register to the server with the command: /register <password> <ConfirmPassword> */ - REGISTER_MESSAGE("reg_msg"), + REGISTER_MESSAGE("registration.register_request"), /** You have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection! */ - MAX_REGISTER_EXCEEDED("max_reg", "%max_acc", "%reg_count", "%reg_names"), + MAX_REGISTER_EXCEEDED("error.max_registration", "%max_acc", "%reg_count", "%reg_names"), /** Usage: /register <password> <ConfirmPassword> */ - USAGE_REGISTER("usage_reg"), + USAGE_REGISTER("registration.command_usage"), /** Usage: /unregister <password> */ - USAGE_UNREGISTER("usage_unreg"), + USAGE_UNREGISTER("unregister.command_usage"), /** Password changed successfully! */ - PASSWORD_CHANGED_SUCCESS("pwd_changed"), + PASSWORD_CHANGED_SUCCESS("misc.password_changed"), /** Passwords didn't match, check them again! */ - PASSWORD_MATCH_ERROR("password_error"), + PASSWORD_MATCH_ERROR("password.match_error"), /** You can't use your name as password, please choose another one... */ - PASSWORD_IS_USERNAME_ERROR("password_error_nick"), + PASSWORD_IS_USERNAME_ERROR("password.name_in_password"), /** The chosen password isn't safe, please choose another one... */ - PASSWORD_UNSAFE_ERROR("password_error_unsafe"), + PASSWORD_UNSAFE_ERROR("password.unsafe_password"), - /** Your password contains illegal characters. Allowed chars: REG_EX */ - PASSWORD_CHARACTERS_ERROR("password_error_chars", "REG_EX"), + /** Your password contains illegal characters. Allowed chars: %valid_chars */ + PASSWORD_CHARACTERS_ERROR("password.forbidden_characters", "%valid_chars"), /** Your IP has been changed and your session data has expired! */ - SESSION_EXPIRED("invalid_session"), + SESSION_EXPIRED("session.invalid_session"), /** Only registered users can join the server! Please visit http://example.com to register yourself! */ - MUST_REGISTER_MESSAGE("reg_only"), + MUST_REGISTER_MESSAGE("registration.reg_only"), /** You're already logged in! */ - ALREADY_LOGGED_IN_ERROR("logged_in"), + ALREADY_LOGGED_IN_ERROR("error.logged_in"), /** Logged out successfully! */ - LOGOUT_SUCCESS("logout"), + LOGOUT_SUCCESS("misc.logout"), /** The same username is already playing on the server! */ - USERNAME_ALREADY_ONLINE_ERROR("same_nick"), + USERNAME_ALREADY_ONLINE_ERROR("on_join_validation.same_nick_online"), /** Successfully registered! */ - REGISTER_SUCCESS("registered"), + REGISTER_SUCCESS("registration.success"), /** Your password is too short or too long! Please try with another one! */ - INVALID_PASSWORD_LENGTH("pass_len"), + INVALID_PASSWORD_LENGTH("password.wrong_length"), /** Configuration and database have been reloaded correctly! */ - CONFIG_RELOAD_SUCCESS("reload"), + CONFIG_RELOAD_SUCCESS("misc.reload"), /** Login timeout exceeded, you have been kicked from the server, please try again! */ - LOGIN_TIMEOUT_ERROR("timeout"), + LOGIN_TIMEOUT_ERROR("login.timeout_error"), /** Usage: /changepassword <oldPassword> <newPassword> */ - USAGE_CHANGE_PASSWORD("usage_changepassword"), + USAGE_CHANGE_PASSWORD("misc.usage_change_password"), /** Your username is either too short or too long! */ - INVALID_NAME_LENGTH("name_len"), + INVALID_NAME_LENGTH("on_join_validation.name_length"), - /** Your username contains illegal characters. Allowed chars: REG_EX */ - INVALID_NAME_CHARACTERS("regex", "REG_EX"), + /** Your username contains illegal characters. Allowed chars: %valid_chars */ + INVALID_NAME_CHARACTERS("on_join_validation.characters_in_name", "%valid_chars"), /** Please add your email to your account with the command: /email add <yourEmail> <confirmEmail> */ - ADD_EMAIL_MESSAGE("add_email"), + ADD_EMAIL_MESSAGE("email.add_email_request"), /** Forgot your password? Please use the command: /email recovery <yourEmail> */ - FORGOT_PASSWORD_MESSAGE("recovery_email"), + FORGOT_PASSWORD_MESSAGE("recovery.forgot_password_hint"), - /** To login you have to solve a captcha code, please use the command: /captcha <theCaptcha> */ - USAGE_CAPTCHA("usage_captcha", ""), + /** To login you have to solve a captcha code, please use the command: /captcha %captcha_code */ + USAGE_CAPTCHA("captcha.usage_captcha", "%captcha_code"), - /** Wrong captcha, please type "/captcha THE_CAPTCHA" into the chat! */ - CAPTCHA_WRONG_ERROR("wrong_captcha", "THE_CAPTCHA"), + /** Wrong captcha, please type "/captcha %captcha_code" into the chat! */ + CAPTCHA_WRONG_ERROR("captcha.wrong_captcha", "%captcha_code"), /** Captcha code solved correctly! */ - CAPTCHA_SUCCESS("valid_captcha"), + CAPTCHA_SUCCESS("captcha.valid_captcha"), - /** To register you have to solve a captcha code first, please use the command: /captcha <theCaptcha> */ - CAPTCHA_FOR_REGISTRATION_REQUIRED("captcha_for_registration", ""), + /** To register you have to solve a captcha code first, please use the command: /captcha %captcha_code */ + CAPTCHA_FOR_REGISTRATION_REQUIRED("captcha.captcha_for_registration", "%captcha_code"), /** Valid captcha! You may now register with /register */ - REGISTER_CAPTCHA_SUCCESS("register_captcha_valid"), + REGISTER_CAPTCHA_SUCCESS("captcha.register_captcha_valid"), /** A VIP player has joined the server when it was full! */ - KICK_FOR_VIP("kick_forvip"), + KICK_FOR_VIP("error.kick_for_vip"), /** The server is full, try again later! */ - KICK_FULL_SERVER("kick_fullserver"), + KICK_FULL_SERVER("on_join_validation.kick_full_server"), /** Usage: /email add <email> <confirmEmail> */ - USAGE_ADD_EMAIL("usage_email_add"), + USAGE_ADD_EMAIL("email.usage_email_add"), /** Usage: /email change <oldEmail> <newEmail> */ - USAGE_CHANGE_EMAIL("usage_email_change"), + USAGE_CHANGE_EMAIL("email.usage_email_change"), /** Usage: /email recovery <Email> */ - USAGE_RECOVER_EMAIL("usage_email_recovery"), + USAGE_RECOVER_EMAIL("recovery.command_usage"), /** Invalid new email, try again! */ - INVALID_NEW_EMAIL("new_email_invalid"), + INVALID_NEW_EMAIL("email.new_email_invalid"), /** Invalid old email, try again! */ - INVALID_OLD_EMAIL("old_email_invalid"), + INVALID_OLD_EMAIL("email.old_email_invalid"), /** Invalid email address, try again! */ - INVALID_EMAIL("email_invalid"), + INVALID_EMAIL("email.invalid"), /** Email address successfully added to your account! */ - EMAIL_ADDED_SUCCESS("email_added"), + EMAIL_ADDED_SUCCESS("email.added"), /** Please confirm your email address! */ - CONFIRM_EMAIL_MESSAGE("email_confirm"), + CONFIRM_EMAIL_MESSAGE("email.request_confirmation"), /** Email address changed correctly! */ - EMAIL_CHANGED_SUCCESS("email_changed"), + EMAIL_CHANGED_SUCCESS("email.changed"), /** Your current email address is: %email */ - EMAIL_SHOW("email_show", "%email"), + EMAIL_SHOW("email.email_show", "%email"), /** You currently don't have email address associated with this account. */ - SHOW_NO_EMAIL("show_no_email"), + SHOW_NO_EMAIL("email.no_email_for_account"), /** Recovery email sent successfully! Please check your email inbox! */ - RECOVERY_EMAIL_SENT_MESSAGE("email_send"), + RECOVERY_EMAIL_SENT_MESSAGE("recovery.email_sent"), /** Your country is banned from this server! */ - COUNTRY_BANNED_ERROR("country_banned"), + COUNTRY_BANNED_ERROR("on_join_validation.country_banned"), /** [AntiBotService] AntiBot enabled due to the huge number of connections! */ - ANTIBOT_AUTO_ENABLED_MESSAGE("antibot_auto_enabled"), + ANTIBOT_AUTO_ENABLED_MESSAGE("antibot.auto_enabled"), /** [AntiBotService] AntiBot disabled after %m minutes! */ - ANTIBOT_AUTO_DISABLED_MESSAGE("antibot_auto_disabled", "%m"), + ANTIBOT_AUTO_DISABLED_MESSAGE("antibot.auto_disabled", "%m"), /** The email address is already being used */ - EMAIL_ALREADY_USED_ERROR("email_already_used"), + EMAIL_ALREADY_USED_ERROR("email.already_used"), /** Your secret code is %code. You can scan it from here %url */ - TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"), + TWO_FACTOR_CREATE("misc.two_factor_create", "%code", "%url"), /** You are not the owner of this account. Please choose another name! */ - NOT_OWNER_ERROR("not_owner_error"), + NOT_OWNER_ERROR("on_join_validation.not_owner_error"), /** You should join using username %valid, not %invalid. */ - INVALID_NAME_CASE("invalid_name_case", "%valid", "%invalid"), + INVALID_NAME_CASE("on_join_validation.invalid_name_case", "%valid", "%invalid"), /** You have been temporarily banned for failing to log in too many times. */ - TEMPBAN_MAX_LOGINS("tempban_max_logins"), + TEMPBAN_MAX_LOGINS("error.tempban_max_logins"), /** You own %count accounts: */ - ACCOUNTS_OWNED_SELF("accounts_owned_self", "%count"), + ACCOUNTS_OWNED_SELF("misc.accounts_owned_self", "%count"), /** The player %name has %count accounts: */ - ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"), + ACCOUNTS_OWNED_OTHER("misc.accounts_owned_other", "%name", "%count"), /** An admin just registered you; please log in again */ - KICK_FOR_ADMIN_REGISTER("kicked_admin_registered"), + KICK_FOR_ADMIN_REGISTER("registration.kicked_admin_registered"), /** Error: not all required settings are set for sending emails. Please contact an admin. */ - INCOMPLETE_EMAIL_SETTINGS("incomplete_email_settings"), + INCOMPLETE_EMAIL_SETTINGS("email.incomplete_settings"), /** The email could not be sent. Please contact an administrator. */ - EMAIL_SEND_FAILURE("email_send_failure"), + EMAIL_SEND_FAILURE("email.send_failure"), /** A recovery code to reset your password has been sent to your email. */ - RECOVERY_CODE_SENT("recovery_code_sent"), + RECOVERY_CODE_SENT("recovery.code.code_sent"), /** The recovery code is not correct! You have %count tries remaining. */ - INCORRECT_RECOVERY_CODE("recovery_code_incorrect", "%count"), + INCORRECT_RECOVERY_CODE("recovery.code.incorrect", "%count"), /** * You have exceeded the maximum number of attempts to enter the recovery code. * Use "/email recovery [email]" to generate a new one. */ - RECOVERY_TRIES_EXCEEDED("recovery_tries_exceeded"), + RECOVERY_TRIES_EXCEEDED("recovery.code.tries_exceeded"), /** Recovery code entered correctly! */ - RECOVERY_CODE_CORRECT("recovery_code_correct"), + RECOVERY_CODE_CORRECT("recovery.code.correct"), /** Please use the command /email setpassword to change your password immediately. */ - RECOVERY_CHANGE_PASSWORD("recovery_change_password"), + RECOVERY_CHANGE_PASSWORD("recovery.code.change_password"), /** You cannot change your password using this command anymore. */ - CHANGE_PASSWORD_EXPIRED("change_password_expired"), + CHANGE_PASSWORD_EXPIRED("email.change_password_expired"), /** An email was already sent recently. You must wait %time before you can send a new one. */ - EMAIL_COOLDOWN_ERROR("email_cooldown_error", "%time"), + EMAIL_COOLDOWN_ERROR("email.email_cooldown_error", "%time"), /** * The command you are trying to execute is sensitive and requires a verification! * A verification code has been sent to your email, * run the command "/verification [code]" to verify your identity. */ - VERIFICATION_CODE_REQUIRED("verification_code_required"), + VERIFICATION_CODE_REQUIRED("verification.code_required"), /** Usage: /verification <code> */ - USAGE_VERIFICATION_CODE("usage_verification_code"), + USAGE_VERIFICATION_CODE("verification.command_usage"), /** Incorrect code, please type "/verification <code>" into the chat! */ - INCORRECT_VERIFICATION_CODE("incorrect_verification_code"), + INCORRECT_VERIFICATION_CODE("verification.incorrect_code"), /** * Your identity has been verified! * You can now execute every sensitive command within the current session! */ - VERIFICATION_CODE_VERIFIED("verification_code_verified"), + VERIFICATION_CODE_VERIFIED("verification.success"), /** * You can already execute every sensitive command within the current session! */ - VERIFICATION_CODE_ALREADY_VERIFIED("verification_code_already_verified"), + VERIFICATION_CODE_ALREADY_VERIFIED("verification.already_verified"), /** Your code has expired! Execute another sensitive command to get a new code! */ - VERIFICATION_CODE_EXPIRED("verification_code_expired"), + VERIFICATION_CODE_EXPIRED("verification.code_expired"), /** To verify your identity you need to link an email address with your account! */ - VERIFICATION_CODE_EMAIL_NEEDED("verification_code_email_needed"), + VERIFICATION_CODE_EMAIL_NEEDED("verification.email_needed"), /** second */ - SECOND("second"), + SECOND("time.second"), /** seconds */ - SECONDS("seconds"), + SECONDS("time.seconds"), /** minute */ - MINUTE("minute"), + MINUTE("time.minute"), /** minutes */ - MINUTES("minutes"), + MINUTES("time.minutes"), /** hour */ - HOUR("hour"), + HOUR("time.hour"), /** hours */ - HOURS("hours"), + HOURS("time.hours"), /** day */ - DAY("day"), + DAY("time.day"), /** days */ - DAYS("days"); + DAYS("time.days"); private String key; diff --git a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java index 002b299d9..d43b58afc 100644 --- a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java +++ b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java @@ -6,6 +6,8 @@ import fr.xephi.authme.message.MessageKey; import java.util.Map; +import static com.google.common.collect.ImmutableMap.of; + /** * Migrates message files from the old keys (before 5.5) to the new ones. * @@ -13,6 +15,15 @@ import java.util.Map; */ final class OldMessageKeysMigrater { + private static final Map> PLACEHOLDER_REPLACEMENTS = + ImmutableMap.>builder() + .put(MessageKey.PASSWORD_CHARACTERS_ERROR, of("REG_EX", "%valid_chars")) + .put(MessageKey.INVALID_NAME_CHARACTERS, of("REG_EX", "%valid_chars")) + .put(MessageKey.USAGE_CAPTCHA, of("", "%captcha_code")) + .put(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, of("", "%captcha_code")) + .put(MessageKey.CAPTCHA_WRONG_ERROR, of("THE_CAPTCHA", "%captcha_code")) + .build(); + private static final Map KEYS_TO_OLD_PATH = ImmutableMap.builder() .put(MessageKey.DENIED_COMMAND, "denied_command") .put(MessageKey.SAME_IP_ONLINE, "same_ip_online") @@ -122,19 +133,33 @@ final class OldMessageKeysMigrater { static boolean migrateOldPaths(PropertyResource resource) { boolean wasPropertyMoved = false; for (Map.Entry migrationEntry : KEYS_TO_OLD_PATH.entrySet()) { - wasPropertyMoved |= moveIfApplicable(resource, migrationEntry.getKey().getKey(), migrationEntry.getValue()); + wasPropertyMoved |= moveIfApplicable(resource, migrationEntry.getKey(), migrationEntry.getValue()); } return wasPropertyMoved; } - private static boolean moveIfApplicable(PropertyResource resource, String newPath, String oldPath) { - if (resource.getString(newPath) == null) { + private static boolean moveIfApplicable(PropertyResource resource, MessageKey messageKey, String oldPath) { + if (resource.getString(messageKey.getKey()) == null) { String textAtOldPath = resource.getString(oldPath); if (textAtOldPath != null) { - resource.setValue(newPath, textAtOldPath); + textAtOldPath = replaceOldPlaceholders(messageKey, textAtOldPath); + resource.setValue(messageKey.getKey(), textAtOldPath); return true; } } return false; } + + private static String replaceOldPlaceholders(MessageKey key, String text) { + Map replacements = PLACEHOLDER_REPLACEMENTS.get(key); + if (replacements == null) { + return text; + } + + String newText = text; + for (Map.Entry replacement : replacements.entrySet()) { + text = text.replace(replacement.getKey(), replacement.getValue()); + } + return newText; + } } diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index 383ec9a80..f2aa1d44a 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -1,117 +1,135 @@ # Registration -reg_msg: '&3Please, register to the server with the command: /register ' -usage_reg: '&cUsage: /register ' -reg_only: '&4Only registered users can join the server! Please visit http://example.com to register yourself!' -kicked_admin_registered: 'An admin just registered you; please log in again' -registered: '&2Successfully registered!' -reg_disabled: '&cIn-game registration is disabled!' -user_regged: '&cYou already have registered this username!' +registration: + register_request: '&3Please, register to the server with the command: /register ' + command_usage: '&cUsage: /register ' + reg_only: '&4Only registered users can join the server! Please visit http://example.com to register yourself!' + kicked_admin_registered: 'An admin just registered you; please log in again' + success: '&2Successfully registered!' + disabled: '&cIn-game registration is disabled!' + name_taken: '&cYou already have registered this username!' # Password errors on registration -password_error: '&cPasswords didn''t match, check them again!' -password_error_nick: '&cYou can''t use your name as password, please choose another one...' -password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' -password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX' -pass_len: '&cYour password is too short or too long! Please try with another one!' +password: + match_error: '&cPasswords didn''t match, check them again!' + name_in_password: '&cYou can''t use your name as password, please choose another one...' + unsafe_password: '&cThe chosen password isn''t safe, please choose another one...' + forbidden_characters: '&4Your password contains illegal characters. Allowed chars: %valid_chars' + wrong_length: '&cYour password is too short or too long! Please try with another one!' # Login -usage_log: '&cUsage: /login ' -wrong_pwd: '&cWrong password!' -login: '&2Successful login!' -login_msg: '&cPlease, login with the command: /login ' -timeout: '&4Login timeout exceeded, you have been kicked from the server, please try again!' +login: + command_usage: '&cUsage: /login ' + wrong_password: '&cWrong password!' + success: '&2Successful login!' + login_request: '&cPlease, login with the command: /login ' + timeout_error: '&4Login timeout exceeded, you have been kicked from the server, please try again!' # Errors -unknown_user: '&cThis user isn''t registered!' -denied_command: '&cIn order to use this command you must be authenticated!' -denied_chat: '&cIn order to chat you must be authenticated!' -not_logged_in: '&cYou''re not logged in!' -tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' -max_reg: '&cYou have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection!' -no_perm: '&4You don''t have the permission to perform this action!' -error: '&4An unexpected error occurred, please contact an administrator!' -kick_forvip: '&3A VIP player has joined the server when it was full!' +error: + unregistered_user: '&cThis user isn''t registered!' + denied_command: '&cIn order to use this command you must be authenticated!' + denied_chat: '&cIn order to chat you must be authenticated!' + not_logged_in: '&cYou''re not logged in!' + tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' + max_registration: '&cYou have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection!' + no_permission: '&4You don''t have the permission to perform this action!' + unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + kick_for_vip: '&3A VIP player has joined the server when it was full!' + logged_in: '&cYou''re already logged in!' # AntiBot -kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' -antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' +antibot: + kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' + auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' + auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' + +unregister: + success: '&cSuccessfully unregistered!' + command_usage: '&cUsage: /unregister ' # Other messages -unregistered: '&cSuccessfully unregistered!' -accounts_owned_self: 'You own %count accounts:' -accounts_owned_other: 'The player %name has %count accounts:' -two_factor_create: '&2Your secret code is %code. You can scan it from here %url' -recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' -recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -recovery_code_correct: 'Recovery code entered correctly!' -recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&cYour account isn''t activated yet, please check your emails!' -usage_unreg: '&cUsage: /unregister ' -pwd_changed: '&2Password changed successfully!' -logged_in: '&cYou''re already logged in!' -logout: '&2Logged out successfully!' -reload: '&2Configuration and database have been reloaded correctly!' -usage_changepassword: '&cUsage: /changepassword ' +misc: + accounts_owned_self: 'You own %count accounts:' + accounts_owned_other: 'The player %name has %count accounts:' + two_factor_create: '&2Your secret code is %code. You can scan it from here %url' + account_not_activated: '&cYour account isn''t activated yet, please check your emails!' + password_changed: '&2Password changed successfully!' + logout: '&2Logged out successfully!' + reload: '&2Configuration and database have been reloaded correctly!' + usage_change_password: '&cUsage: /changepassword ' # Session messages -invalid_session: '&cYour IP has been changed and your session data has expired!' -valid_session: '&2Logged-in due to Session Reconnection.' +session: + invalid_session: '&cYour IP has been changed and your session data has expired!' + valid_session: '&2Logged-in due to Session Reconnection.' # Error messages when joining -name_len: '&4Your username is either too short or too long!' -regex: '&4Your username contains illegal characters. Allowed chars: REG_EX' -country_banned: '&4Your country is banned from this server!' -not_owner_error: 'You are not the owner of this account. Please choose another name!' -kick_fullserver: '&4The server is full, try again later!' -same_nick: '&4The same username is already playing on the server!' -invalid_name_case: 'You should join using username %valid, not %invalid.' -same_ip_online: 'A player with the same IP is already in game!' +on_join_validation: + name_length: '&4Your username is either too short or too long!' + characters_in_name: '&4Your username contains illegal characters. Allowed chars: %valid_chars' + country_banned: '&4Your country is banned from this server!' + not_owner_error: 'You are not the owner of this account. Please choose another name!' + kick_full_server: '&4The server is full, try again later!' + same_nick_online: '&4The same username is already playing on the server!' + invalid_name_case: 'You should join using username %valid, not %invalid.' + same_ip_online: 'A player with the same IP is already in game!' # Email -usage_email_add: '&cUsage: /email add ' -usage_email_change: '&cUsage: /email change ' -usage_email_recovery: '&cUsage: /email recovery ' -new_email_invalid: '&cInvalid new email, try again!' -old_email_invalid: '&cInvalid old email, try again!' -email_invalid: '&cInvalid email address, try again!' -email_added: '&2Email address successfully added to your account!' -email_confirm: '&cPlease confirm your email address!' -email_changed: '&2Email address changed correctly!' -email_send: '&2Recovery email sent successfully! Please check your email inbox!' -email_show: '&2Your current email address is: &f%email' -incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' -email_already_used: '&4The email address is already being used' -email_send_failure: 'The email could not be sent. Please contact an administrator.' -show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&3Please add your email to your account with the command: /email add ' -recovery_email: '&3Forgot your password? Please use the command: /email recovery ' -change_password_expired: 'You cannot change your password using this command anymore.' -email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + usage_email_add: '&cUsage: /email add ' + usage_email_change: '&cUsage: /email change ' + new_email_invalid: '&cInvalid new email, try again!' + old_email_invalid: '&cInvalid old email, try again!' + invalid: '&cInvalid email address, try again!' + added: '&2Email address successfully added to your account!' + request_confirmation: '&cPlease confirm your email address!' + changed: '&2Email address changed correctly!' + email_show: '&2Your current email address is: &f%email' + incomplete_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' + already_used: '&4The email address is already being used' + send_failure: 'The email could not be sent. Please contact an administrator.' + no_email_for_account: '&2You currently don''t have email address associated with this account.' + add_email_request: '&3Please add your email to your account with the command: /email add ' + change_password_expired: 'You cannot change your password using this command anymore.' + email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Forgot your password? Please use the command: /email recovery ' + command_usage: '&cUsage: /email recovery ' + email_sent: '&2Recovery email sent successfully! Please check your email inbox!' + code: + code_sent: 'A recovery code to reset your password has been sent to your email.' + incorrect: 'The recovery code is not correct! You have %count tries remaining.' + tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + correct: 'Recovery code entered correctly!' + change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha -usage_captcha: '&3To log in you have to solve a captcha code, please use the command: /captcha ' -wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' -valid_captcha: '&2Captcha code solved correctly!' -captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3To log in you have to solve a captcha code, please use the command: /captcha %captcha_code' + wrong_captcha: '&cWrong captcha, please type "/captcha %captcha_code" into the chat!' + valid_captcha: '&2Captcha code solved correctly!' + captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code -verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -usage_verification_code: '&cUsage: /verification ' -incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + command_usage: '&cUsage: /verification ' + incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + success: '&2Your identity has been verified! You can now execute all commands within the current session!' + already_verified: '&2You can already execute every sensitive command within the current session!' + code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units -second: 'second' -seconds: 'seconds' -minute: 'minute' -minutes: 'minutes' -hour: 'hour' -hours: 'hours' -day: 'day' -days: 'days' +time: + second: 'second' + seconds: 'seconds' + minute: 'minute' + minutes: 'minutes' + hour: 'hour' + hours: 'hours' + day: 'day' + days: 'days' diff --git a/src/test/java/fr/xephi/authme/message/MessageKeyTest.java b/src/test/java/fr/xephi/authme/message/MessageKeyTest.java index 3eb9f96ed..b28116341 100644 --- a/src/test/java/fr/xephi/authme/message/MessageKeyTest.java +++ b/src/test/java/fr/xephi/authme/message/MessageKeyTest.java @@ -3,9 +3,12 @@ package fr.xephi.authme.message; import fr.xephi.authme.util.StringUtils; import org.junit.Test; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import static org.hamcrest.Matchers.matchesPattern; +import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -29,4 +32,18 @@ public class MessageKeyTest { } } } + + @Test + public void shouldHaveWellFormedPlaceholders() { + // given + MessageKey[] messageKeys = MessageKey.values(); + + // when / then + for (MessageKey messageKey : messageKeys) { + String[] tags = messageKey.getTags(); + Arrays.stream(tags) + .forEach(tag -> assertThat("Tag '" + tag + "' corresponds to valid format for key '" + messageKey + "'", + tag, matchesPattern("^%[a-z_]+$"))); + } + } } From 760a2a909cfdd33cd55e24c2b0a15eae0ce198ad Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 29 Jan 2018 21:46:58 +0100 Subject: [PATCH 014/155] #1467 Fix export issues (style, encoding) - Override yaml file resource to ensure that lines aren't wrapped - Override yaml file reader to ensure the file is always read as UTF-8 --- .../MessageMigraterPropertyReader.java | 131 ++++++++++++++++++ .../message/updater/MessageUpdater.java | 55 +++++++- 2 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java b/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java new file mode 100644 index 000000000..400b25ff3 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java @@ -0,0 +1,131 @@ +package fr.xephi.authme.message.updater; + +import ch.jalu.configme.exception.ConfigMeException; +import ch.jalu.configme.resource.PropertyReader; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Duplication of ConfigMe's {@link ch.jalu.configme.resource.YamlFileReader} with a character encoding + * fix in {@link #reload}. + */ +public class MessageMigraterPropertyReader implements PropertyReader { + + private final File file; + private Map root; + /** See same field in {@link ch.jalu.configme.resource.YamlFileReader} for details. */ + private boolean hasObjectAsRoot = false; + + /** + * Constructor. + * + * @param file the file to load + */ + public MessageMigraterPropertyReader(File file) { + this.file = file; + reload(); + } + + @Override + public Object getObject(String path) { + if (path.isEmpty()) { + return hasObjectAsRoot ? root.get("") : root; + } + Object node = root; + String[] keys = path.split("\\."); + for (String key : keys) { + node = getIfIsMap(key, node); + if (node == null) { + return null; + } + } + return node; + } + + @Override + public T getTypedObject(String path, Class clazz) { + Object value = getObject(path); + if (clazz.isInstance(value)) { + return clazz.cast(value); + } + return null; + } + + @Override + public void set(String path, Object value) { + Objects.requireNonNull(path); + + if (path.isEmpty()) { + root.clear(); + root.put("", value); + hasObjectAsRoot = true; + } else if (hasObjectAsRoot) { + throw new ConfigMeException("The root path is a bean property; you cannot set values to any subpath. " + + "Modify the bean at the root or set a new one instead."); + } else { + setValueInChildPath(path, value); + } + } + + /** + * Sets the value at the given path. This method is used when the root is a map and not a specific object. + * + * @param path the path to set the value at + * @param value the value to set + */ + @SuppressWarnings("unchecked") + private void setValueInChildPath(String path, Object value) { + Map node = root; + String[] keys = path.split("\\."); + for (int i = 0; i < keys.length - 1; ++i) { + Object child = node.get(keys[i]); + if (child instanceof Map) { + node = (Map) child; + } else { // child is null or some other value - replace with map + Map newEntry = new HashMap<>(); + node.put(keys[i], newEntry); + if (value == null) { + // For consistency, replace whatever value/null here with an empty map, + // but if the value is null our work here is done. + return; + } + node = newEntry; + } + } + // node now contains the parent map (existing or newly created) + if (value == null) { + node.remove(keys[keys.length - 1]); + } else { + node.put(keys[keys.length - 1], value); + } + } + + @Override + public void reload() { + try (FileInputStream fis = new FileInputStream(file); + InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) { + + Object obj = new Yaml().load(isr); + root = obj == null ? new HashMap<>() : (Map) obj; + } catch (IOException e) { + throw new ConfigMeException("Could not read file '" + file + "'", e); + } catch (ClassCastException e) { + throw new ConfigMeException("Top-level is not a map in '" + file + "'", e); + } + } + + private static Object getIfIsMap(String key, Object value) { + if (value instanceof Map) { + return ((Map) value).get(key); + } + return null; + } +} diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index 2fc6fb0f5..1c0721082 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -1,12 +1,16 @@ package fr.xephi.authme.message.updater; import ch.jalu.configme.SettingsManager; +import ch.jalu.configme.beanmapper.leafproperties.LeafPropertiesGenerator; +import ch.jalu.configme.configurationdata.PropertyListBuilder; import ch.jalu.configme.properties.Property; import ch.jalu.configme.properties.StringProperty; import ch.jalu.configme.resource.YamlFileResource; import com.google.common.collect.ImmutableList; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.message.MessageKey; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; import java.io.File; import java.util.List; @@ -40,7 +44,7 @@ public class MessageUpdater { */ boolean migrateAndSave(File userFile, JarMessageSource jarMessageSource) { // YamlConfiguration escapes all special characters when saving, making the file hard to use, so use ConfigMe - YamlFileResource userResource = new YamlFileResource(userFile); + YamlFileResource userResource = new MigraterYamlFileResource(userFile); SettingsManager settingsManager = SettingsManager.createWithProperties(userResource, null, TEXT_PROPERTIES); // Step 1: Migrate any old keys in the file to the new paths @@ -81,10 +85,53 @@ public class MessageUpdater { } private static List> buildPropertyEntriesForMessageKeys() { - ImmutableList.Builder> listBuilder = ImmutableList.builder(); + StringPropertyListBuilder builder = new StringPropertyListBuilder(); for (MessageKey messageKey : MessageKey.values()) { - listBuilder.add(new StringProperty(messageKey.getKey(), "")); + builder.add(messageKey.getKey()); + } + return ImmutableList.copyOf(builder.create()); + } + + /** + * Wraps a {@link PropertyListBuilder} for easier construction of string properties. + * ConfigMe's property list builder ensures that properties are grouped together by path. + */ + private static final class StringPropertyListBuilder { + private PropertyListBuilder propertyListBuilder = new PropertyListBuilder(); + + void add(String path) { + propertyListBuilder.add(new StringProperty(path, "")); + } + + @SuppressWarnings("unchecked") + List> create() { + return (List) propertyListBuilder.create(); + } + } + + /** + * Extension of {@link YamlFileResource} to fine-tune the export style. + */ + private static final class MigraterYamlFileResource extends YamlFileResource { + + private Yaml singleQuoteYaml; + + MigraterYamlFileResource(File file) { + super(file, new MessageMigraterPropertyReader(file), new LeafPropertiesGenerator()); + } + + @Override + protected Yaml getSingleQuoteYaml() { + if (singleQuoteYaml == null) { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setAllowUnicode(true); + options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED); + // Overridden setting: don't split lines + options.setSplitLines(false); + singleQuoteYaml = new Yaml(options); + } + return singleQuoteYaml; } - return listBuilder.build(); } } From abd19cdb86e38b3bddd32743ccad8a7a6875ab24 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 29 Jan 2018 22:15:39 +0100 Subject: [PATCH 015/155] #1467 Fix error in placeholder migration, create and fix tests --- .../message/updater/JarMessageSource.java | 2 +- .../message/updater/MessageUpdater.java | 59 +++++---- .../updater/OldMessageKeysMigrater.java | 2 +- .../message/MessagesIntegrationTest.java | 2 +- .../message/YamlTextFileCheckerTest.java | 2 + .../message/updater/MessageUpdaterTest.java | 26 +++- .../xephi/authme/message/messages_en_old.yml | 121 ++++++++++++++++++ .../fr/xephi/authme/message/messages_test.yml | 16 ++- 8 files changed, 193 insertions(+), 37 deletions(-) create mode 100644 src/test/resources/fr/xephi/authme/message/messages_en_old.yml diff --git a/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java b/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java index 13858bfc2..c4da59c75 100644 --- a/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java +++ b/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java @@ -34,7 +34,7 @@ public class JarMessageSource { } } - public String getMessageFromJar(Property property) { + public String getMessageFromJar(Property property) { String key = property.getPath(); String message = localJarConfiguration == null ? null : localJarConfiguration.getString(key); return message == null ? defaultJarConfiguration.getString(key) : message; diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index 1c0721082..37bbe735e 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -2,25 +2,26 @@ package fr.xephi.authme.message.updater; import ch.jalu.configme.SettingsManager; import ch.jalu.configme.beanmapper.leafproperties.LeafPropertiesGenerator; +import ch.jalu.configme.configurationdata.ConfigurationData; import ch.jalu.configme.configurationdata.PropertyListBuilder; import ch.jalu.configme.properties.Property; import ch.jalu.configme.properties.StringProperty; import ch.jalu.configme.resource.YamlFileResource; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.message.MessageKey; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import java.io.File; -import java.util.List; +import java.util.Map; /** * Migrates the used messages file to a complete, up-to-date version when necessary. */ public class MessageUpdater { - private static final List> TEXT_PROPERTIES = buildPropertyEntriesForMessageKeys(); + private static final ConfigurationData CONFIGURATION_DATA = buildConfigurationData(); /** * Applies any necessary migrations to the user's messages file and saves it if it has been modified. @@ -42,10 +43,10 @@ public class MessageUpdater { * @param jarMessageSource jar message source to get texts from if missing * @return true if the file has been migrated and saved, false if it is up-to-date */ - boolean migrateAndSave(File userFile, JarMessageSource jarMessageSource) { + private boolean migrateAndSave(File userFile, JarMessageSource jarMessageSource) { // YamlConfiguration escapes all special characters when saving, making the file hard to use, so use ConfigMe YamlFileResource userResource = new MigraterYamlFileResource(userFile); - SettingsManager settingsManager = SettingsManager.createWithProperties(userResource, null, TEXT_PROPERTIES); + SettingsManager settingsManager = new SettingsManager(userResource, null, CONFIGURATION_DATA); // Step 1: Migrate any old keys in the file to the new paths boolean movedOldKeys = migrateOldKeys(userResource); @@ -71,9 +72,9 @@ public class MessageUpdater { private boolean addMissingKeys(JarMessageSource jarMessageSource, YamlFileResource userResource, SettingsManager settingsManager) { int addedKeys = 0; - for (Property property : TEXT_PROPERTIES) { + for (Property property : CONFIGURATION_DATA.getProperties()) { if (!property.isPresent(userResource)) { - settingsManager.setProperty(property, jarMessageSource.getMessageFromJar(property)); + settingsManager.setProperty((Property) property, jarMessageSource.getMessageFromJar(property)); ++addedKeys; } } @@ -84,29 +85,33 @@ public class MessageUpdater { return false; } - private static List> buildPropertyEntriesForMessageKeys() { - StringPropertyListBuilder builder = new StringPropertyListBuilder(); - for (MessageKey messageKey : MessageKey.values()) { - builder.add(messageKey.getKey()); - } - return ImmutableList.copyOf(builder.create()); - } - /** - * Wraps a {@link PropertyListBuilder} for easier construction of string properties. - * ConfigMe's property list builder ensures that properties are grouped together by path. + * Constructs the {@link ConfigurationData} for exporting a messages file in its entirety. + * + * @return the configuration data to export with */ - private static final class StringPropertyListBuilder { - private PropertyListBuilder propertyListBuilder = new PropertyListBuilder(); - - void add(String path) { - propertyListBuilder.add(new StringProperty(path, "")); - } - - @SuppressWarnings("unchecked") - List> create() { - return (List) propertyListBuilder.create(); + private static ConfigurationData buildConfigurationData() { + PropertyListBuilder builder = new PropertyListBuilder(); + for (MessageKey messageKey : MessageKey.values()) { + builder.add(new StringProperty(messageKey.getKey(), "")); } + Map comments = ImmutableMap.builder() + .put("registration", new String[]{"Registration"}) + .put("password", new String[]{"Password errors on registration"}) + .put("login", new String[]{"Login"}) + .put("error", new String[]{"Errors"}) + .put("antibot", new String[]{"AntiBot"}) + .put("unregister", new String[]{"Unregister"}) + .put("misc", new String[]{"Other messages"}) + .put("session", new String[]{"Session messages"}) + .put("on_join_validation", new String[]{"Error messages when joining"}) + .put("email", new String[]{"Email"}) + .put("recovery", new String[]{"Password recovery by email"}) + .put("captcha", new String[]{"Captcha"}) + .put("verification", new String[]{"Verification code"}) + .put("time", new String[]{"Time units"}) + .build(); + return new ConfigurationData(builder.create(), comments); } /** diff --git a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java index d43b58afc..4f5e5ad72 100644 --- a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java +++ b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java @@ -158,7 +158,7 @@ final class OldMessageKeysMigrater { String newText = text; for (Map.Entry replacement : replacements.entrySet()) { - text = text.replace(replacement.getKey(), replacement.getValue()); + newText = newText.replace(replacement.getKey(), replacement.getValue()); } return newText; } diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 7e854334d..30af10773 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -180,7 +180,7 @@ public class MessagesIntegrationTest { messages.send(sender, key); // then - verify(sender).sendMessage("Use /captcha THE_CAPTCHA to solve the captcha"); + verify(sender).sendMessage("Use /captcha %captcha_code to solve the captcha"); } @Test diff --git a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java index 1499c976d..8f3b10483 100644 --- a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java +++ b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java @@ -5,6 +5,7 @@ import fr.xephi.authme.command.help.HelpSection; import fr.xephi.authme.util.StringUtils; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -33,6 +34,7 @@ public class YamlTextFileCheckerTest { } @Test + @Ignore // TODO #1467: Migrate all files to new keys public void testAllMessagesYmlFiles() { checkFiles( Pattern.compile("messages_\\w+\\.yml"), diff --git a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java index a43a5f299..1d1abef5c 100644 --- a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java +++ b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java @@ -67,5 +67,29 @@ public class MessageUpdaterTest { assertThat(configuration.getString(MessageKey.ERROR.getKey()), equalTo("&4An unexpected error occurred, please contact an administrator!")); } - // TODO #1467: Check migration of old keys + @Test + public void shouldMigrateOldEntries() throws IOException { + // given + File messagesFile = temporaryFolder.newFile(); + Files.copy(TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "message/messages_en_old.yml"), messagesFile); + + // when + boolean wasChanged = messageUpdater.migrateAndSave(messagesFile, "messages/messages_en.yml", "messages/messages_en.yml"); + + // then + assertThat(wasChanged, equalTo(true)); + FileConfiguration configuration = YamlConfiguration.loadConfiguration(messagesFile); + assertThat(configuration.getString(MessageKey.PASSWORD_MATCH_ERROR.getKey()), + equalTo("Password error message")); + assertThat(configuration.getString(MessageKey.INVALID_NAME_CHARACTERS.getKey()), + equalTo("not valid username: Allowed chars are %valid_chars")); + assertThat(configuration.getString(MessageKey.INVALID_OLD_EMAIL.getKey()), + equalTo("Email (old) is not valid!!")); + assertThat(configuration.getString(MessageKey.CAPTCHA_WRONG_ERROR.getKey()), + equalTo("The captcha code is %captcha_code for you")); + assertThat(configuration.getString(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED.getKey()), + equalTo("Now type /captcha %captcha_code")); + assertThat(configuration.getString(MessageKey.SECONDS.getKey()), + equalTo("seconds in plural")); + } } diff --git a/src/test/resources/fr/xephi/authme/message/messages_en_old.yml b/src/test/resources/fr/xephi/authme/message/messages_en_old.yml new file mode 100644 index 000000000..92def98f9 --- /dev/null +++ b/src/test/resources/fr/xephi/authme/message/messages_en_old.yml @@ -0,0 +1,121 @@ +# messages_en.yml file with old keys and placeholders (prior to 5.5) +# Messages commented with '# Custom' don't correspond to the default messages, +# allowing to check that they have not been overridden + +# Registration +reg_msg: '&3Please, register to the server with the command: /register ' +usage_reg: '&cUsage: /register ' +reg_only: '&4Only registered users can join the server! Please visit http://example.com to register yourself!' +kicked_admin_registered: 'An admin just registered you; please log in again' +registered: '&2Successfully registered!' +reg_disabled: '&cIn-game registration is disabled!' +user_regged: '&cYou already have registered this username!' + +# Password errors on registration +password_error: 'Password error message' ## Custom +password_error_nick: '&cYou can''t use your name as password, please choose another one...' +password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' +password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX' +pass_len: '&cYour password is too short or too long! Please try with another one!' + +# Login +usage_log: '&cUsage: /login ' +wrong_pwd: '&cWrong password!' +login: '&2Successful login!' +login_msg: '&cPlease, login with the command: /login ' +timeout: '&4Login timeout exceeded, you have been kicked from the server, please try again!' + +# Errors +unknown_user: '&cThis user isn''t registered!' +denied_command: '&cIn order to use this command you must be authenticated!' +denied_chat: '&cIn order to chat you must be authenticated!' +not_logged_in: '&cYou''re not logged in!' +tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' +max_reg: '&cYou have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection!' +no_perm: '&4You don''t have the permission to perform this action!' +error: '&4An unexpected error occurred, please contact an administrator!' +kick_forvip: '&3A VIP player has joined the server when it was full!' + +# AntiBot +kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' +antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' +antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' + +# Other messages +unregistered: '&cSuccessfully unregistered!' +accounts_owned_self: 'You own %count accounts:' +accounts_owned_other: 'The player %name has %count accounts:' +two_factor_create: '&2Your secret code is %code. You can scan it from here %url' +recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' +recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' +recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' +recovery_code_correct: 'Recovery code entered correctly!' +recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' +vb_nonActiv: '&cYour account isn''t activated yet, please check your emails!' +usage_unreg: '&cUsage: /unregister ' +pwd_changed: '&2Password changed successfully!' +logged_in: '&cYou''re already logged in!' +logout: '&2Logged out successfully!' +reload: '&2Configuration and database have been reloaded correctly!' +usage_changepassword: '&cUsage: /changepassword ' + +# Session messages +invalid_session: '&cYour IP has been changed and your session data has expired!' +valid_session: '&2Logged-in due to Session Reconnection.' + +# Error messages when joining +name_len: '&4Your username is either too short or too long!' +regex: 'not valid username: Allowed chars are REG_EX' ## Custom +country_banned: '&4Your country is banned from this server!' +not_owner_error: 'You are not the owner of this account. Please choose another name!' +kick_fullserver: '&4The server is full, try again later!' +same_nick: '&4The same username is already playing on the server!' +invalid_name_case: 'You should join using username %valid, not %invalid.' +same_ip_online: 'A player with the same IP is already in game!' + +# Email +usage_email_add: '&cUsage: /email add ' +usage_email_change: '&cUsage: /email change ' +usage_email_recovery: '&cUsage: /email recovery ' +new_email_invalid: '&cInvalid new email, try again!' +old_email_invalid: 'Email (old) is not valid!!' ## Custom +email_invalid: '&cInvalid email address, try again!' +email_added: '&2Email address successfully added to your account!' +email_confirm: '&cPlease confirm your email address!' +email_changed: '&2Email address changed correctly!' +email_send: '&2Recovery email sent successfully! Please check your email inbox!' +email_show: '&2Your current email address is: &f%email' +incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' +email_already_used: '&4The email address is already being used' +email_send_failure: 'The email could not be sent. Please contact an administrator.' +show_no_email: '&2You currently don''t have email address associated with this account.' +add_email: '&3Please add your email to your account with the command: /email add ' +recovery_email: '&3Forgot your password? Please use the command: /email recovery ' +change_password_expired: 'You cannot change your password using this command anymore.' +email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + +# Captcha +usage_captcha: '&3To log in you have to solve a captcha code, please use the command: /captcha ' +wrong_captcha: 'The captcha code is THE_CAPTCHA for you' ## Custom +valid_captcha: '&2Captcha code solved correctly!' +captcha_for_registration: 'Now type /captcha ' ## Custom +register_captcha_valid: '&2Valid captcha! You may now register with /register' + +# Verification code +verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' +usage_verification_code: '&cUsage: /verification ' +incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' +verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' +verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' +verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' +verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' + +# Time units +second: 'second' +seconds: 'seconds in plural' ## Custom +minute: 'minute' +minutes: 'minutes' +hour: 'hour' +hours: 'hours' +day: 'day' +days: 'days' diff --git a/src/test/resources/fr/xephi/authme/message/messages_test.yml b/src/test/resources/fr/xephi/authme/message/messages_test.yml index d955a6a64..b7670dc2d 100644 --- a/src/test/resources/fr/xephi/authme/message/messages_test.yml +++ b/src/test/resources/fr/xephi/authme/message/messages_test.yml @@ -1,9 +1,13 @@ # Sample messages file -unknown_user: 'We''ve got%nl%new lines%nl%and '' apostrophes' -login: '&cHere we have&bdefined some colors &dand some other <hings' +error: + unregistered_user: 'We''ve got%nl%new lines%nl%and '' apostrophes' +login: + success: '&cHere we have&bdefined some colors &dand some other <hings' + wrong_password: '&cWrong password!' + command_usage: '&cUsage: /login ' reg_voluntarily: 'You can register yourself to the server with the command "/register "' -usage_log: '&cUsage: /login ' -wrong_pwd: '&cWrong password!' -wrong_captcha: 'Use /captcha THE_CAPTCHA to solve the captcha' -email_already_used: '' +captcha: + wrong_captcha: 'Use /captcha %captcha_code to solve the captcha' +email: + already_used: '' From e50a1e26e456332e5e8ec0ef6bdfa2fa8fd3b65f Mon Sep 17 00:00:00 2001 From: ljacqu Date: Tue, 30 Jan 2018 22:15:07 +0100 Subject: [PATCH 016/155] Update ISSUE_TEMPLATE.MD --- .github/ISSUE_TEMPLATE.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.MD b/.github/ISSUE_TEMPLATE.MD index dce4b445f..c8c99783b 100644 --- a/.github/ISSUE_TEMPLATE.MD +++ b/.github/ISSUE_TEMPLATE.MD @@ -19,7 +19,7 @@ Standalone server/Bungeecord network, SQLite/MySql, ... This can be found by running `/authme version` ### Error Log: -Pastebin/Hastebin/Gist link of the error logo or stacktrace (if any) +Pastebin/Hastebin/Gist link of the error log or stacktrace (if any) ### Configuration: Pastebin/Hastebin/Gist link of your config.yml file (remember to delete any sensitive data) From e0e926354615dca377bb714e270c819612af4b86 Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Wed, 31 Jan 2018 19:10:05 +0100 Subject: [PATCH 017/155] Remove travis, update circle ci config --- .travis.yml | 25 ------------------------- README.md | 1 - circle.yml | 14 +++++++------- pom.xml | 2 +- 4 files changed, 8 insertions(+), 34 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 56146b568..000000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -sudo: required -addons: - apt: - packages: - - oracle-java8-installer - - git - -language: java -jdk: - - oraclejdk8 - - oraclejdk9 - -before_script: - - "sudo git clone https://www.github.com/P-H-C/phc-winner-argon2.git argon2-src" - - "cd argon2-src && sudo make && sudo make install && cd .." - -script: mvn clean verify -B - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/952357dbd9d3cea70fd5 - on_success: change # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always - on_start: false # default: false diff --git a/README.md b/README.md index 7a0994c1c..ee54889dc 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ - CI Services: - [Official Jenkins](http://ci.xephi.fr/job/AuthMeReloaded) (**DEVELOPMENT BUILDS**) - - Travis CI: [![Travis CI](https://travis-ci.org/AuthMe/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/AuthMe/AuthMeReloaded) - CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) - Project status: diff --git a/circle.yml b/circle.yml index 169726b8a..d6d8fcbbc 100644 --- a/circle.yml +++ b/circle.yml @@ -1,20 +1,20 @@ machine: java: version: oraclejdk8 + dependencies: pre: - "sudo apt-get update; sudo apt-get install -y git; sudo git clone https://www.github.com/P-H-C/phc-winner-argon2.git argon2-src; cd argon2-src; sudo make; sudo make install" -general: - artifacts: - - "target/AuthMe-*.jar" + override: + - mvn -DskipTests clean install dependency:resolve-plugins dependency:go-offline + test: override: - - mvn clean install -B + - mvn -o surefire:test post: - - cp ./target/AuthMe-*.jar $CIRCLE_ARTIFACTS - - cp ./target/AuthMe-*.jar $CIRCLE_ARTIFACTS/AuthMe.jar - mkdir -p $CIRCLE_TEST_REPORTS/junit/ - find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; + notify: webhooks: - - url: https://webhooks.gitter.im/e/7b92ac1a1741748b26bf + - url: https://discordapp.com/api/webhooks/408312347388674048/Tv5q5C1Hyn-QnDis4GT0A2jlFtJ_n1Dw3R9LGIxLCNaZd7BuCKvPWIQSY-yHm9N_RCUi/slack diff --git a/pom.xml b/pom.xml index 4a5292ce8..83bafa471 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ jenkins - http://ci.xephi.fr/job/AuthMeReloaded/ + http://ci.codemc.org/job/AuthMeReloaded/ From a1305073c1b10589495ef6b68621684fb5b46458 Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Wed, 31 Jan 2018 19:31:54 +0100 Subject: [PATCH 018/155] Use circle 2.0 --- .circleci/circle.yml | 51 ++++++++++++++++++++++++++++++++++++++++++++ circle.yml | 20 ----------------- 2 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 .circleci/circle.yml delete mode 100644 circle.yml diff --git a/.circleci/circle.yml b/.circleci/circle.yml new file mode 100644 index 000000000..a93423042 --- /dev/null +++ b/.circleci/circle.yml @@ -0,0 +1,51 @@ +version: 2 +jobs: + build_and_test_jdk8: + working_directory: ~/authmereloaded-jdk8 + docker: + - image: circleci/openjdk:8-jdk + environment: + MAVEN_OPTS: -Xmx2048m + steps: + - checkout + - restore_cache: + key: authmereloaded-{{ checksum "pom.xml" }} + - run: mvn -T 2 dependency:go-offline + - save_cache: + paths: + - ~/.m2 + key: authmereloaded-{{ checksum "pom.xml" }} + - run: mvn -T 2 package + - store_test_results: + path: target/surefire-reports + - store_artifacts: + path: target/*.jar + build_and_test_jdk9: + working_directory: ~/authmereloaded-jdk9 + docker: + - image: circleci/openjdk:9-jdk + environment: + MAVEN_OPTS: -Xmx2048m + steps: + - checkout + - restore_cache: + key: authmereloaded-{{ checksum "pom.xml" }} + - run: mvn -T 2 dependency:go-offline + - save_cache: + paths: + - ~/.m2 + key: authmereloaded-{{ checksum "pom.xml" }} + - run: mvn -T 2 package + - store_test_results: + path: target/surefire-reports + - store_artifacts: + path: target/*.jar +workflows: + version: 2 + build_and_test: + jobs: + - build_and_test_jdk8 + - build_and_test_jdk9 +notify: + webhooks: + - url: https://discordapp.com/api/webhooks/408312347388674048/Tv5q5C1Hyn-QnDis4GT0A2jlFtJ_n1Dw3R9LGIxLCNaZd7BuCKvPWIQSY-yHm9N_RCUi/slack diff --git a/circle.yml b/circle.yml deleted file mode 100644 index d6d8fcbbc..000000000 --- a/circle.yml +++ /dev/null @@ -1,20 +0,0 @@ -machine: - java: - version: oraclejdk8 - -dependencies: - pre: - - "sudo apt-get update; sudo apt-get install -y git; sudo git clone https://www.github.com/P-H-C/phc-winner-argon2.git argon2-src; cd argon2-src; sudo make; sudo make install" - override: - - mvn -DskipTests clean install dependency:resolve-plugins dependency:go-offline - -test: - override: - - mvn -o surefire:test - post: - - mkdir -p $CIRCLE_TEST_REPORTS/junit/ - - find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; - -notify: - webhooks: - - url: https://discordapp.com/api/webhooks/408312347388674048/Tv5q5C1Hyn-QnDis4GT0A2jlFtJ_n1Dw3R9LGIxLCNaZd7BuCKvPWIQSY-yHm9N_RCUi/slack From bdb954649e0829a703245c8d14f498811669e469 Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Wed, 31 Jan 2018 19:39:15 +0100 Subject: [PATCH 019/155] Fix circle ci artifacts --- .circleci/circle.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.circleci/circle.yml b/.circleci/circle.yml index a93423042..7b70a191d 100644 --- a/.circleci/circle.yml +++ b/.circleci/circle.yml @@ -38,14 +38,10 @@ jobs: - run: mvn -T 2 package - store_test_results: path: target/surefire-reports - - store_artifacts: - path: target/*.jar + - run: cp ./target/*.jar $CIRCLE_ARTIFACTS workflows: version: 2 build_and_test: jobs: - build_and_test_jdk8 - build_and_test_jdk9 -notify: - webhooks: - - url: https://discordapp.com/api/webhooks/408312347388674048/Tv5q5C1Hyn-QnDis4GT0A2jlFtJ_n1Dw3R9LGIxLCNaZd7BuCKvPWIQSY-yHm9N_RCUi/slack From a0080686ac8dcf255baac6ead7c619f5cf3a8bc3 Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Wed, 31 Jan 2018 19:49:03 +0100 Subject: [PATCH 020/155] Reuse old circle caches --- .circleci/circle.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/circle.yml b/.circleci/circle.yml index 7b70a191d..cb1e2ba61 100644 --- a/.circleci/circle.yml +++ b/.circleci/circle.yml @@ -9,7 +9,9 @@ jobs: steps: - checkout - restore_cache: - key: authmereloaded-{{ checksum "pom.xml" }} + keys: + - authmereloaded-{{ checksum "pom.xml" }} + - authmereloaded- - run: mvn -T 2 dependency:go-offline - save_cache: paths: From 74c06346d8b8fe31cd77c6eab3f5c71e0c3bdf1a Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Wed, 31 Jan 2018 21:43:55 +0100 Subject: [PATCH 021/155] Eureka, found a better solution to deploy the non shaded jar as default artifact! --- pom.xml | 51 +++++++-------------------------------------------- 1 file changed, 7 insertions(+), 44 deletions(-) diff --git a/pom.xml b/pom.xml index 83bafa471..b508bfa19 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ clean install - ${project.outputName}-${project.version} + ${project.outputName}-${project.version}-noshade @@ -197,6 +197,7 @@ + ${project.outputName}-${project.version} public false @@ -213,6 +214,9 @@ + + ${project.outputName}-${project.version} + org.apache.maven.plugins @@ -228,6 +232,8 @@ false + true + ${project.outputName}-${project.version} - ${project.distributionManagement.snapshotRepository.id} - ${project.distributionManagement.snapshotRepository.url} - pom.xml - target/original-${project.build.finalName}.jar - - org.eluder.coveralls From f67ddb0c77c0a2cf9dc31613af12542c1832cb43 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Thu, 1 Feb 2018 20:22:18 +0100 Subject: [PATCH 022/155] #1467 Migrate all message files, make sure migrater keeps predefined order --- .../message/updater/MessageUpdater.java | 22 +- src/main/resources/messages/messages_bg.yml | 208 +++++++++------- src/main/resources/messages/messages_br.yml | 208 +++++++++------- src/main/resources/messages/messages_cz.yml | 207 +++++++++------- src/main/resources/messages/messages_de.yml | 207 +++++++++------- src/main/resources/messages/messages_eo.yml | 207 +++++++++------- src/main/resources/messages/messages_es.yml | 209 ++++++++-------- src/main/resources/messages/messages_et.yml | 207 +++++++++------- src/main/resources/messages/messages_eu.yml | 208 +++++++++------- src/main/resources/messages/messages_fi.yml | 208 +++++++++------- src/main/resources/messages/messages_fr.yml | 225 +++++++++-------- src/main/resources/messages/messages_gl.yml | 208 +++++++++------- src/main/resources/messages/messages_hu.yml | 207 +++++++++------- src/main/resources/messages/messages_id.yml | 208 +++++++++------- src/main/resources/messages/messages_it.yml | 225 +++++++++-------- src/main/resources/messages/messages_ko.yml | 232 +++++++++-------- src/main/resources/messages/messages_lt.yml | 208 +++++++++------- src/main/resources/messages/messages_nl.yml | 223 +++++++++-------- src/main/resources/messages/messages_pl.yml | 207 +++++++++------- src/main/resources/messages/messages_pt.yml | 209 ++++++++-------- src/main/resources/messages/messages_ro.yml | 207 +++++++++------- src/main/resources/messages/messages_ru.yml | 231 +++++++++-------- src/main/resources/messages/messages_sk.yml | 207 +++++++++------- src/main/resources/messages/messages_tr.yml | 224 +++++++++-------- src/main/resources/messages/messages_uk.yml | 207 +++++++++------- src/main/resources/messages/messages_vn.yml | 208 +++++++++------- src/main/resources/messages/messages_zhcn.yml | 233 ++++++++++-------- src/main/resources/messages/messages_zhhk.yml | 207 +++++++++------- src/main/resources/messages/messages_zhmc.yml | 208 +++++++++------- src/main/resources/messages/messages_zhtw.yml | 208 +++++++++------- .../message/YamlTextFileCheckerTest.java | 4 +- 31 files changed, 3366 insertions(+), 2821 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index 37bbe735e..a833d4c19 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -14,7 +14,10 @@ import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import java.io.File; +import java.util.Arrays; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * Migrates the used messages file to a complete, up-to-date version when necessary. @@ -91,10 +94,6 @@ public class MessageUpdater { * @return the configuration data to export with */ private static ConfigurationData buildConfigurationData() { - PropertyListBuilder builder = new PropertyListBuilder(); - for (MessageKey messageKey : MessageKey.values()) { - builder.add(new StringProperty(messageKey.getKey(), "")); - } Map comments = ImmutableMap.builder() .put("registration", new String[]{"Registration"}) .put("password", new String[]{"Password errors on registration"}) @@ -111,6 +110,21 @@ public class MessageUpdater { .put("verification", new String[]{"Verification code"}) .put("time", new String[]{"Time units"}) .build(); + + Set addedKeys = new HashSet<>(); + PropertyListBuilder builder = new PropertyListBuilder(); + // Add one key per section based on the comments map above so that the order is clear + for (String path : comments.keySet()) { + MessageKey key = Arrays.stream(MessageKey.values()).filter(p -> p.getKey().startsWith(path + ".")) + .findFirst().orElseThrow(() -> new IllegalStateException(path)); + builder.add(new StringProperty(key.getKey(), "")); + addedKeys.add(key.getKey()); + } + // Add all remaining keys to the property list builder + Arrays.stream(MessageKey.values()) + .filter(key -> !addedKeys.contains(key.getKey())) + .forEach(key -> builder.add(new StringProperty(key.getKey(), ""))); + return new ConfigurationData(builder.create(), comments); } diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index d43639d55..64341683c 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&3Моля регистрирайте се с: /register парола парола' -usage_reg: '&cКоманда: /register парола парола' -reg_only: '&4Само регистрирани потребители могат да влизат в сървъра! Моля посетете http://example.com, за да се регистрирате!' -kicked_admin_registered: 'Ти беше регистриран от администратора, моля влезте отново' -registered: '&2Успешна регистрация!' -reg_disabled: '&cРегистрациите са изключени!' -user_regged: '&cПотребителското име е заетo!' +registration: + disabled: '&cРегистрациите са изключени!' + name_taken: '&cПотребителското име е заетo!' + register_request: '&3Моля регистрирайте се с: /register парола парола' + command_usage: '&cКоманда: /register парола парола' + reg_only: '&4Само регистрирани потребители могат да влизат в сървъра! Моля посетете http://example.com, за да се регистрирате!' + success: '&2Успешна регистрация!' + kicked_admin_registered: 'Ти беше регистриран от администратора, моля влезте отново' # Password errors on registration -password_error: '&cПаролите не съвпадат, провете ги отново!' -password_error_nick: '&cНе можеш да използваш потребителското си име за парола, моля изберете друга парола.' -password_error_unsafe: '&cИзбраната парола не е безопасна, моля изберете друга парола.' -password_error_chars: '&4Паролата съдържа непозволени символи. Позволени символи: REG_EX' -pass_len: '&cПаролата е твърде къса или прекалено дълга! Моля опитайте с друга парола.' +password: + match_error: '&cПаролите не съвпадат, провете ги отново!' + name_in_password: '&cНе можеш да използваш потребителското си име за парола, моля изберете друга парола.' + unsafe_password: '&cИзбраната парола не е безопасна, моля изберете друга парола.' + forbidden_characters: '&4Паролата съдържа непозволени символи. Позволени символи: %valid_chars' + wrong_length: '&cПаролата е твърде къса или прекалено дълга! Моля опитайте с друга парола.' # Login -usage_log: '&cКоманда: /login парола' -wrong_pwd: '&cГрешна парола!' -login: '&2Успешен вход!' -login_msg: '&cМоля влезте с: /login парола' -timeout: '&4Времето за вход изтече, беше кикнат от сървъра. Моля опитайте отново!' +login: + command_usage: '&cКоманда: /login парола' + wrong_password: '&cГрешна парола!' + success: '' + login_request: '&cМоля влезте с: /login парола' + timeout_error: '&4Времето за вход изтече, беше кикнат от сървъра. Моля опитайте отново!' # Errors -unknown_user: '&cПотребителското име не е регистрирано!' -denied_command: '&cЗа да използваш тази команда трябва да си си влезнал в акаунта!' -denied_chat: '&cЗа да пишеш в чата трябва даи сиси влезнал в акаунта!' -not_logged_in: '&cНе си влязъл!' -tempban_max_logins: '&cТи беше баннат временно, понеже си сгрешил паролата прекалено много пъти.' -max_reg: '&cТи си достигнал максималният брой регистрации (%reg_count/%max_acc %reg_names)!' -no_perm: '&4Нямаш нужните права за това действие!' -error: '&4Получи се неочаквана грешка, моля свържете се с администратора!' -kick_forvip: '&3VIP потребител влезе докато сървъра беше пълен, ти беше изгонен!' +error: + denied_command: '&cЗа да използваш тази команда трябва да си си влезнал в акаунта!' + denied_chat: '&cЗа да пишеш в чата трябва даи сиси влезнал в акаунта!' + unregistered_user: '&cПотребителското име не е регистрирано!' + not_logged_in: '&cНе си влязъл!' + no_permission: '&4Нямаш нужните права за това действие!' + unexpected_error: '' + max_registration: '&cТи си достигнал максималният брой регистрации (%reg_count/%max_acc %reg_names)!' + logged_in: '&cВече си вписан!' + kick_for_vip: '&3VIP потребител влезе докато сървъра беше пълен, ти беше изгонен!' + tempban_max_logins: '&cТи беше баннат временно, понеже си сгрешил паролата прекалено много пъти.' # AntiBot -kick_antibot: 'Защитата от ботове е включена! Трябва да изчакаш няколко минути преди да влезеш в сървъра.' -antibot_auto_enabled: '&4Защитата за ботове е включена заради потенциална атака!' -antibot_auto_disabled: '&2Защитата за ботове ще се изключи след %m минута/и!' +antibot: + kick_antibot: 'Защитата от ботове е включена! Трябва да изчакаш няколко минути преди да влезеш в сървъра.' + auto_enabled: '&4Защитата за ботове е включена заради потенциална атака!' + auto_disabled: '&2Защитата за ботове ще се изключи след %m минута/и!' + +# Unregister +unregister: + success: '&cРегистрацията е премахната успешно!' + command_usage: '&cКоманда: /unregister парола' # Other messages -unregistered: '&cРегистрацията е премахната успешно!' -accounts_owned_self: 'Претежаваш %count акаунт/а:' -accounts_owned_other: 'Потребителят %name има %count акаунт/а:' -two_factor_create: '&2Кода е %code. Можеш да го провериш оттука: %url' -recovery_code_sent: 'Възстановяващият код беше изпратен на твоят email адрес.' -# TODO: Missing tags %count -recovery_code_incorrect: 'Възстановяващият код е неправилен! Използвайте: /email recovery имейл, за да генерирате нов' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&cТвоят акаунт все още не е актириван, моля провете своят email адрес!' -usage_unreg: '&cКоманда: /unregister парола' -pwd_changed: '&2Паротала е променена успешно!' -logged_in: '&cВече си вписан!' -logout: '&2Излязохте успешно!' -reload: '&2Конфигурацията и база данните бяха презаредени правилно!' -usage_changepassword: '&cКоманда: /changepassword Стара-Парола Нова-Парола' +misc: + account_not_activated: '&cТвоят акаунт все още не е актириван, моля провете своят email адрес!' + password_changed: '&2Паротала е променена успешно!' + logout: '&2Излязохте успешно!' + reload: '&2Конфигурацията и база данните бяха презаредени правилно!' + usage_change_password: '&cКоманда: /changepassword Стара-Парола Нова-Парола' + two_factor_create: '&2Кода е %code. Можеш да го провериш оттука: %url' + accounts_owned_self: 'Претежаваш %count акаунт/а:' + accounts_owned_other: 'Потребителят %name има %count акаунт/а:' # Session messages -invalid_session: '&cТвоят IP се е променил и сесията беше прекратена.' -valid_session: '&2Сесията е продължена.' +session: + valid_session: '&2Сесията е продължена.' + invalid_session: '&cТвоят IP се е променил и сесията беше прекратена.' # Error messages when joining -name_len: '&4Потребителското име е прекалено късо или дълга. Моля опитайте с друго потребителско име!' -regex: '&4Потребителското име съдържа забранени знаци. Позволени знаци: REG_EX' -country_banned: '&4Твоята държава е забранена в този сървър!' -not_owner_error: 'Ти не си собственика на този акаунт. Моля избери друго потребителско име!' -kick_fullserver: '&4Сървъра е пълен, моля опитайте отново!' -same_nick: '&4Вече има потребител, който играете в сървъра със същото потребителско име!' -invalid_name_case: 'Трябва да влезеш с %valid, а не с %invalid.' -same_ip_online: 'Вече има потребител със същото IP в сървъра!' +on_join_validation: + same_ip_online: 'Вече има потребител със същото IP в сървъра!' + same_nick_online: '&4Вече има потребител, който играете в сървъра със същото потребителско име!' + name_length: '&4Потребителското име е прекалено късо или дълга. Моля опитайте с друго потребителско име!' + characters_in_name: '&4Потребителското име съдържа забранени знаци. Позволени знаци: %valid_chars' + kick_full_server: '&4Сървъра е пълен, моля опитайте отново!' + country_banned: '&4Твоята държава е забранена в този сървър!' + not_owner_error: 'Ти не си собственика на този акаунт. Моля избери друго потребителско име!' + invalid_name_case: 'Трябва да влезеш с %valid, а не с %invalid.' # Email -usage_email_add: '&cКоманда: /email add имейл имейл' -usage_email_change: '&cКоманда: /email change Стар-Имейл Нов-Имейл' -usage_email_recovery: '&cКоманда: /email recovery имейл' -new_email_invalid: '&cНовият имейл е грешен, опитайте отново!' -old_email_invalid: '&cСтарият имейл е грешен, опитайте отново!' -email_invalid: '&cИмейла е невалиден, опитайте с друг!' -email_added: '&2Имейл адреса е добавен!' -email_confirm: '&cМоля потвърди своя имейл адрес!' -email_changed: '&2Имейл адреса е сменен!' -email_send: '&2Възстановяващият имейл е изпратен успешно. Моля провете пощата си!' -email_show: '&2Твоят имейл адрес е: &f%email' -incomplete_email_settings: 'Грешка: Не всички настройки са написани за изпращане на имейл адрес. Моля свържете се с администратора!' -email_already_used: '&4Имейл адреса вече се използва, опитайте с друг.' -email_send_failure: 'Съобщението не беше изпратено. Моля свържете се с администратора.' -show_no_email: '&2Няма добавен имейл адрес към акаунта.' -add_email: '&3Моля добавете имейл адрес към своят акаунт: /email add имейл имейл' -recovery_email: '&3Забравена парола? Използвайте: /email recovery имейл' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -email_cooldown_error: '&cВече е бил изпратен имейл адрес. Трябва а изчакаш %time преди да пратиш нов.' +email: + add_email_request: '&3Моля добавете имейл адрес към своят акаунт: /email add имейл имейл' + usage_email_add: '&cКоманда: /email add имейл имейл' + usage_email_change: '&cКоманда: /email change Стар-Имейл Нов-Имейл' + new_email_invalid: '&cНовият имейл е грешен, опитайте отново!' + old_email_invalid: '&cСтарият имейл е грешен, опитайте отново!' + invalid: '&cИмейла е невалиден, опитайте с друг!' + added: '&2Имейл адреса е добавен!' + request_confirmation: '&cМоля потвърди своя имейл адрес!' + changed: '&2Имейл адреса е сменен!' + email_show: '&2Твоят имейл адрес е: &f%email' + no_email_for_account: '&2Няма добавен имейл адрес към акаунта.' + already_used: '&4Имейл адреса вече се използва, опитайте с друг.' + incomplete_settings: 'Грешка: Не всички настройки са написани за изпращане на имейл адрес. Моля свържете се с администратора!' + send_failure: 'Съобщението не беше изпратено. Моля свържете се с администратора.' + change_password_expired: '' + email_cooldown_error: '&cВече е бил изпратен имейл адрес. Трябва а изчакаш %time преди да пратиш нов.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Забравена парола? Използвайте: /email recovery имейл' + command_usage: '&cКоманда: /email recovery имейл' + email_sent: '&2Възстановяващият имейл е изпратен успешно. Моля провете пощата си!' + code: + code_sent: 'Възстановяващият код беше изпратен на твоят email адрес.' + incorrect: 'Възстановяващият код е неправилен! Използвайте: /email recovery имейл, за да генерирате нов' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&3Моля въведе цифрите/буквите от капчата: /captcha ' -wrong_captcha: '&cКода е грешен, използвайте: "/captcha THE_CAPTCHA" в чата!' -valid_captcha: '&2Кода е валиден!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Моля въведе цифрите/буквите от капчата: /captcha %captcha_code' + wrong_captcha: '&cКода е грешен, използвайте: "/captcha %captcha_code" в чата!' + valid_captcha: '&2Кода е валиден!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'секунда' -seconds: 'секунди' -minute: 'минута' -minutes: 'минути' -hour: 'час' -hours: 'часа' -day: 'ден' -days: 'дена' +time: + second: 'секунда' + seconds: 'секунди' + minute: 'минута' + minutes: 'минути' + hour: 'час' + hours: 'часа' + day: 'ден' + days: 'дена' diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index a0200415e..3318f8a35 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -3,120 +3,138 @@ # http://gamersboard.com.br/ | www.magitechserver.com # Registration -reg_msg: '&3Por favor, registre-se com o comando "/register "' -usage_reg: '&cUse: /register ' -reg_only: '&4Somente usuários registrados podem entrar no servidor! Por favor visite www.seusite.com para se registrar!' -kicked_admin_registered: 'Um administrador registrou você, por favor faça login novamente' -registered: '&2Registrado com êxito!' -reg_disabled: '&cO registro está desativado nesse servidor!' -user_regged: '&cVocê já registrou este nome de usuário!' +registration: + disabled: '&cO registro está desativado nesse servidor!' + name_taken: '&cVocê já registrou este nome de usuário!' + register_request: '&3Por favor, registre-se com o comando "/register "' + command_usage: '&cUse: /register ' + reg_only: '&4Somente usuários registrados podem entrar no servidor! Por favor visite www.seusite.com para se registrar!' + success: '&2Registrado com êxito!' + kicked_admin_registered: 'Um administrador registrou você, por favor faça login novamente' # Password errors on registration -password_error: '&cAs senhas não coincidem, verifique novamente!' -password_error_nick: '&Você não pode usar o seu nome como senha, por favor, escolha outra senha...' -password_error_unsafe: '&cA senha escolhida não é segura, por favor, escolha outra...' -password_error_chars: '&Sua senha contém caracteres ilegais. caracteres permitidos: REG_EX' -pass_len: '&cSua senha é muito curta ou muito longa! Por favor, tente com outra!' +password: + match_error: '&cAs senhas não coincidem, verifique novamente!' + name_in_password: '&Você não pode usar o seu nome como senha, por favor, escolha outra senha...' + unsafe_password: '&cA senha escolhida não é segura, por favor, escolha outra...' + forbidden_characters: '&Sua senha contém caracteres ilegais. caracteres permitidos: %valid_chars' + wrong_length: '&cSua senha é muito curta ou muito longa! Por favor, tente com outra!' # Login -usage_log: '&cUse: /login ' -wrong_pwd: '&cSenha incorreta!' -login: '&2Login realizado com sucesso!' -login_msg: '&cPor favor, faça o login com o comando "/login "' -timeout: '&4Tempo limite de sessão excedido, você foi expulso do servidor, por favor, tente novamente!' +login: + command_usage: '&cUse: /login ' + wrong_password: '&cSenha incorreta!' + success: '' + login_request: '&cPor favor, faça o login com o comando "/login "' + timeout_error: '&4Tempo limite de sessão excedido, você foi expulso do servidor, por favor, tente novamente!' # Errors -unknown_user: '&cEste usuário não está registrado!' -denied_command: '&cPara utilizar este comando é necessário estar logado!' -denied_chat: '&cPara utilizar o chat, você deve estar logado!' -not_logged_in: '&cVocê não está logado!' -tempban_max_logins: '&cVocê foi temporariamente banido por tentar logar muitas vezes.' -max_reg: '&cVocê excedeu o número máximo de inscrições (%reg_count/%max_acc %reg_names) do seu IP!' -no_perm: '&4Você não tem permissão para executar esta ação!' -error: '&4Ocorreu um erro inesperado, por favor contacte um administrador!' -kick_forvip: '&3Um jogador VIP juntou-se ao servidor enquanto ele estava cheio!' +error: + denied_command: '&cPara utilizar este comando é necessário estar logado!' + denied_chat: '&cPara utilizar o chat, você deve estar logado!' + unregistered_user: '&cEste usuário não está registrado!' + not_logged_in: '&cVocê não está logado!' + no_permission: '&4Você não tem permissão para executar esta ação!' + unexpected_error: '' + max_registration: '&cVocê excedeu o número máximo de inscrições (%reg_count/%max_acc %reg_names) do seu IP!' + logged_in: '&cVocê já está logado!' + kick_for_vip: '&3Um jogador VIP juntou-se ao servidor enquanto ele estava cheio!' + tempban_max_logins: '&cVocê foi temporariamente banido por tentar logar muitas vezes.' # AntiBot -kick_antibot: 'O AntiBot está ativo, espere alguns minutos antes de entrar no servidor!' -antibot_auto_enabled: '&4O AntiBot foi ativado devido ao grande número de conexões!' -antibot_auto_disabled: '&2Desativando o AntiBot passados %m minutos!' +antibot: + kick_antibot: 'O AntiBot está ativo, espere alguns minutos antes de entrar no servidor!' + auto_enabled: '&4O AntiBot foi ativado devido ao grande número de conexões!' + auto_disabled: '&2Desativando o AntiBot passados %m minutos!' + +# Unregister +unregister: + success: '&cConta deletada!' + command_usage: '&cUse: /unregister ' # Other messages -unregistered: '&cConta deletada!' -accounts_owned_self: 'Você tem %count contas:' -accounts_owned_other: 'O jogador %name tem %count contas:' -two_factor_create: '&2O seu código secreto é %code. Você pode verificá-lo a partir daqui %url' -recovery_code_sent: 'Um código de recuperação para redefinir sua senha foi enviada para o seu e-mail.' -# TODO: Missing tags %count -recovery_code_incorrect: 'O código de recuperação esta incorreto! Use /email recovery [email] para gerar um novo!' -recovery_tries_exceeded: 'Você excedeu o limite de tentativas de usar o código de recuperação! Use "/email recovery [email]" para gerar um novo.' -recovery_code_correct: 'Código de recuperação aceito!' -recovery_change_password: 'Por favor, use o comando /email setpassword para alterar sua senha imediatamente!' -vb_nonActiv: '&cA sua conta ainda não está ativada, por favor, verifique seus e-mails!' -usage_unreg: '&cUse: /unregister ' -pwd_changed: '&2Senha alterada com sucesso!' -logged_in: '&cVocê já está logado!' -logout: '&2Desconectado com sucesso!' -reload: '&2Configuração e o banco de dados foram recarregados corretamente!' -usage_changepassword: '&cUse: /changepassword ' +misc: + account_not_activated: '&cA sua conta ainda não está ativada, por favor, verifique seus e-mails!' + password_changed: '&2Senha alterada com sucesso!' + logout: '&2Desconectado com sucesso!' + reload: '&2Configuração e o banco de dados foram recarregados corretamente!' + usage_change_password: '&cUse: /changepassword ' + two_factor_create: '&2O seu código secreto é %code. Você pode verificá-lo a partir daqui %url' + accounts_owned_self: 'Você tem %count contas:' + accounts_owned_other: 'O jogador %name tem %count contas:' # Session messages -invalid_session: '&O seu IP foi alterado e sua sessão expirou!' -valid_session: '&2Você deslogou recentemente, então sua sessão foi resumida!' +session: + valid_session: '&2Você deslogou recentemente, então sua sessão foi resumida!' + invalid_session: '&O seu IP foi alterado e sua sessão expirou!' # Error messages when joining -name_len: '&4Seu nome de usuário ou é muito curto ou muito longo!' -regex: '&4Seu nome de usuário contém caracteres inválidos. Caracteres permitidos: REG_EX' -country_banned: '&4O seu país está banido neste servidor!' -not_owner_error: 'Você não é o proprietário da conta. Por favor, escolha outro nome!' -kick_fullserver: '&4O servidor está cheio, tente novamente mais tarde!' -same_nick: '&4O mesmo nome de usuário já está jogando no servidor!' -invalid_name_case: 'Você deve se juntar usando nome de usuário %valid, não %invalid.' -same_ip_online: 'Um jogador com o mesmo IP já está no servidor!' +on_join_validation: + same_ip_online: 'Um jogador com o mesmo IP já está no servidor!' + same_nick_online: '&4O mesmo nome de usuário já está jogando no servidor!' + name_length: '&4Seu nome de usuário ou é muito curto ou muito longo!' + characters_in_name: '&4Seu nome de usuário contém caracteres inválidos. Caracteres permitidos: %valid_chars' + kick_full_server: '&4O servidor está cheio, tente novamente mais tarde!' + country_banned: '&4O seu país está banido neste servidor!' + not_owner_error: 'Você não é o proprietário da conta. Por favor, escolha outro nome!' + invalid_name_case: 'Você deve se juntar usando nome de usuário %valid, não %invalid.' # Email -usage_email_add: '&cUse: /email add ' -usage_email_change: '&cUse: /email change ' -usage_email_recovery: '&cUse: /email recovery ' -new_email_invalid: '&cE-mail novo inválido, tente novamente!' -old_email_invalid: '&cE-mail velho inválido, tente novamente!' -email_invalid: '&E-mail inválido, tente novamente!' -email_added: '&2Email adicionado com sucesso à sua conta!' -email_confirm: '&cPor favor confirme seu endereço de email!' -email_changed: '&2Troca de email com sucesso.!' -email_send: '&2Recuperação de email enviada com sucesso! Por favor, verifique sua caixa de entrada de e-mail!' -email_show: '&2O seu endereço de e-mail atual é: &f%email' -incomplete_email_settings: 'Erro: Nem todas as configurações necessárias estão definidas para o envio de e-mails. Entre em contato com um administrador.' -email_already_used: '&4O endereço de e-mail já está sendo usado' -email_send_failure: '&cO e-mail não pôde ser enviado, reporte isso a um administrador!' -show_no_email: '&2Você atualmente não têm endereço de e-mail associado a esta conta.' -add_email: '&3Por favor, adicione seu e-mail para a sua conta com o comando "/email add "' -recovery_email: '&3Esqueceu sua senha? Por favor, use o comando "/email recovery "' -change_password_expired: 'Você não pode mais usar esse comando de recuperação de senha!' -email_cooldown_error: '&cUm e-mail já foi enviado, espere mais %time antes de enviar novamente!' +email: + add_email_request: '&3Por favor, adicione seu e-mail para a sua conta com o comando "/email add "' + usage_email_add: '&cUse: /email add ' + usage_email_change: '&cUse: /email change ' + new_email_invalid: '&cE-mail novo inválido, tente novamente!' + old_email_invalid: '&cE-mail velho inválido, tente novamente!' + invalid: '&E-mail inválido, tente novamente!' + added: '&2Email adicionado com sucesso à sua conta!' + request_confirmation: '&cPor favor confirme seu endereço de email!' + changed: '&2Troca de email com sucesso.!' + email_show: '&2O seu endereço de e-mail atual é: &f%email' + no_email_for_account: '&2Você atualmente não têm endereço de e-mail associado a esta conta.' + already_used: '&4O endereço de e-mail já está sendo usado' + incomplete_settings: 'Erro: Nem todas as configurações necessárias estão definidas para o envio de e-mails. Entre em contato com um administrador.' + send_failure: '&cO e-mail não pôde ser enviado, reporte isso a um administrador!' + change_password_expired: 'Você não pode mais usar esse comando de recuperação de senha!' + email_cooldown_error: '&cUm e-mail já foi enviado, espere mais %time antes de enviar novamente!' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Esqueceu sua senha? Por favor, use o comando "/email recovery "' + command_usage: '&cUse: /email recovery ' + email_sent: '&2Recuperação de email enviada com sucesso! Por favor, verifique sua caixa de entrada de e-mail!' + code: + code_sent: 'Um código de recuperação para redefinir sua senha foi enviada para o seu e-mail.' + incorrect: 'O código de recuperação esta incorreto! Use /email recovery [email] para gerar um novo!' + tries_exceeded: 'Você excedeu o limite de tentativas de usar o código de recuperação! Use "/email recovery [email]" para gerar um novo.' + correct: 'Código de recuperação aceito!' + change_password: 'Por favor, use o comando /email setpassword para alterar sua senha imediatamente!' # Captcha -usage_captcha: '&3Para iniciar sessão você tem que resolver um código captcha, utilize o comando "/captcha "' -wrong_captcha: '&cCaptcha errado, por favor, escreva "/captcha THE_CAPTCHA" no chat!' -valid_captcha: '&2Código Captcha resolvido corretamente!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Para iniciar sessão você tem que resolver um código captcha, utilize o comando "/captcha %captcha_code"' + wrong_captcha: '&cCaptcha errado, por favor, escreva "/captcha %captcha_code" no chat!' + valid_captcha: '&2Código Captcha resolvido corretamente!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'segundo' -seconds: 'segundos' -minute: 'minuto' -minutes: 'minutos' -hour: 'hora' -hours: 'horas' -day: 'dia' -days: 'dias' +time: + second: 'segundo' + seconds: 'segundos' + minute: 'minuto' + minutes: 'minutos' + hour: 'hora' + hours: 'horas' + day: 'dia' + days: 'dias' diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index 8c01d4a61..6d98a3cda 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&cProsím, zaregistruj se pomocí "/register "' -usage_reg: '&cPoužij: "/register heslo ZnovuHeslo".' -reg_only: '&cServer je pouze pro registrované! Navštiv naší webovou stránku http://priklad.cz.' -kicked_admin_registered: 'Admin vás právě zaregistroval; Přihlašte se prosím znovu.' -registered: '&cÚspěšně zaregistrován!' -reg_disabled: '&cRegistrace je zakázána!' -user_regged: '&cUživatelské jméno je již zaregistrováno.' +registration: + disabled: '&cRegistrace je zakázána!' + name_taken: '&cUživatelské jméno je již zaregistrováno.' + register_request: '&cProsím, zaregistruj se pomocí "/register "' + command_usage: '&cPoužij: "/register heslo ZnovuHeslo".' + reg_only: '&cServer je pouze pro registrované! Navštiv naší webovou stránku http://priklad.cz.' + success: '&cÚspěšně zaregistrován!' + kicked_admin_registered: 'Admin vás právě zaregistroval; Přihlašte se prosím znovu.' # Password errors on registration -password_error: '&cHesla se neshodují!' -password_error_nick: '&cNemůžeš použít tvoje jméno jako heslo, prosím, zvol si jiné heslo...' -password_error_unsafe: '&cToto heslo není bezpečné, prosím, zvol si jiné heslo...' -password_error_chars: '&4Tvoje heslo obsahuje nepovolené znaky. Přípustné znaky jsou: REG_EX' -pass_len: '&cTvoje heslo nedosahuje minimální délky (4).' +password: + match_error: '&cHesla se neshodují!' + name_in_password: '&cNemůžeš použít tvoje jméno jako heslo, prosím, zvol si jiné heslo...' + unsafe_password: '&cToto heslo není bezpečné, prosím, zvol si jiné heslo...' + forbidden_characters: '&4Tvoje heslo obsahuje nepovolené znaky. Přípustné znaky jsou: %valid_chars' + wrong_length: '&cTvoje heslo nedosahuje minimální délky (4).' # Login -usage_log: '&cPoužij: "/login TvojeHeslo".' -wrong_pwd: '&cŠpatné heslo.' -login: '&cÚspěšně přihlášen!' -login_msg: '&cProsím přihlaš se pomocí "/login TvojeHeslo".' -timeout: '&cČas pro přihlášení vypršel!' +login: + command_usage: '&cPoužij: "/login TvojeHeslo".' + wrong_password: '&cŠpatné heslo.' + success: '' + login_request: '&cProsím přihlaš se pomocí "/login TvojeHeslo".' + timeout_error: '&cČas pro přihlášení vypršel!' # Errors -unknown_user: '&cUživatelské jméno není zaregistrováno.' -denied_command: '&cPokud chceš použít tento příkaz musíš být přihlášen/registrován!' -denied_chat: '&cNemůžeš psát do chatu dokuď se nepřihlásíš/nezaregistruješ!' -not_logged_in: '&cNejsi přihlášený!' -tempban_max_logins: '&cByl jsi dočasně zabanován za příliš mnoho neúspěšných pokusů o přihlášení.' -max_reg: '&cPřekročil(a) jsi limit pro počet účtů (%reg_count/%max_acc %reg_names) z jedné IP adresy.' -no_perm: '&cNa tento příkaz nemáš dostatečné pravomoce.' -error: '&cVyskytla se chyba - kontaktujte prosím administrátora ...' -kick_forvip: '&cOmlouváme se, ale VIP hráč se připojil na plný server!' +error: + denied_command: '&cPokud chceš použít tento příkaz musíš být přihlášen/registrován!' + denied_chat: '&cNemůžeš psát do chatu dokuď se nepřihlásíš/nezaregistruješ!' + unregistered_user: '&cUživatelské jméno není zaregistrováno.' + not_logged_in: '&cNejsi přihlášený!' + no_permission: '&cNa tento příkaz nemáš dostatečné pravomoce.' + unexpected_error: '' + max_registration: '&cPřekročil(a) jsi limit pro počet účtů (%reg_count/%max_acc %reg_names) z jedné IP adresy.' + logged_in: '&cJsi již přihlášen!' + kick_for_vip: '&cOmlouváme se, ale VIP hráč se připojil na plný server!' + tempban_max_logins: '&cByl jsi dočasně zabanován za příliš mnoho neúspěšných pokusů o přihlášení.' # AntiBot -kick_antibot: 'Bezpečnostní mód AntiBot je zapnut! Musíš počkat několik minut než se budeš moct připojit znovu na server.' -antibot_auto_enabled: '[AuthMe] AntiBotMod automaticky spuštěn z důvodu masivních připojení!' -antibot_auto_disabled: '[AuthMe] AntiBotMod automaticky ukončen po %m minutách, doufejme v konec invaze' +antibot: + kick_antibot: 'Bezpečnostní mód AntiBot je zapnut! Musíš počkat několik minut než se budeš moct připojit znovu na server.' + auto_enabled: '[AuthMe] AntiBotMod automaticky spuštěn z důvodu masivních připojení!' + auto_disabled: '[AuthMe] AntiBotMod automaticky ukončen po %m minutách, doufejme v konec invaze' + +# Unregister +unregister: + success: '&cÚspěšně odregistrován!' + command_usage: '&cPoužij: "/unregister TvojeHeslo".' # Other messages -unregistered: '&cÚspěšně odregistrován!' -accounts_owned_self: 'Vlastníš tyto účty (%count):' -accounts_owned_other: 'Hráč %name vlastní tyto účty (%count):' -two_factor_create: '&2Tvůj tajný kód je %code. Můžeš ho oskenovat zde %url' -recovery_code_sent: 'Kód pro obnovení hesla byl odeslán na váš email.' -recovery_code_incorrect: 'Obnovovací kód není správný! Počet zbývajících pokusů: %count.' -recovery_tries_exceeded: 'Překročil(a) jsi počet pokusů pro vložení kódu. Použij "/email recovery [email]" pro vygenerování nového.' -recovery_code_correct: 'Obnovovací kód zadán správně!!' -recovery_change_password: 'Prosíme, použijte příkaz /email setpassword pro změnu hesla okamžitě.' -vb_nonActiv: '&cTvůj účet není aktivovaný, zkontroluj si svůj E-mail.' -usage_unreg: '&cPoužij: "/unregister TvojeHeslo".' -pwd_changed: '&cHeslo změněno!' -logged_in: '&cJsi již přihlášen!' -logout: '&cÚspěšně jsi se odhlásil.' -reload: '&cZnovu načtení nastavení AuthMe proběhlo úspěšně.' -usage_changepassword: '&cPoužij: "/changepassword StaréHeslo NovéHeslo".' +misc: + account_not_activated: '&cTvůj účet není aktivovaný, zkontroluj si svůj E-mail.' + password_changed: '&cHeslo změněno!' + logout: '&cÚspěšně jsi se odhlásil.' + reload: '&cZnovu načtení nastavení AuthMe proběhlo úspěšně.' + usage_change_password: '&cPoužij: "/changepassword StaréHeslo NovéHeslo".' + two_factor_create: '&2Tvůj tajný kód je %code. Můžeš ho oskenovat zde %url' + accounts_owned_self: 'Vlastníš tyto účty (%count):' + accounts_owned_other: 'Hráč %name vlastní tyto účty (%count):' # Session messages -invalid_session: '&cChybná data při čtení, počkejte do vypršení.' -valid_session: '&cAutomatické znovupřihlášení.' +session: + valid_session: '&cAutomatické znovupřihlášení.' + invalid_session: '&cChybná data při čtení, počkejte do vypršení.' # Error messages when joining -name_len: '&cTvůj nick je přílíš krátký, nebo přílíš dlouhý' -regex: '&cTvůj nick obsahuje nepovolené znaky. Přípustné znaky jsou: REG_EX' -country_banned: 'Vaše země je na tomto serveru zakázána' -not_owner_error: 'Nejsi majitelem tohoto účtu, prosím zvol si jiné jméno!' -kick_fullserver: '&cServer je momentálně plný, zkus to prosím později!' -same_nick: '&cUživatel s tímto jménem již hraje na serveru.' -invalid_name_case: 'Měl by jsi použít jméno %valid, ne jméno %invalid.' -same_ip_online: 'Hráč se stejnou IP adresou nyní hraje na serveru!' +on_join_validation: + same_ip_online: 'Hráč se stejnou IP adresou nyní hraje na serveru!' + same_nick_online: '&cUživatel s tímto jménem již hraje na serveru.' + name_length: '&cTvůj nick je přílíš krátký, nebo přílíš dlouhý' + characters_in_name: '&cTvůj nick obsahuje nepovolené znaky. Přípustné znaky jsou: %valid_chars' + kick_full_server: '&cServer je momentálně plný, zkus to prosím později!' + country_banned: 'Vaše země je na tomto serveru zakázána' + not_owner_error: 'Nejsi majitelem tohoto účtu, prosím zvol si jiné jméno!' + invalid_name_case: 'Měl by jsi použít jméno %valid, ne jméno %invalid.' # Email -usage_email_add: '&fPoužij: /email add ' -usage_email_change: '&fPoužij: /email change ' -usage_email_recovery: '&fPoužij: /email recovery ' -new_email_invalid: '[AuthMe] Nový email je chybně zadán!' -old_email_invalid: '[AuthMe] Starý email je chybně zadán!' -email_invalid: '[AuthMe] Nesprávný email' -email_added: '[AuthMe] Email přidán!' -email_confirm: '[AuthMe] Potvrď prosím svůj email!' -email_changed: '[AuthMe] Email změněn!' -email_send: '[AuthMe] Email pro obnovení hesla odeslán!' -email_show: '&2Váš aktuální email je: &f%email' -incomplete_email_settings: 'Chyba: chybí některé důležité informace pro odeslání emailu. Kontaktujte prosím admina.' -email_already_used: '&4Tato emailová adresa je již používána' -email_send_failure: 'Email nemohl být odeslán. Kontaktujte prosím admina.' -show_no_email: '&2K tomuto účtu nemáte přidanou žádnou emailovou adresu.' -add_email: '&cPřidej prosím svůj email pomocí : /email add TvůjEmail TvůjEmail' -recovery_email: '&cZapomněl jsi heslo? Napiš: /email recovery ' -change_password_expired: 'Nemůžeš si změnit heslo pomocí toho příkazu.' -email_cooldown_error: '&cEmail už byl nedávno odeslán. Musíš čekat %time před odesláním nového.' +email: + add_email_request: '&cPřidej prosím svůj email pomocí : /email add TvůjEmail TvůjEmail' + usage_email_add: '&fPoužij: /email add ' + usage_email_change: '&fPoužij: /email change ' + new_email_invalid: '[AuthMe] Nový email je chybně zadán!' + old_email_invalid: '[AuthMe] Starý email je chybně zadán!' + invalid: '[AuthMe] Nesprávný email' + added: '[AuthMe] Email přidán!' + request_confirmation: '[AuthMe] Potvrď prosím svůj email!' + changed: '[AuthMe] Email změněn!' + email_show: '&2Váš aktuální email je: &f%email' + no_email_for_account: '&2K tomuto účtu nemáte přidanou žádnou emailovou adresu.' + already_used: '&4Tato emailová adresa je již používána' + incomplete_settings: 'Chyba: chybí některé důležité informace pro odeslání emailu. Kontaktujte prosím admina.' + send_failure: 'Email nemohl být odeslán. Kontaktujte prosím admina.' + change_password_expired: 'Nemůžeš si změnit heslo pomocí toho příkazu.' + email_cooldown_error: '&cEmail už byl nedávno odeslán. Musíš čekat %time před odesláním nového.' + +# Password recovery by email +recovery: + forgot_password_hint: '&cZapomněl jsi heslo? Napiš: /email recovery ' + command_usage: '&fPoužij: /email recovery ' + email_sent: '[AuthMe] Email pro obnovení hesla odeslán!' + code: + code_sent: 'Kód pro obnovení hesla byl odeslán na váš email.' + incorrect: 'Obnovovací kód není správný! Počet zbývajících pokusů: %count.' + tries_exceeded: 'Překročil(a) jsi počet pokusů pro vložení kódu. Použij "/email recovery [email]" pro vygenerování nového.' + correct: 'Obnovovací kód zadán správně!!' + change_password: 'Prosíme, použijte příkaz /email setpassword pro změnu hesla okamžitě.' # Captcha -usage_captcha: '&cPoužij: /captcha ' -wrong_captcha: '&cŠpatné opsana Captcha, pouzij prosim: /captcha THE_CAPTCHA' -valid_captcha: '&cZadaná captcha je v pořádku!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&cPoužij: /captcha %captcha_code' + wrong_captcha: '&cŠpatné opsana Captcha, pouzij prosim: /captcha %captcha_code' + valid_captcha: '&cZadaná captcha je v pořádku!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'sekundy' -seconds: 'sekund' -minute: 'minuty' -minutes: 'minut' -hour: 'hodiny' -hours: 'hodin' -day: 'dny' -days: 'dnu' +time: + second: 'sekundy' + seconds: 'sekund' + minute: 'minuty' + minutes: 'minut' + hour: 'hodiny' + hours: 'hodin' + day: 'dny' + days: 'dnu' diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 64b801d51..38d18f2f2 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&3Bitte registriere dich mit "/register "' -usage_reg: '&cBenutze: /register ' -reg_only: '&4Nur für registrierte Spieler! Bitte besuche http://example.com um dich zu registrieren.' -kicked_admin_registered: 'Ein Administrator hat dich bereits registriert; bitte logge dich erneut ein.' -registered: '&2Erfolgreich registriert!' -reg_disabled: '&cRegistrierungen sind deaktiviert' -user_regged: '&cDieser Benutzername ist schon vergeben' +registration: + disabled: '&cRegistrierungen sind deaktiviert' + name_taken: '&cDieser Benutzername ist schon vergeben' + register_request: '&3Bitte registriere dich mit "/register "' + command_usage: '&cBenutze: /register ' + reg_only: '&4Nur für registrierte Spieler! Bitte besuche http://example.com um dich zu registrieren.' + success: '&2Erfolgreich registriert!' + kicked_admin_registered: 'Ein Administrator hat dich bereits registriert; bitte logge dich erneut ein.' # Password errors on registration -password_error: '&cPasswörter stimmen nicht überein!' -password_error_nick: '&cDu kannst deinen Namen nicht als Passwort verwenden!' -password_error_unsafe: '&cPasswort unsicher! Bitte wähle ein anderes.' -password_error_chars: '&4Dein Passwort enthält unerlaubte Zeichen. Zulässige Zeichen: REG_EX' -pass_len: '&cDein Passwort ist zu kurz oder zu lang!' +password: + match_error: '&cPasswörter stimmen nicht überein!' + name_in_password: '&cDu kannst deinen Namen nicht als Passwort verwenden!' + unsafe_password: '&cPasswort unsicher! Bitte wähle ein anderes.' + forbidden_characters: '&4Dein Passwort enthält unerlaubte Zeichen. Zulässige Zeichen: %valid_chars' + wrong_length: '&cDein Passwort ist zu kurz oder zu lang!' # Login -usage_log: '&cBenutze: /login ' -wrong_pwd: '&cFalsches Passwort' -login: '&2Erfolgreich eingeloggt!' -login_msg: '&cBitte logge dich ein mit "/login "' -timeout: '&4Zeitüberschreitung beim Login' +login: + command_usage: '&cBenutze: /login ' + wrong_password: '&cFalsches Passwort' + success: '' + login_request: '&cBitte logge dich ein mit "/login "' + timeout_error: '&4Zeitüberschreitung beim Login' # Errors -unknown_user: '&cBenutzername nicht registriert!' -denied_command: '&cUm diesen Befehl zu nutzen musst du authentifiziert sein!' -denied_chat: '&cDu musst eingeloggt sein, um chatten zu können!' -not_logged_in: '&cNicht eingeloggt!' -tempban_max_logins: '&cDu bist wegen zu vielen fehlgeschlagenen Login-Versuchen temporär gebannt!' -max_reg: '&cDu hast die maximale Anzahl an Accounts erreicht (%reg_count/%max_acc %reg_names).' -no_perm: '&4Du hast keine Rechte, um diese Aktion auszuführen!' -error: '&4Ein Fehler ist aufgetreten. Bitte kontaktiere einen Administrator.' -kick_forvip: '&3Ein VIP-Spieler hat den vollen Server betreten!' +error: + denied_command: '&cUm diesen Befehl zu nutzen musst du authentifiziert sein!' + denied_chat: '&cDu musst eingeloggt sein, um chatten zu können!' + unregistered_user: '&cBenutzername nicht registriert!' + not_logged_in: '&cNicht eingeloggt!' + no_permission: '&4Du hast keine Rechte, um diese Aktion auszuführen!' + unexpected_error: '' + max_registration: '&cDu hast die maximale Anzahl an Accounts erreicht (%reg_count/%max_acc %reg_names).' + logged_in: '&cBereits eingeloggt!' + kick_for_vip: '&3Ein VIP-Spieler hat den vollen Server betreten!' + tempban_max_logins: '&cDu bist wegen zu vielen fehlgeschlagenen Login-Versuchen temporär gebannt!' # AntiBot -kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor du dich mit dem Server verbindest.' -antibot_auto_enabled: '&4[AntiBotService] AntiBotMod wurde aufgrund hoher Netzauslastung automatisch aktiviert!' -antibot_auto_disabled: '&2[AntiBotService] AntiBotMod wurde nach %m Minuten deaktiviert, hoffentlich ist die Invasion vorbei.' +antibot: + kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor du dich mit dem Server verbindest.' + auto_enabled: '&4[AntiBotService] AntiBotMod wurde aufgrund hoher Netzauslastung automatisch aktiviert!' + auto_disabled: '&2[AntiBotService] AntiBotMod wurde nach %m Minuten deaktiviert, hoffentlich ist die Invasion vorbei.' + +# Unregister +unregister: + success: '&cBenutzerkonto erfolgreich gelöscht!' + command_usage: '&cBenutze: /unregister ' # Other messages -unregistered: '&cBenutzerkonto erfolgreich gelöscht!' -accounts_owned_self: 'Du besitzt %count Accounts:' -accounts_owned_other: 'Der Spieler %name hat %count Accounts:' -two_factor_create: '&2Dein geheimer Code ist %code. Du kannst ihn hier abfragen: %url' -recovery_code_sent: 'Ein Wiederherstellungscode zum Zurücksetzen deines Passworts wurde an deine E-Mail-Adresse geschickt.' -recovery_code_incorrect: 'Der Wiederherstellungscode stimmt nicht! Du hast noch %count Versuche. Nutze /email recovery [email] um einen neuen zu generieren.' -recovery_tries_exceeded: 'Du hast die maximale Anzahl an Versuchen zur Eingabe des Wiederherstellungscodes überschritten. Benutze "/email recovery [email]" um einen neuen zu generieren.' -recovery_code_correct: 'Der eingegebene Wiederherstellungscode ist richtig!' -recovery_change_password: 'Benutze bitte den Befehl /email setpassword um dein Passwort umgehend zu ändern.' -vb_nonActiv: '&cDein Account wurde noch nicht aktiviert. Bitte prüfe deine E-Mails!' -usage_unreg: '&cBenutze: /unregister ' -pwd_changed: '&2Passwort geändert!' -logged_in: '&cBereits eingeloggt!' -logout: '&2Erfolgreich ausgeloggt' -reload: '&2Konfiguration und Datenbank wurden erfolgreich neu geladen.' -usage_changepassword: '&cBenutze: /changepassword ' +misc: + account_not_activated: '&cDein Account wurde noch nicht aktiviert. Bitte prüfe deine E-Mails!' + password_changed: '&2Passwort geändert!' + logout: '&2Erfolgreich ausgeloggt' + reload: '&2Konfiguration und Datenbank wurden erfolgreich neu geladen.' + usage_change_password: '&cBenutze: /changepassword ' + two_factor_create: '&2Dein geheimer Code ist %code. Du kannst ihn hier abfragen: %url' + accounts_owned_self: 'Du besitzt %count Accounts:' + accounts_owned_other: 'Der Spieler %name hat %count Accounts:' # Session messages -invalid_session: '&cUngültige Session. Bitte starte das Spiel neu oder warte, bis die Session abgelaufen ist.' -valid_session: '&2Erfolgreich eingeloggt!' +session: + valid_session: '&2Erfolgreich eingeloggt!' + invalid_session: '&cUngültige Session. Bitte starte das Spiel neu oder warte, bis die Session abgelaufen ist.' # Error messages when joining -name_len: '&4Dein Nickname ist zu kurz oder zu lang.' -regex: '&4Dein Nickname enthält unerlaubte Zeichen. Zulässige Zeichen: REG_EX' -country_banned: '&4Dein Land ist gesperrt!' -not_owner_error: 'Du bist nicht der Besitzer dieses Accounts. Bitte wähle einen anderen Namen!' -kick_fullserver: '&4Der Server ist momentan voll, Sorry!' -same_nick: '&4Jemand mit diesem Namen spielt bereits auf dem Server!' -invalid_name_case: 'Dein registrierter Benutzername ist &2%valid&f - nicht &4%invalid&f.' -same_ip_online: 'Ein Spieler mit derselben IP ist bereits online!' +on_join_validation: + same_ip_online: 'Ein Spieler mit derselben IP ist bereits online!' + same_nick_online: '&4Jemand mit diesem Namen spielt bereits auf dem Server!' + name_length: '&4Dein Nickname ist zu kurz oder zu lang.' + characters_in_name: '&4Dein Nickname enthält unerlaubte Zeichen. Zulässige Zeichen: %valid_chars' + kick_full_server: '&4Der Server ist momentan voll, Sorry!' + country_banned: '&4Dein Land ist gesperrt!' + not_owner_error: 'Du bist nicht der Besitzer dieses Accounts. Bitte wähle einen anderen Namen!' + invalid_name_case: 'Dein registrierter Benutzername ist &2%valid&f - nicht &4%invalid&f.' # Email -usage_email_add: '&cBenutze: /email add ' -usage_email_change: '&cBenutze: /email change ' -usage_email_recovery: '&cBenutze: /email recovery ' -new_email_invalid: '&cDie neue E-Mail ist ungültig!' -old_email_invalid: '&cDie alte E-Mail ist ungültig!' -email_invalid: '&cUngültige E-Mail!' -email_added: '&2E-Mail hinzugefügt!' -email_confirm: '&cBitte bestätige deine E-Mail!' -email_changed: '&2E-Mail aktualisiert!' -email_send: '&2Wiederherstellungs-E-Mail wurde gesendet!' -email_show: '&2Deine aktuelle E-Mail-Adresse ist: &f%email' -incomplete_email_settings: 'Fehler: Es wurden nicht alle notwendigen Einstellungen vorgenommen, um E-Mails zu senden. Bitte kontaktiere einen Administrator.' -email_already_used: '&4Diese E-Mail-Adresse wird bereits genutzt.' -email_send_failure: 'Die E-Mail konnte nicht gesendet werden. Bitte kontaktiere einen Administrator.' -show_no_email: '&2Du hast zur Zeit keine E-Mail-Adresse für deinen Account hinterlegt.' -add_email: '&3Bitte hinterlege deine E-Mail-Adresse: /email add ' -recovery_email: '&3Passwort vergessen? Nutze "/email recovery " für ein neues Passwort' -change_password_expired: 'Mit diesem Befehl kannst du dein Passwort nicht mehr ändern.' -email_cooldown_error: '&cEine E-Mail wurde erst kürzlich versendet. Du musst %time warten, bevor du eine neue anfordern kannst.' +email: + add_email_request: '&3Bitte hinterlege deine E-Mail-Adresse: /email add ' + usage_email_add: '&cBenutze: /email add ' + usage_email_change: '&cBenutze: /email change ' + new_email_invalid: '&cDie neue E-Mail ist ungültig!' + old_email_invalid: '&cDie alte E-Mail ist ungültig!' + invalid: '&cUngültige E-Mail!' + added: '&2E-Mail hinzugefügt!' + request_confirmation: '&cBitte bestätige deine E-Mail!' + changed: '&2E-Mail aktualisiert!' + email_show: '&2Deine aktuelle E-Mail-Adresse ist: &f%email' + no_email_for_account: '&2Du hast zur Zeit keine E-Mail-Adresse für deinen Account hinterlegt.' + already_used: '&4Diese E-Mail-Adresse wird bereits genutzt.' + incomplete_settings: 'Fehler: Es wurden nicht alle notwendigen Einstellungen vorgenommen, um E-Mails zu senden. Bitte kontaktiere einen Administrator.' + send_failure: 'Die E-Mail konnte nicht gesendet werden. Bitte kontaktiere einen Administrator.' + change_password_expired: 'Mit diesem Befehl kannst du dein Passwort nicht mehr ändern.' + email_cooldown_error: '&cEine E-Mail wurde erst kürzlich versendet. Du musst %time warten, bevor du eine neue anfordern kannst.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Passwort vergessen? Nutze "/email recovery " für ein neues Passwort' + command_usage: '&cBenutze: /email recovery ' + email_sent: '&2Wiederherstellungs-E-Mail wurde gesendet!' + code: + code_sent: 'Ein Wiederherstellungscode zum Zurücksetzen deines Passworts wurde an deine E-Mail-Adresse geschickt.' + incorrect: 'Der Wiederherstellungscode stimmt nicht! Du hast noch %count Versuche. Nutze /email recovery [email] um einen neuen zu generieren.' + tries_exceeded: 'Du hast die maximale Anzahl an Versuchen zur Eingabe des Wiederherstellungscodes überschritten. Benutze "/email recovery [email]" um einen neuen zu generieren.' + correct: 'Der eingegebene Wiederherstellungscode ist richtig!' + change_password: 'Benutze bitte den Befehl /email setpassword um dein Passwort umgehend zu ändern.' # Captcha -usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha ' -wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha THE_CAPTCHA' -valid_captcha: '&2Das Captcha ist korrekt!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha %captcha_code' + wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha %captcha_code' + valid_captcha: '&2Das Captcha ist korrekt!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'Sekunde' -seconds: 'Sekunden' -minute: 'Minute' -minutes: 'Minuten' -hour: 'Stunde' -hours: 'Stunden' -day: 'Tag' -days: 'Tage' +time: + second: 'Sekunde' + seconds: 'Sekunden' + minute: 'Minute' + minutes: 'Minuten' + hour: 'Stunde' + hours: 'Stunden' + day: 'Tag' + days: 'Tage' diff --git a/src/main/resources/messages/messages_eo.yml b/src/main/resources/messages/messages_eo.yml index da578175d..0d71447d3 100644 --- a/src/main/resources/messages/messages_eo.yml +++ b/src/main/resources/messages/messages_eo.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&3Bonvolu registri al la servilo per la komando: /register ' -usage_reg: '&cUzado: /register ' -reg_only: '&4Nur registritaj uzantoj povas aliĝi la servilon! Bonvolu viziti http://example.com registri vin mem!' -kicked_admin_registered: 'Administranto ĵus registrita vin; bonvolu ensaluti denove' -registered: '&2Sukcese registris!' -reg_disabled: '&cEn-ludo registriĝo estas malebligita!' -user_regged: '&cVi jam registris tiun uzantnomon!' +registration: + disabled: '&cEn-ludo registriĝo estas malebligita!' + name_taken: '&cVi jam registris tiun uzantnomon!' + register_request: '&3Bonvolu registri al la servilo per la komando: /register ' + command_usage: '&cUzado: /register ' + reg_only: '&4Nur registritaj uzantoj povas aliĝi la servilon! Bonvolu viziti http://example.com registri vin mem!' + success: '&2Sukcese registris!' + kicked_admin_registered: 'Administranto ĵus registrita vin; bonvolu ensaluti denove' # Password errors on registration -password_error: '&cLa pasvortoj ne kongruas, kontrolu ilin!' -password_error_nick: '&cVi ne povas uzi vian nomon kiel pasvorto, bonvolu elekti alian...' -password_error_unsafe: '&cLa elektita pasvorto estas danĝere, bonvolu elekti alian...' -password_error_chars: '&4Via pasvorto enhavas kontraŭleĝan karakteroj. Permesita signoj: REG_EX' -pass_len: '&cVia pasvorto estas tro mallonga aŭ tro longa! Bonvolu provi alian pasvorton!' +password: + match_error: '&cLa pasvortoj ne kongruas, kontrolu ilin!' + name_in_password: '&cVi ne povas uzi vian nomon kiel pasvorto, bonvolu elekti alian...' + unsafe_password: '&cLa elektita pasvorto estas danĝere, bonvolu elekti alian...' + forbidden_characters: '&4Via pasvorto enhavas kontraŭleĝan karakteroj. Permesita signoj: %valid_chars' + wrong_length: '&cVia pasvorto estas tro mallonga aŭ tro longa! Bonvolu provi alian pasvorton!' # Login -usage_log: '&cUzado: /login ' -wrong_pwd: '&cErara pasvorto!' -login: '&2Sukcesa ensaluto!' -login_msg: '&cBonvolu ensaluti per la komando: /login ' -timeout: '&4Salutnomo tempolimo superis, vi estis piedbatita el la servilo, bonvolu provi denove!' +login: + command_usage: '&cUzado: /login ' + wrong_password: '&cErara pasvorto!' + success: '' + login_request: '&cBonvolu ensaluti per la komando: /login ' + timeout_error: '&4Salutnomo tempolimo superis, vi estis piedbatita el la servilo, bonvolu provi denove!' # Errors -unknown_user: '&cTiu uzanto ne estas registrita!' -denied_command: '&cPor uzi ĉi komando vi devas esti legalizita!' -denied_chat: '&cPor babili vi devas esti legalizita!' -not_logged_in: '&cVi ne estas ensalutita!' -tempban_max_logins: '&cVi estis portempe malpermesita por ne ensaluti tro multajn fojojn.' -max_reg: 'Vi superis la maksimuman nombron de enregistroj (%reg_count/%max_acc %reg_names) pro via ligo!' -no_perm: '&4Vi ne havas la permeson por fari ĉi tiun funkcion!' -error: '&4Neatendita eraro, bonvolu kontakti administranto!' -kick_forvip: '&3VIP ludanto aliĝis al la servilo kiam ĝi pleniĝis!' +error: + denied_command: '&cPor uzi ĉi komando vi devas esti legalizita!' + denied_chat: '&cPor babili vi devas esti legalizita!' + unregistered_user: '&cTiu uzanto ne estas registrita!' + not_logged_in: '&cVi ne estas ensalutita!' + no_permission: '&4Vi ne havas la permeson por fari ĉi tiun funkcion!' + unexpected_error: '' + max_registration: 'Vi superis la maksimuman nombron de enregistroj (%reg_count/%max_acc %reg_names) pro via ligo!' + logged_in: '&cVi jam estas ensalutinta!' + kick_for_vip: '&3VIP ludanto aliĝis al la servilo kiam ĝi pleniĝis!' + tempban_max_logins: '&cVi estis portempe malpermesita por ne ensaluti tro multajn fojojn.' # AntiBot -kick_antibot: 'KontraŭRoboto protekto modon estas ŝaltita! Vi devas atendi kelkajn minutojn antaŭ kunigi al la servilo.' -antibot_auto_enabled: '&4[KontraŭRobotoServo] KontraŭRoboto ŝaltita pro la grandega nombro de konektoj!' -antibot_auto_disabled: '&2[KontraŭRobotoServo] KontraŭRoboto malebligita post %m minutoj!' +antibot: + kick_antibot: 'KontraŭRoboto protekto modon estas ŝaltita! Vi devas atendi kelkajn minutojn antaŭ kunigi al la servilo.' + auto_enabled: '&4[KontraŭRobotoServo] KontraŭRoboto ŝaltita pro la grandega nombro de konektoj!' + auto_disabled: '&2[KontraŭRobotoServo] KontraŭRoboto malebligita post %m minutoj!' + +# Unregister +unregister: + success: '&cSukcese neregistritajn!' + command_usage: '&cUzado: /unregister ' # Other messages -unregistered: '&cSukcese neregistritajn!' -accounts_owned_self: 'Vi posedas %count kontoj:' -accounts_owned_other: 'La ludanto %name havas %count kontojn::' -two_factor_create: '&2Via sekreta kodo estas %code. Vi povas skani ĝin de tie %url' -recovery_code_sent: 'Rekorda kodo restarigi vian pasvorton sendis al via retpoŝto.' -recovery_code_incorrect: 'La reakiro kodo estas ne korekti! Vi havas %count provoj restas.' -recovery_tries_exceeded: 'Vi superis la maksimuman nombron provas eniri la reakiro kodo. Uzo "/email recovery [retpoŝto]" por generi novan.' -recovery_code_correct: 'Reakiro kodo eniris ĝuste!' -recovery_change_password: 'Bonvolu uzi la komando /email setpassword ŝanĝi vian pasvorton tuj.' -vb_nonActiv: '&cVia konto ne aktivigis tamen, bonvolu kontroli viajn retpoŝtojn!' -usage_unreg: '&cUzado: /unregister ' -pwd_changed: '&2Pasvorto sukcese ŝanĝita!' -logged_in: '&cVi jam estas ensalutinta!' -logout: '&2Elsalutita sukcese!' -reload: '&2Agordo kaj datumbazo estis larditaj korekte!' -usage_changepassword: '&cUzado: /changepassword ' +misc: + account_not_activated: '&cVia konto ne aktivigis tamen, bonvolu kontroli viajn retpoŝtojn!' + password_changed: '&2Pasvorto sukcese ŝanĝita!' + logout: '&2Elsalutita sukcese!' + reload: '&2Agordo kaj datumbazo estis larditaj korekte!' + usage_change_password: '&cUzado: /changepassword ' + two_factor_create: '&2Via sekreta kodo estas %code. Vi povas skani ĝin de tie %url' + accounts_owned_self: 'Vi posedas %count kontoj:' + accounts_owned_other: 'La ludanto %name havas %count kontojn::' # Session messages -invalid_session: '&cVia IP estis ŝanĝita kaj via seanco datumoj finiĝis!' -valid_session: '&2Ensalutantojn pro Sesio Rekonektas.' +session: + valid_session: '&2Ensalutantojn pro Sesio Rekonektas.' + invalid_session: '&cVia IP estis ŝanĝita kaj via seanco datumoj finiĝis!' # Error messages when joining -name_len: '&4Via uzantnomo estas aŭ tro mallonga aŭ tro longa!' -regex: '&4Via uzantnomo enhavas kontraŭleĝan karakteroj. Permesita signoj: REG_EX' -country_banned: '&4Via lando estas malpermesitaj de tiu servilo!' -not_owner_error: 'Vi ne estas la posedanto de tiu konto. Bonvolu elekti alian nomon!' -kick_fullserver: '&4La servilo estas plena, reprovi poste!' -same_nick: '&4La sama uzantnomo estas jam ludas sur la servilo!' -invalid_name_case: 'Vi devus aliĝi uzante uzantnomon %valid, ne %invalid.' -same_ip_online: 'Ludanto kun la sama IP jam estas en ludo!' +on_join_validation: + same_ip_online: 'Ludanto kun la sama IP jam estas en ludo!' + same_nick_online: '&4La sama uzantnomo estas jam ludas sur la servilo!' + name_length: '&4Via uzantnomo estas aŭ tro mallonga aŭ tro longa!' + characters_in_name: '&4Via uzantnomo enhavas kontraŭleĝan karakteroj. Permesita signoj: %valid_chars' + kick_full_server: '&4La servilo estas plena, reprovi poste!' + country_banned: '&4Via lando estas malpermesitaj de tiu servilo!' + not_owner_error: 'Vi ne estas la posedanto de tiu konto. Bonvolu elekti alian nomon!' + invalid_name_case: 'Vi devus aliĝi uzante uzantnomon %valid, ne %invalid.' # Email -usage_email_add: '&cUzado: /email add ' -usage_email_change: '&cUzado: /email change ' -usage_email_recovery: '&cUzado: /email recovery ' -new_email_invalid: '&cNevalida nova retpoŝta, provu denove!' -old_email_invalid: '&cNevalida malnovaj retpoŝto, provu denove!' -email_invalid: '&cNevalida retadreso, provu denove!' -email_added: '&2Retpoŝtadreso sukcese aldonitaj al via konto!' -email_confirm: '&cBonvolu konfirmi vian retadreson!' -email_changed: '&2Retpoŝtadreso ŝanĝis ĝuste!' -email_send: '&2Reakiro retpoŝto sendita sukcese! Bonvolu kontroli vian retpoŝton enirkesto!' -email_show: '&2Via nuna retadreso estas: &f%email' -incomplete_email_settings: 'Eraro: ne ĉiuj postulata agordoj estas metita por sendi retpoŝtojn. Bonvolu kontakti administranto.' -email_already_used: '&4La retpoŝto jam estas uzata' -email_send_failure: 'La retpoŝto ne estis sendita. Bonvolu kontakti administranto.' -show_no_email: '&2Vi aktuale ne havas retadreson asociita kun ĉi tiu konto.' -add_email: '&3Bonvolu aldoni vian retpoŝtan vian konton kun la komando: /email add ' -recovery_email: '&3Ĉu vi forgesis vian pasvorton? Bonvolu uzi la komando: /email recovery ' -change_password_expired: 'Vi ne povas ŝanĝi vian pasvorton per tiu ĉi komando plu.' -email_cooldown_error: '&cRetmesaĝon jam sendita lastatempe. Vi devas atendi %time antaŭ vi povas sendi novan.' +email: + add_email_request: '&3Bonvolu aldoni vian retpoŝtan vian konton kun la komando: /email add ' + usage_email_add: '&cUzado: /email add ' + usage_email_change: '&cUzado: /email change ' + new_email_invalid: '&cNevalida nova retpoŝta, provu denove!' + old_email_invalid: '&cNevalida malnovaj retpoŝto, provu denove!' + invalid: '&cNevalida retadreso, provu denove!' + added: '&2Retpoŝtadreso sukcese aldonitaj al via konto!' + request_confirmation: '&cBonvolu konfirmi vian retadreson!' + changed: '&2Retpoŝtadreso ŝanĝis ĝuste!' + email_show: '&2Via nuna retadreso estas: &f%email' + no_email_for_account: '&2Vi aktuale ne havas retadreson asociita kun ĉi tiu konto.' + already_used: '&4La retpoŝto jam estas uzata' + incomplete_settings: 'Eraro: ne ĉiuj postulata agordoj estas metita por sendi retpoŝtojn. Bonvolu kontakti administranto.' + send_failure: 'La retpoŝto ne estis sendita. Bonvolu kontakti administranto.' + change_password_expired: 'Vi ne povas ŝanĝi vian pasvorton per tiu ĉi komando plu.' + email_cooldown_error: '&cRetmesaĝon jam sendita lastatempe. Vi devas atendi %time antaŭ vi povas sendi novan.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Ĉu vi forgesis vian pasvorton? Bonvolu uzi la komando: /email recovery ' + command_usage: '&cUzado: /email recovery ' + email_sent: '&2Reakiro retpoŝto sendita sukcese! Bonvolu kontroli vian retpoŝton enirkesto!' + code: + code_sent: 'Rekorda kodo restarigi vian pasvorton sendis al via retpoŝto.' + incorrect: 'La reakiro kodo estas ne korekti! Vi havas %count provoj restas.' + tries_exceeded: 'Vi superis la maksimuman nombron provas eniri la reakiro kodo. Uzo "/email recovery [retpoŝto]" por generi novan.' + correct: 'Reakiro kodo eniris ĝuste!' + change_password: 'Bonvolu uzi la komando /email setpassword ŝanĝi vian pasvorton tuj.' # Captcha -usage_captcha: '&3Ensaluti vi devas solvi captcha kodo, bonvolu uzi la komando: /captcha ' -wrong_captcha: '&cMalĝusta captcha, bonvolu tajpi "/captcha THE_CAPTCHA" en la babilejo!' -valid_captcha: '&2Captcha kodo solvita ĝuste!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Ensaluti vi devas solvi captcha kodo, bonvolu uzi la komando: /captcha %captcha_code' + wrong_captcha: '&cMalĝusta captcha, bonvolu tajpi "/captcha %captcha_code" en la babilejo!' + valid_captcha: '&2Captcha kodo solvita ĝuste!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'sekundo' -seconds: 'sekundoj' -minute: 'minuto' -minutes: 'minutoj' -hour: 'horo' -hours: 'horoj' -day: 'tago' -days: 'tagoj' +time: + second: 'sekundo' + seconds: 'sekundoj' + minute: 'minuto' + minutes: 'minutoj' + hour: 'horo' + hours: 'horoj' + day: 'tago' + days: 'tagoj' diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index dd29ab8bc..98c417f1f 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -1,121 +1,138 @@ # This file must be in ANSI if win, or UTF-8 if linux. # Registration -reg_msg: '&cPor favor, regístrate con "/register ' -usage_reg: '&cUso: /register Contraseña ConfirmarContraseña' -reg_only: '&f¡Sólo para jugadores registrados! Por favor visita http://www.example.com/ para registrarte' -kicked_admin_registered: 'Un administrador te acaba de registrar; entra en la cuenta de nuevo' -registered: '&c¡Registrado correctamente!' -reg_disabled: '&cEl registro está desactivado' -user_regged: '&cUsuario ya registrado' +registration: + disabled: '&cEl registro está desactivado' + name_taken: '&cUsuario ya registrado' + register_request: '&cPor favor, regístrate con "/register ' + command_usage: '&cUso: /register Contraseña ConfirmarContraseña' + reg_only: '&f¡Sólo para jugadores registrados! Por favor visita http://www.example.com/ para registrarte' + success: '&c¡Registrado correctamente!' + kicked_admin_registered: 'Un administrador te acaba de registrar; entra en la cuenta de nuevo' # Password errors on registration -password_error: '&fLas contraseñas no son iguales' -password_error_nick: '&cNo puedes utilizar tu nombre como contraseña, por favor elige otra...' -password_error_unsafe: '&cLa contraseña elejida no es segura, por favor elige otra...' -password_error_chars: '&cTu contraseña tiene carácteres no admitidos, los cuales son: REG_EX' -pass_len: '&fTu contraseña es muy larga o muy corta' +password: + match_error: '&fLas contraseñas no son iguales' + name_in_password: '&cNo puedes utilizar tu nombre como contraseña, por favor elige otra...' + unsafe_password: '&cLa contraseña elejida no es segura, por favor elige otra...' + forbidden_characters: '&cTu contraseña tiene carácteres no admitidos, los cuales son: %valid_chars' + wrong_length: '&fTu contraseña es muy larga o muy corta' # Login -usage_log: '&cUso: /login contraseña' -wrong_pwd: '&cContraseña incorrecta' -login: '&c¡Sesión iniciada!' -login_msg: '&cInicia sesión con "/login contraseña"' -timeout: '&fTiempo de espera para inicio de sesión excedido' +login: + command_usage: '&cUso: /login contraseña' + wrong_password: '&cContraseña incorrecta' + success: '' + login_request: '&cInicia sesión con "/login contraseña"' + timeout_error: '&fTiempo de espera para inicio de sesión excedido' # Errors -unknown_user: '&cUsuario no registrado' -denied_command: '&c¡Para utilizar este comando debes iniciar sesión!' -denied_chat: '&c¡Para poder hablar en el chat debes iniciar sesión!' -not_logged_in: '&c¡No has iniciado sesión!' -tempban_max_logins: '&cHas sido expulsado temporalmente por intentar iniciar sesión demasiadas veces.' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&fHas excedido la cantidad máxima de registros para tu cuenta' -no_perm: '&cNo tienes permiso' -error: '&fHa ocurrido un error. Por favor contacta al administrador.' -kick_forvip: '&c¡Un jugador VIP ha ingresado al servidor lleno!' +error: + denied_command: '&c¡Para utilizar este comando debes iniciar sesión!' + denied_chat: '&c¡Para poder hablar en el chat debes iniciar sesión!' + unregistered_user: '&cUsuario no registrado' + not_logged_in: '&c¡No has iniciado sesión!' + no_permission: '&cNo tienes permiso' + unexpected_error: '' + max_registration: '&fHas excedido la cantidad máxima de registros para tu cuenta' + logged_in: '&c¡Ya has iniciado sesión!' + kick_for_vip: '&c¡Un jugador VIP ha ingresado al servidor lleno!' + tempban_max_logins: '&cHas sido expulsado temporalmente por intentar iniciar sesión demasiadas veces.' # AntiBot -kick_antibot: '¡El modo de protección AntiBot está habilitado! Tienes que esperar varios minutos antes de entrar en el servidor.' -antibot_auto_enabled: '[AuthMe] AntiBotMod activado automáticamente debido a conexiones masivas!' -antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automáticamente después de %m minutos. Esperamos que haya terminado' +antibot: + kick_antibot: '¡El modo de protección AntiBot está habilitado! Tienes que esperar varios minutos antes de entrar en el servidor.' + auto_enabled: '[AuthMe] AntiBotMod activado automáticamente debido a conexiones masivas!' + auto_disabled: '[AuthMe] AntiBotMod desactivado automáticamente después de %m minutos. Esperamos que haya terminado' + +# Unregister +unregister: + success: '&c¡Cuenta eliminada del registro!' + command_usage: '&cUso: /unregister contraseña' # Other messages -unregistered: '&c¡Cuenta eliminada del registro!' -accounts_owned_self: 'Eres propietario de %count cuentas:' -accounts_owned_other: 'El jugador %name tiene %count cuentas:' -two_factor_create: '&2Tu código secreto es %code. Lo puedes escanear desde aquí %url' -recovery_code_sent: 'El código de recuperación para recuperar tu contraseña se ha enviado a tu correo.' -# TODO: Missing tags %count -recovery_code_incorrect: '¡El código de recuperación no es correcto! Usa "/email recovery [email]" para generar uno nuevo' -recovery_tries_exceeded: 'Has excedido el número máximo de intentos para introducir el código de recuperación. Escribe "/email recovery [tuEmail]" para generar uno nuevo.' -recovery_code_correct: '¡Código de recuperación introducido correctamente!' -recovery_change_password: 'Por favor usa el comando "/email setpassword " para cambiar tu contraseña inmediatamente.' -vb_nonActiv: '&fTu cuenta no está activada aún, ¡revisa tu correo!' -usage_unreg: '&cUso: /unregister contraseña' -pwd_changed: '&c¡Contraseña cambiada!' -logged_in: '&c¡Ya has iniciado sesión!' -logout: '&cDesconectado correctamente.' -reload: '&fLa configuración y la base de datos han sido recargados' -usage_changepassword: '&fUso: /changepw contraseñaActual contraseñaNueva' +misc: + account_not_activated: '&fTu cuenta no está activada aún, ¡revisa tu correo!' + password_changed: '&c¡Contraseña cambiada!' + logout: '&cDesconectado correctamente.' + reload: '&fLa configuración y la base de datos han sido recargados' + usage_change_password: '&fUso: /changepw contraseñaActual contraseñaNueva' + two_factor_create: '&2Tu código secreto es %code. Lo puedes escanear desde aquí %url' + accounts_owned_self: 'Eres propietario de %count cuentas:' + accounts_owned_other: 'El jugador %name tiene %count cuentas:' # Session messages -invalid_session: '&fLos datos de sesión no corresponden. Por favor espera a terminar la sesión.' -valid_session: '&cInicio de sesión' +session: + valid_session: '&cInicio de sesión' + invalid_session: '&fLos datos de sesión no corresponden. Por favor espera a terminar la sesión.' # Error messages when joining -name_len: '&cTu nombre de usuario es muy largo o muy corto.' -regex: '&cTu usuario tiene carácteres no admitidos, los cuales son: REG_EX' -country_banned: '¡Tu país ha sido baneado de este servidor!' -not_owner_error: 'No eres el propietario de esta cuenta. ¡Por favor, elije otro nombre!' -kick_fullserver: '&c¡El servidor está lleno, lo sentimos!' -same_nick: '&fYa hay un usuario con ese nick conectado (posible error)' -invalid_name_case: 'Solo puedes unirte mediante el nombre de usuario %valid, no %invalid.' -same_ip_online: '¡Un jugador con la misma IP ya está en el juego!' +on_join_validation: + same_ip_online: '¡Un jugador con la misma IP ya está en el juego!' + same_nick_online: '&fYa hay un usuario con ese nick conectado (posible error)' + name_length: '&cTu nombre de usuario es muy largo o muy corto.' + characters_in_name: '&cTu usuario tiene carácteres no admitidos, los cuales son: %valid_chars' + kick_full_server: '&c¡El servidor está lleno, lo sentimos!' + country_banned: '¡Tu país ha sido baneado de este servidor!' + not_owner_error: 'No eres el propietario de esta cuenta. ¡Por favor, elije otro nombre!' + invalid_name_case: 'Solo puedes unirte mediante el nombre de usuario %valid, no %invalid.' # Email -usage_email_add: '&fUso: /email add ' -usage_email_change: '&fUso: /email change ' -usage_email_recovery: '&fUso: /email recovery ' -new_email_invalid: '[AuthMe] Nuevo email inválido!' -old_email_invalid: '[AuthMe] Email anterior inválido!' -email_invalid: '[AuthMe] Email inválido' -email_added: '[AuthMe] Email agregado !' -email_confirm: '[AuthMe] Confirma tu Email !' -email_changed: '[AuthMe] Email cambiado !' -email_send: '[AuthMe] Correo de recuperación enviado !' -email_show: '&2Tu dirección de E-Mail actual es: &f%email' -incomplete_email_settings: 'Error: no todos los ajustes necesarios se han configurado para poder enviar correos. Por favor, contacta con un administrador.' -email_already_used: '&4La dirección Email ya está siendo usada' -email_send_failure: 'No se ha podido enviar el correo electrónico. Por favor, contacta con un administrador.' -show_no_email: '&2No tienes ningun E-Mail asociado en esta cuenta.' -add_email: '&cPor favor agrega tu e-mail con: /email add tuEmail confirmarEmail' -recovery_email: '&c¿Olvidaste tu contraseña? Por favor usa /email recovery ' -change_password_expired: 'No puedes cambiar la contraseña utilizando este comando.' -email_cooldown_error: '&cEl correo ha sido enviado recientemente. Debes esperar %time antes de volver a enviar uno nuevo.' +email: + add_email_request: '&cPor favor agrega tu e-mail con: /email add tuEmail confirmarEmail' + usage_email_add: '&fUso: /email add ' + usage_email_change: '&fUso: /email change ' + new_email_invalid: '[AuthMe] Nuevo email inválido!' + old_email_invalid: '[AuthMe] Email anterior inválido!' + invalid: '[AuthMe] Email inválido' + added: '[AuthMe] Email agregado !' + request_confirmation: '[AuthMe] Confirma tu Email !' + changed: '[AuthMe] Email cambiado !' + email_show: '&2Tu dirección de E-Mail actual es: &f%email' + no_email_for_account: '&2No tienes ningun E-Mail asociado en esta cuenta.' + already_used: '&4La dirección Email ya está siendo usada' + incomplete_settings: 'Error: no todos los ajustes necesarios se han configurado para poder enviar correos. Por favor, contacta con un administrador.' + send_failure: 'No se ha podido enviar el correo electrónico. Por favor, contacta con un administrador.' + change_password_expired: 'No puedes cambiar la contraseña utilizando este comando.' + email_cooldown_error: '&cEl correo ha sido enviado recientemente. Debes esperar %time antes de volver a enviar uno nuevo.' + +# Password recovery by email +recovery: + forgot_password_hint: '&c¿Olvidaste tu contraseña? Por favor usa /email recovery ' + command_usage: '&fUso: /email recovery ' + email_sent: '[AuthMe] Correo de recuperación enviado !' + code: + code_sent: 'El código de recuperación para recuperar tu contraseña se ha enviado a tu correo.' + incorrect: '¡El código de recuperación no es correcto! Usa "/email recovery [email]" para generar uno nuevo' + tries_exceeded: 'Has excedido el número máximo de intentos para introducir el código de recuperación. Escribe "/email recovery [tuEmail]" para generar uno nuevo.' + correct: '¡Código de recuperación introducido correctamente!' + change_password: 'Por favor usa el comando "/email setpassword " para cambiar tu contraseña inmediatamente.' # Captcha -usage_captcha: '&cUso: /captcha ' -wrong_captcha: '&cCaptcha incorrecto, por favor usa: /captcha THE_CAPTCHA' -valid_captcha: '&c¡Captcha ingresado correctamente!' -captcha_for_registration: 'Para registrarse primero debes resolver el captcha, por favor usa el comando: /captcha ' -register_captcha_valid: '&2¡Captcha validado! Ahora puedes registrarte usando /register' +captcha: + usage_captcha: '&cUso: /captcha %captcha_code' + wrong_captcha: '&cCaptcha incorrecto, por favor usa: /captcha %captcha_code' + valid_captcha: '&c¡Captcha ingresado correctamente!' + captcha_for_registration: 'Para registrarse primero debes resolver el captcha, por favor usa el comando: /captcha %captcha_code' + register_captcha_valid: '&2¡Captcha validado! Ahora puedes registrarte usando /register' # Verification code -verification_code_required: '&3¡Este comando es privado y requiere una verificación por correo electrónico! Revisa tu bandeja de entrada y sigue las instrucciones del correo electrónico.' -usage_verification_code: '&cUso: /verification ' -incorrect_verification_code: '&cCódigo incorrecto, por favor escribe "/verification " en el chat, utilizando el código que has recibido por correo electrónico.' -verification_code_verified: '&2Tu identidad ha sido verificada! Ahora puedes ejecutar todos los comandos dentro de la sesión actual.' -verification_code_already_verified: '&2¡Ya puedes ejecutar todos los comandos de seguridad dentro de la sesión actual!' -verification_code_expired: '&3Tu código ha expirado! Ejecuta otra vez el comando de seguridad para obtener un nuevo código!' -verification_code_email_needed: '&3¡Para verificar tu identidad necesitas añadir tu correo electrónico a tu cuenta!' +verification: + code_required: '&3¡Este comando es privado y requiere una verificación por correo electrónico! Revisa tu bandeja de entrada y sigue las instrucciones del correo electrónico.' + command_usage: '&cUso: /verification ' + incorrect_code: '&cCódigo incorrecto, por favor escribe "/verification " en el chat, utilizando el código que has recibido por correo electrónico.' + success: '&2Tu identidad ha sido verificada! Ahora puedes ejecutar todos los comandos dentro de la sesión actual.' + already_verified: '&2¡Ya puedes ejecutar todos los comandos de seguridad dentro de la sesión actual!' + code_expired: '&3Tu código ha expirado! Ejecuta otra vez el comando de seguridad para obtener un nuevo código!' + email_needed: '&3¡Para verificar tu identidad necesitas añadir tu correo electrónico a tu cuenta!' # Time units -second: 'segundo' -seconds: 'segundos' -minute: 'minuto' -minutes: 'minutos' -hour: 'hora' -hours: 'horas' -day: 'día' -days: 'días' +time: + second: 'segundo' + seconds: 'segundos' + minute: 'minuto' + minutes: 'minutos' + hour: 'hora' + hours: 'horas' + day: 'día' + days: 'días' diff --git a/src/main/resources/messages/messages_et.yml b/src/main/resources/messages/messages_et.yml index 97ec3afd4..f0eb19163 100644 --- a/src/main/resources/messages/messages_et.yml +++ b/src/main/resources/messages/messages_et.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&3Palun registreeri käsklusega /register ' -usage_reg: '&cKasutus: /register ' -reg_only: '&4Ainult registreeritud mängijad saavad liituda! Külasta veebilehte http://example.com, et ennast regsitreerida.' -kicked_admin_registered: 'Administraator registreeris sind, palun lahku korra serverist ja liitu tagasi.' -registered: '&2Edukalt registreeritud!' -reg_disabled: '&cMängusisene registreerimine välja lülitatud!' -user_regged: '&cKasutajanimi juba registreeritud!' +registration: + disabled: '&cMängusisene registreerimine välja lülitatud!' + name_taken: '&cKasutajanimi juba registreeritud!' + register_request: '&3Palun registreeri käsklusega /register ' + command_usage: '&cKasutus: /register ' + reg_only: '&4Ainult registreeritud mängijad saavad liituda! Külasta veebilehte http://example.com, et ennast regsitreerida.' + success: '&2Edukalt registreeritud!' + kicked_admin_registered: 'Administraator registreeris sind, palun lahku korra serverist ja liitu tagasi.' # Password errors on registration -password_error: '&cParoolid ei klapi, kontrolli neid!' -password_error_nick: '&cSa ei saa kasutada oma kasutajanime paroolina, vali teine parool.' -password_error_unsafe: '&cSee parool ei ole turvaline, vali teistsugune.' -password_error_chars: '&44Su parool sisaldab keelatud karaktereid. Lubatud karakterid: REG_EX' -pass_len: '&cSu parool on liiga pikk või lühike, vali teine!' +password: + match_error: '&cParoolid ei klapi, kontrolli neid!' + name_in_password: '&cSa ei saa kasutada oma kasutajanime paroolina, vali teine parool.' + unsafe_password: '&cSee parool ei ole turvaline, vali teistsugune.' + forbidden_characters: '&44Su parool sisaldab keelatud karaktereid. Lubatud karakterid: %valid_chars' + wrong_length: '&cSu parool on liiga pikk või lühike, vali teine!' # Login -usage_log: '&cKasutus: /login ' -wrong_pwd: '&cVale salasõna!' -login: '&2Edukalt sisselogitud!' -login_msg: '&cPalun logi sisse kasutades käsklust: /login ' -timeout: '&4Sisselogimise taimer täis, palun logi sisse ja proovi uuesti!' +login: + command_usage: '&cKasutus: /login ' + wrong_password: '&cVale salasõna!' + success: '' + login_request: '&cPalun logi sisse kasutades käsklust: /login ' + timeout_error: '&4Sisselogimise taimer täis, palun logi sisse ja proovi uuesti!' # Errors -unknown_user: '&cSee kasutaja ei ole registreeritud.' -denied_command: '&cSelle käskluse kasutamiseks pead sa olema audenditud!' -denied_chat: '&cChati kasutamiseks pead sa olema audenditud!' -not_logged_in: '&cSa ei ole sisse loginud!' -tempban_max_logins: '&cSind on ajutiselt pagendatud serverist, sest sul ebaõnnestus sisse logida liiga palju kordi.' -max_reg: '&cSu IP peal (%reg_count/%max_acc %reg_names) on liiga palju kontosid!' -no_perm: '&4Sul ei ole selle käskluse kasutamiseks õigust.' -error: '&4Süsteemi viga, teata sellest serveri meeskonda.' -kick_forvip: '&3VIP kasutaja liitus serveriga, kui see oli täis. Sind visati välja, et talle ruumi teha.' +error: + denied_command: '&cSelle käskluse kasutamiseks pead sa olema audenditud!' + denied_chat: '&cChati kasutamiseks pead sa olema audenditud!' + unregistered_user: '&cSee kasutaja ei ole registreeritud.' + not_logged_in: '&cSa ei ole sisse loginud!' + no_permission: '&4Sul ei ole selle käskluse kasutamiseks õigust.' + unexpected_error: '' + max_registration: '&cSu IP peal (%reg_count/%max_acc %reg_names) on liiga palju kontosid!' + logged_in: '&cJuba sisselogitud!' + kick_for_vip: '&3VIP kasutaja liitus serveriga, kui see oli täis. Sind visati välja, et talle ruumi teha.' + tempban_max_logins: '&cSind on ajutiselt pagendatud serverist, sest sul ebaõnnestus sisse logida liiga palju kordi.' # AntiBot -kick_antibot: 'AntiBot on sisse lülitatud! Sa pead ootama mõned minutid enne kui uuesti sisse logid.' -antibot_auto_enabled: '&4[AntiBot] AntiBot sisse lülitatud suure liikluse tõttu!' -antibot_auto_disabled: '&2[AntiBot] AntiBot välja lülitatud peale %m minutit!' +antibot: + kick_antibot: 'AntiBot on sisse lülitatud! Sa pead ootama mõned minutid enne kui uuesti sisse logid.' + auto_enabled: '&4[AntiBot] AntiBot sisse lülitatud suure liikluse tõttu!' + auto_disabled: '&2[AntiBot] AntiBot välja lülitatud peale %m minutit!' + +# Unregister +unregister: + success: '&cKonto edukalt kustutatud!' + command_usage: '&cKasutus: /unregister ' # Other messages -unregistered: '&cKonto edukalt kustutatud!' -accounts_owned_self: 'Sa omad %count kontot:' -accounts_owned_other: 'Mängijal %name on %count kontot:' -two_factor_create: '&2Su salajane kood on %code. Skänni see siin: %url' -recovery_code_sent: 'Taastuskood saadeti su meilile.' -recovery_code_incorrect: 'Taastuskood ei ole õige! Sul on %count proovi järelejäänud.' -recovery_tries_exceeded: 'Sa oled liiga palju proovinud taastuskoodi sisestada. Kasuta "/email recovery [email]" et genereerida uut koodi.' -recovery_code_correct: 'Taastuskood on õige!' -recovery_change_password: 'Palun kasuta käsklust /email setpassword et vahetada oma parooli koheselt.' -vb_nonActiv: '&cSu konto pole veel aktiveeritud, vaata oma postkasti.' -usage_unreg: '&cKasutus: /unregister ' -pwd_changed: '&2Parool edukalt vahetatud!' -logged_in: '&cJuba sisselogitud!' -logout: '&2Edukalt välja logitud!!' -reload: '&2Andmebaas uuendatud!' -usage_changepassword: '&cKasutus: /changepassword ' +misc: + account_not_activated: '&cSu konto pole veel aktiveeritud, vaata oma postkasti.' + password_changed: '&2Parool edukalt vahetatud!' + logout: '&2Edukalt välja logitud!!' + reload: '&2Andmebaas uuendatud!' + usage_change_password: '&cKasutus: /changepassword ' + two_factor_create: '&2Su salajane kood on %code. Skänni see siin: %url' + accounts_owned_self: 'Sa omad %count kontot:' + accounts_owned_other: 'Mängijal %name on %count kontot:' # Session messages -invalid_session: '&cSu IP on vahetunud ja sessiooni info on aegunud!' -valid_session: '&2Sisselogitud eelneva sessiooni tõttu.' +session: + valid_session: '&2Sisselogitud eelneva sessiooni tõttu.' + invalid_session: '&cSu IP on vahetunud ja sessiooni info on aegunud!' # Error messages when joining -name_len: '&4Su kasutajanimi on liiga pikk või lühike!' -regex: '&4Su kasutajanimes on keelatud karaktereid. Lubatud karakterid: REG_EX' -country_banned: '&4Su riik on siin serveris keelatud!' -not_owner_error: 'Sa ei ole selle konto omanik. Vali teine nimi.' -kick_fullserver: '&4Server on täis, proovi hiljem uuesti.' -same_nick: '&4Sama kasutajanimi mängib juba serveris.' -invalid_name_case: 'Logi sisse kasutajanimega %valid, mitte kasutajanimega %invalid.' -same_ip_online: 'Sama IP-ga mängija on juba sees!' +on_join_validation: + same_ip_online: 'Sama IP-ga mängija on juba sees!' + same_nick_online: '&4Sama kasutajanimi mängib juba serveris.' + name_length: '&4Su kasutajanimi on liiga pikk või lühike!' + characters_in_name: '&4Su kasutajanimes on keelatud karaktereid. Lubatud karakterid: %valid_chars' + kick_full_server: '&4Server on täis, proovi hiljem uuesti.' + country_banned: '&4Su riik on siin serveris keelatud!' + not_owner_error: 'Sa ei ole selle konto omanik. Vali teine nimi.' + invalid_name_case: 'Logi sisse kasutajanimega %valid, mitte kasutajanimega %invalid.' # Email -usage_email_add: '&cKasutus: /email add ' -usage_email_change: '&cKasutus: /email change ' -usage_email_recovery: '&cKasutus: /email recovery ' -new_email_invalid: '&cVale meiliaadress, proovi uuesti.' -old_email_invalid: '&cVale vana meiliaadress, proovi uuesti.' -email_invalid: '&cVale meiliaadress, proovi uuesti.' -email_added: '&2Meiliaadress edukalt vahetatud!' -email_confirm: '&cPalun kinnita oma meiliaadress.' -email_changed: '&2Meiliaadress edukalt vahetatud.' -email_send: '&2Taastuskood edukalt saadetud su meiliaadressile. Kontrolli oma postkasti.' -email_show: '&2Su meiliaadress on: &f%email' -incomplete_email_settings: 'Viga: Meiliaadressi kinnitamiseks vajalikud seaded pole serveri poolt paigas. Kontakteeru meeskonnaga.' -email_already_used: '&4Meiliaadress juba kasutuses.' -email_send_failure: 'Meili ei õnnestunud saata. Kontakteeru meeskonnaga.' -show_no_email: '&2Selle kasutajaga pole seotud ühtegi meiliaadressi.' -add_email: '&3Lisa oma meiliaadress kasutades käsklust: /email add ' -recovery_email: '&3Unustasid salasõna? Kasuta käsklust: /email recovery ' -change_password_expired: '&3Enam ei saa vahetada oma parooli kasutades seda käsklust.' -email_cooldown_error: '&cEmail juba saadeti. Sa pead ootama %time ennem, kui saad uuesti saata.' +email: + add_email_request: '&3Lisa oma meiliaadress kasutades käsklust: /email add ' + usage_email_add: '&cKasutus: /email add ' + usage_email_change: '&cKasutus: /email change ' + new_email_invalid: '&cVale meiliaadress, proovi uuesti.' + old_email_invalid: '&cVale vana meiliaadress, proovi uuesti.' + invalid: '&cVale meiliaadress, proovi uuesti.' + added: '&2Meiliaadress edukalt vahetatud!' + request_confirmation: '&cPalun kinnita oma meiliaadress.' + changed: '&2Meiliaadress edukalt vahetatud.' + email_show: '&2Su meiliaadress on: &f%email' + no_email_for_account: '&2Selle kasutajaga pole seotud ühtegi meiliaadressi.' + already_used: '&4Meiliaadress juba kasutuses.' + incomplete_settings: 'Viga: Meiliaadressi kinnitamiseks vajalikud seaded pole serveri poolt paigas. Kontakteeru meeskonnaga.' + send_failure: 'Meili ei õnnestunud saata. Kontakteeru meeskonnaga.' + change_password_expired: '&3Enam ei saa vahetada oma parooli kasutades seda käsklust.' + email_cooldown_error: '&cEmail juba saadeti. Sa pead ootama %time ennem, kui saad uuesti saata.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Unustasid salasõna? Kasuta käsklust: /email recovery ' + command_usage: '&cKasutus: /email recovery ' + email_sent: '&2Taastuskood edukalt saadetud su meiliaadressile. Kontrolli oma postkasti.' + code: + code_sent: 'Taastuskood saadeti su meilile.' + incorrect: 'Taastuskood ei ole õige! Sul on %count proovi järelejäänud.' + tries_exceeded: 'Sa oled liiga palju proovinud taastuskoodi sisestada. Kasuta "/email recovery [email]" et genereerida uut koodi.' + correct: 'Taastuskood on õige!' + change_password: 'Palun kasuta käsklust /email setpassword et vahetada oma parooli koheselt.' # Captcha -usage_captcha: '&3Sisselogimiseks pead lahendama captcha. Tee seda kasutades käsklust: /captcha ' -wrong_captcha: '&cVale captcha, kirjuta "/captcha THE_CAPTCHA" chatti!' -valid_captcha: '&2Captcha lahendatud!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Sisselogimiseks pead lahendama captcha. Tee seda kasutades käsklust: /captcha %captcha_code' + wrong_captcha: '&cVale captcha, kirjuta "/captcha %captcha_code" chatti!' + valid_captcha: '&2Captcha lahendatud!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'sekund' -seconds: 'sekundit' -minute: 'minut' -minutes: 'minutit' -hour: 'tund' -hours: 'tundi' -day: 'päev' -days: 'päeva' +time: + second: 'sekund' + seconds: 'sekundit' + minute: 'minut' + minutes: 'minutit' + hour: 'tund' + hours: 'tundi' + day: 'päev' + days: 'päeva' diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index 86f74c7cb..e729aafcd 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&cMesedez erabili "/register pasahitza pasahitza" erregistratzeko' -usage_reg: '&cErabili: /register pasahitza pasahitza' -reg_only: '&fErregistratuako erabiltzaileak bakarrik! Mesedez bisitatu http://example.com erregistratzeko' -# TODO kicked_admin_registered: 'An admin just registered you; please log in again' -registered: '&cOndo erregistratu zara!' -reg_disabled: '&cErregistroa desgaitua' -user_regged: '&cErabiltzailea dagoeneko erregistratua' +registration: + disabled: '&cErregistroa desgaitua' + name_taken: '&cErabiltzailea dagoeneko erregistratua' + register_request: '&cMesedez erabili "/register pasahitza pasahitza" erregistratzeko' + command_usage: '&cErabili: /register pasahitza pasahitza' + reg_only: '&fErregistratuako erabiltzaileak bakarrik! Mesedez bisitatu http://example.com erregistratzeko' + success: '&cOndo erregistratu zara!' + kicked_admin_registered: '' # Password errors on registration -password_error: '&fPasahitzak ez datoz bat' -# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' -# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' -# TODO password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX' -pass_len: '&fZure pasahitza motzegia edo luzeegia da' +password: + match_error: '&fPasahitzak ez datoz bat' + name_in_password: '' + unsafe_password: '' + forbidden_characters: '' + wrong_length: '&fZure pasahitza motzegia edo luzeegia da' # Login -usage_log: '&cErabili: /login pasahitza' -wrong_pwd: '&cPasahitz okerra' -login: '&cOngi etorri!' -login_msg: '&cMesedez erabili "/login pasahitza" saioa hasteko' -timeout: '&fDenbora gehiegi egon zara saioa hasi gabe' +login: + command_usage: '&cErabili: /login pasahitza' + wrong_password: '&cPasahitz okerra' + success: '' + login_request: '&cMesedez erabili "/login pasahitza" saioa hasteko' + timeout_error: '&fDenbora gehiegi egon zara saioa hasi gabe' # Errors -unknown_user: '&cErabiltzailea ez dago erregistratuta' -# TODO denied_command: '&cIn order to use this command you must be authenticated!' -# TODO denied_chat: '&cIn order to chat you must be authenticated!' -not_logged_in: '&cSaioa hasi gabe!' -# TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&fKontuko 2 erabiltzaile bakarrik izan ditzakezu' -no_perm: '&cBaimenik ez' -error: '&fErrorea; Mesedez jarri kontaktuan administratzaile batekin' -kick_forvip: '&cVIP erabiltzaile bat sartu da zerbitzaria beteta zegoenean!' +error: + denied_command: '' + denied_chat: '' + unregistered_user: '&cErabiltzailea ez dago erregistratuta' + not_logged_in: '&cSaioa hasi gabe!' + no_permission: '&cBaimenik ez' + unexpected_error: '' + max_registration: '&fKontuko 2 erabiltzaile bakarrik izan ditzakezu' + logged_in: '&cDagoeneko saioa hasita!' + kick_for_vip: '&cVIP erabiltzaile bat sartu da zerbitzaria beteta zegoenean!' + tempban_max_logins: '' # AntiBot -# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' -# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' +antibot: + kick_antibot: '' + auto_enabled: '' + auto_disabled: '' + +# Unregister +unregister: + success: '&cZure erregistroa ezabatu duzu!' + command_usage: '&cErabili: /unregister password' # Other messages -unregistered: '&cZure erregistroa ezabatu duzu!' -# TODO accounts_owned_self: 'You own %count accounts:' -# TODO accounts_owned_other: 'The player %name has %count accounts:' -# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&fZure kontua aktibatu gabe dago, konfirmatu zure emaila!' -usage_unreg: '&cErabili: /unregister password' -pwd_changed: '&cPasahitza aldatu duzu!' -logged_in: '&cDagoeneko saioa hasita!' -logout: '&cAtera zara' -reload: '&fConfiguration and database has been reloaded' -usage_changepassword: '&fErabili: /changepassword pasahitzZaharra pasahitzBerria' +misc: + account_not_activated: '&fZure kontua aktibatu gabe dago, konfirmatu zure emaila!' + password_changed: '&cPasahitza aldatu duzu!' + logout: '&cAtera zara' + reload: '&fConfiguration and database has been reloaded' + usage_change_password: '&fErabili: /changepassword pasahitzZaharra pasahitzBerria' + two_factor_create: '' + accounts_owned_self: '' + accounts_owned_other: '' # Session messages -# TODO invalid_session: '&cYour IP has been changed and your session data has expired!' -valid_session: '&cSession login' +session: + valid_session: '&cSession login' + invalid_session: '' # Error messages when joining -name_len: '&cZure erabiltzaile izena motzegia edo luzeegia da' -regex: '&cZure erabiltzaileak karaktere debekatuak ditu. Karaktere onartuak: REG_EX' -country_banned: '[AuthMe] Zure herrialdea blokeatuta dago zerbitzari honetan' -# TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' -kick_fullserver: '&cZerbitzaria beteta dago, Barkatu!' -same_nick: '&fZure izen berdina duen erabiltzaile bat zerbitzarian jolasten dago' -# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' -# TODO same_ip_online: 'A player with the same IP is already in game!' +on_join_validation: + same_ip_online: '' + same_nick_online: '&fZure izen berdina duen erabiltzaile bat zerbitzarian jolasten dago' + name_length: '&cZure erabiltzaile izena motzegia edo luzeegia da' + characters_in_name: '&cZure erabiltzaileak karaktere debekatuak ditu. Karaktere onartuak: %valid_chars' + kick_full_server: '&cZerbitzaria beteta dago, Barkatu!' + country_banned: '[AuthMe] Zure herrialdea blokeatuta dago zerbitzari honetan' + not_owner_error: '' + invalid_name_case: '' # Email -usage_email_add: '&fErabili: /email add ' -usage_email_change: '&fErabili: /email change ' -usage_email_recovery: '&fErabili: /email recovery ' -new_email_invalid: '[AuthMe] Email okerra!' -old_email_invalid: '[AuthMe] Email zaharra okerra!' -email_invalid: '[AuthMe] Email okerrea' -email_added: '[AuthMe] Emaila gehitu duzu !' -email_confirm: '[AuthMe] Konfirmatu zure emaila !' -email_changed: '[AuthMe] Emaila aldatua!' -email_send: '[AuthMe] Berreskuratze emaila bidalita !' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' -# TODO email_already_used: '&4The email address is already being used' -# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&cMesedez gehitu zure emaila : /email add yourEmail confirmEmail' -recovery_email: '&cPasahitza ahaztu duzu? Erabili /email recovery ' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + add_email_request: '&cMesedez gehitu zure emaila : /email add yourEmail confirmEmail' + usage_email_add: '&fErabili: /email add ' + usage_email_change: '&fErabili: /email change ' + new_email_invalid: '[AuthMe] Email okerra!' + old_email_invalid: '[AuthMe] Email zaharra okerra!' + invalid: '[AuthMe] Email okerrea' + added: '[AuthMe] Emaila gehitu duzu !' + request_confirmation: '[AuthMe] Konfirmatu zure emaila !' + changed: '[AuthMe] Emaila aldatua!' + email_show: '' + no_email_for_account: '' + already_used: '' + incomplete_settings: '' + send_failure: '' + change_password_expired: '' + email_cooldown_error: '' + +# Password recovery by email +recovery: + forgot_password_hint: '&cPasahitza ahaztu duzu? Erabili /email recovery ' + command_usage: '&fErabili: /email recovery ' + email_sent: '[AuthMe] Berreskuratze emaila bidalita !' + code: + code_sent: '' + incorrect: '' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -# TODO usage_captcha: '&3To log in you have to solve a captcha code, please use the command: /captcha ' -# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' -# TODO valid_captcha: '&2Captcha code solved correctly!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '' + wrong_captcha: '' + valid_captcha: '' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -# TODO second: 'second' -# TODO seconds: 'seconds' -# TODO minute: 'minute' -# TODO minutes: 'minutes' -# TODO hour: 'hour' -# TODO hours: 'hours' -# TODO day: 'day' -# TODO days: 'days' +time: + second: '' + seconds: '' + minute: '' + minutes: '' + hour: '' + hours: '' + day: '' + days: '' diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index b91e2633b..f2f958f7c 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&cRekisteröidy palvelimellemme komennolla "/register "' -usage_reg: '&cKäyttötapa: /register ' -reg_only: '&fMene sivustolle: http://example.com rekisteröityäksesi!' -# TODO kicked_admin_registered: 'An admin just registered you; please log in again' -registered: '&cRekisteröidyit onnistuneesti!' -reg_disabled: '&cRekisteröinti on suljettu!' -user_regged: '&cPelaaja on jo rekisteröity' +registration: + disabled: '&cRekisteröinti on suljettu!' + name_taken: '&cPelaaja on jo rekisteröity' + register_request: '&cRekisteröidy palvelimellemme komennolla "/register "' + command_usage: '&cKäyttötapa: /register ' + reg_only: '&fMene sivustolle: http://example.com rekisteröityäksesi!' + success: '&cRekisteröidyit onnistuneesti!' + kicked_admin_registered: '' # Password errors on registration -password_error: '&fSalasanat ei täsmää' -# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' -# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' -# TODO password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX' -pass_len: '&fSalasanasi on liian pitkä tai lyhyt.' +password: + match_error: '&fSalasanat ei täsmää' + name_in_password: '' + unsafe_password: '' + forbidden_characters: '' + wrong_length: '&fSalasanasi on liian pitkä tai lyhyt.' # Login -usage_log: '&cKäyttötapa: /login salasana' -wrong_pwd: '&cVäärä salasana' -login: '&cKirjauduit onnistuneesti' -login_msg: '&cKirjaudu palvelimmelle komennolla "/login salasana"' -timeout: '&fKirjautumisaika meni umpeen.' +login: + command_usage: '&cKäyttötapa: /login salasana' + wrong_password: '&cVäärä salasana' + success: '' + login_request: '&cKirjaudu palvelimmelle komennolla "/login salasana"' + timeout_error: '&fKirjautumisaika meni umpeen.' # Errors -unknown_user: '&cSalasanat eivät täsmää' -# TODO denied_command: '&cIn order to use this command you must be authenticated!' -# TODO denied_chat: '&cIn order to chat you must be authenticated!' -not_logged_in: '&cEt ole kirjautunut sisään!' -# TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&fSinulla ei ole oikeuksia tehdä enempää pelaajatilejä!' -no_perm: '&cEi oikeuksia' -error: '&fVirhe: Ota yhteys palveluntarjoojaan!' -kick_forvip: '&cVIP pelaaja liittyi täyteen palvelimeen!' +error: + denied_command: '' + denied_chat: '' + unregistered_user: '&cSalasanat eivät täsmää' + not_logged_in: '&cEt ole kirjautunut sisään!' + no_permission: '&cEi oikeuksia' + unexpected_error: '' + max_registration: '&fSinulla ei ole oikeuksia tehdä enempää pelaajatilejä!' + logged_in: '&cOlet jo kirjautunut!' + kick_for_vip: '&cVIP pelaaja liittyi täyteen palvelimeen!' + tempban_max_logins: '' # AntiBot -# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' -# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' +antibot: + kick_antibot: '' + auto_enabled: '' + auto_disabled: '' + +# Unregister +unregister: + success: '&cPelaajatili poistettu onnistuneesti!' + command_usage: '&cKäyttötapa: /unregister password' # Other messages -unregistered: '&cPelaajatili poistettu onnistuneesti!' -# TODO accounts_owned_self: 'You own %count accounts:' -# TODO accounts_owned_other: 'The player %name has %count accounts:' -# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&fKäyttäjäsi ei ole vahvistettu!' -usage_unreg: '&cKäyttötapa: /unregister password' -pwd_changed: '&cSalasana vaihdettu!!' -logged_in: '&cOlet jo kirjautunut!' -logout: '&cKirjauduit ulos palvelimelta.' -reload: '&fAsetukset uudelleenladattu' -usage_changepassword: '&fKäyttötapa: /changepassword vanhaSalasana uusiSalasana' +misc: + account_not_activated: '&fKäyttäjäsi ei ole vahvistettu!' + password_changed: '&cSalasana vaihdettu!!' + logout: '&cKirjauduit ulos palvelimelta.' + reload: '&fAsetukset uudelleenladattu' + usage_change_password: '&fKäyttötapa: /changepassword vanhaSalasana uusiSalasana' + two_factor_create: '' + accounts_owned_self: '' + accounts_owned_other: '' # Session messages -invalid_session: '&fIstunto ei täsmää! Ole hyvä ja odota istunnon loppuun' -valid_session: '&cIstunto jatkettu!' +session: + valid_session: '&cIstunto jatkettu!' + invalid_session: '&fIstunto ei täsmää! Ole hyvä ja odota istunnon loppuun' # Error messages when joining -name_len: '&cPelaajanimesi on liian lyhyt tai pitkä' -regex: '&cPelaajanimesi sisältää luvattomia merkkejä. Hyväksytyt merkit: REG_EX' -# TODO country_banned: '&4Your country is banned from this server!' -# TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' -kick_fullserver: '&cPalvelin on täynnä, Yritä pian uudelleen!' -same_nick: '&COlet jo palvelimella! &COdota käyttäjän aikakatkaisua tai ota yhteyttä palveluntarjoojaan.' -# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' -# TODO same_ip_online: 'A player with the same IP is already in game!' +on_join_validation: + same_ip_online: '' + same_nick_online: '&COlet jo palvelimella! &COdota käyttäjän aikakatkaisua tai ota yhteyttä palveluntarjoojaan.' + name_length: '&cPelaajanimesi on liian lyhyt tai pitkä' + characters_in_name: '&cPelaajanimesi sisältää luvattomia merkkejä. Hyväksytyt merkit: %valid_chars' + kick_full_server: '&cPalvelin on täynnä, Yritä pian uudelleen!' + country_banned: '' + not_owner_error: '' + invalid_name_case: '' # Email -usage_email_add: '&fKäyttötapa: /email add ' -usage_email_change: '&fKäyttötapa: /email change ' -usage_email_recovery: '&fKäyttötapa: /email recovery ' -new_email_invalid: '[AuthMe] Uusi sähköposti on väärä!' -old_email_invalid: '[AuthMe] Vanha sähköposti on väärä!' -email_invalid: '[AuthMe] Väärä sähköposti' -email_added: '[AuthMe] Sähköposti lisätty!' -email_confirm: '[AuthMe] Vahvistuta sähköposti!' -email_changed: '[AuthMe] Sähköposti vaihdettu!' -email_send: '[AuthMe] Palautus sähköposti lähetetty!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' -# TODO email_already_used: '&4The email address is already being used' -# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&cLisää sähköpostisi: /email add sähköpostisi sähköpostisiUudelleen' -recovery_email: '&cUnohtuiko salasana? Käytä komentoa: /email recovery ' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + add_email_request: '&cLisää sähköpostisi: /email add sähköpostisi sähköpostisiUudelleen' + usage_email_add: '&fKäyttötapa: /email add ' + usage_email_change: '&fKäyttötapa: /email change ' + new_email_invalid: '[AuthMe] Uusi sähköposti on väärä!' + old_email_invalid: '[AuthMe] Vanha sähköposti on väärä!' + invalid: '[AuthMe] Väärä sähköposti' + added: '[AuthMe] Sähköposti lisätty!' + request_confirmation: '[AuthMe] Vahvistuta sähköposti!' + changed: '[AuthMe] Sähköposti vaihdettu!' + email_show: '' + no_email_for_account: '' + already_used: '' + incomplete_settings: '' + send_failure: '' + change_password_expired: '' + email_cooldown_error: '' + +# Password recovery by email +recovery: + forgot_password_hint: '&cUnohtuiko salasana? Käytä komentoa: /email recovery ' + command_usage: '&fKäyttötapa: /email recovery ' + email_sent: '[AuthMe] Palautus sähköposti lähetetty!' + code: + code_sent: '' + incorrect: '' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&cKäyttötapa: /captcha ' -wrong_captcha: '&cVäärä varmistus, käytä : /captcha THE_CAPTCHA' -valid_captcha: '&cSinun varmistus onnistui.!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&cKäyttötapa: /captcha %captcha_code' + wrong_captcha: '&cVäärä varmistus, käytä : /captcha %captcha_code' + valid_captcha: '&cSinun varmistus onnistui.!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -# TODO second: 'second' -# TODO seconds: 'seconds' -# TODO minute: 'minute' -# TODO minutes: 'minutes' -# TODO hour: 'hour' -# TODO hours: 'hours' -# TODO day: 'day' -# TODO days: 'days' +time: + second: '' + seconds: '' + minute: '' + minutes: '' + hour: '' + hours: '' + day: '' + days: '' diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index 7d651351b..fdfe622b7 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -3,120 +3,139 @@ # Pour afficher une apostrophe, vous devez en mettre deux consécutivement (ex: «J''ai» au lieu de «J'ai») # Pour passer à la ligne, utilisez: %nl% -# Inscription -reg_msg: '&cPour vous inscrire, utilisez "/register "' -usage_reg: '&cUsage: /register ' -reg_only: 'Seul les joueurs enregistrés sont admis!%nl%Veuillez vous rendre sur https://www.example.com pour plus d''infos.' -kicked_admin_registered: 'Un admin vient de vous inscrire, veuillez vous reconnecter.' -registered: '&aInscription effectuée !' -reg_disabled: '&cL''inscription est désactivée.' -user_regged: '&cUtilisateur déjà inscrit.' +# Registration +registration: + disabled: '&cL''inscription est désactivée.' + name_taken: '&cUtilisateur déjà inscrit.' + register_request: '&cPour vous inscrire, utilisez "/register "' + command_usage: '&cUsage: /register ' + reg_only: 'Seul les joueurs enregistrés sont admis!%nl%Veuillez vous rendre sur https://www.example.com pour plus d''infos.' + success: '&aInscription effectuée !' + kicked_admin_registered: 'Un admin vient de vous inscrire, veuillez vous reconnecter.' -# Erreurs MDP pour l'inscription -password_error: '&cLe mot de passe de confirmation ne correspond pas.' -password_error_nick: '&cVous ne pouvez pas utiliser votre pseudo comme mot de passe.' -password_error_unsafe: '&cCe mot de passe n''est pas accepté, choisissez-en un autre.' -password_error_chars: '&cVotre mot de passe contient des caractères non autorisés. Caractères permis : REG_EX' -pass_len: '&cVotre mot de passe est trop court ou trop long !' +# Password errors on registration +password: + match_error: '&cLe mot de passe de confirmation ne correspond pas.' + name_in_password: '&cVous ne pouvez pas utiliser votre pseudo comme mot de passe.' + unsafe_password: '&cCe mot de passe n''est pas accepté, choisissez-en un autre.' + forbidden_characters: '&cVotre mot de passe contient des caractères non autorisés. Caractères permis : %valid_chars' + wrong_length: '&cVotre mot de passe est trop court ou trop long !' -# Connexion -usage_log: '&cUsage: /login ' -wrong_pwd: '&cMauvais mot de passe !' -login: '&aConnexion effectuée !' -login_msg: '&cPour vous connecter, utilisez "/login "' -timeout: 'Vous avez été expulsé car vous êtes trop lent pour vous enregistrer/connecter !' +# Login +login: + command_usage: '&cUsage: /login ' + wrong_password: '&cMauvais mot de passe !' + success: '' + login_request: '&cPour vous connecter, utilisez "/login "' + timeout_error: 'Vous avez été expulsé car vous êtes trop lent pour vous enregistrer/connecter !' -# Erreurs -unknown_user: '&cUtilisateur non-inscrit.' -denied_command: '&cVous devez être connecté pour pouvoir utiliser cette commande.' -denied_chat: '&cVous devez être connecté pour pouvoir écrire dans le chat.' -not_logged_in: '&cUtilisateur non connecté !' -tempban_max_logins: '&cVous êtes temporairement banni suite à plusieurs échecs de connexions !' -max_reg: 'Vous avez atteint la limite d''inscription !%nl%&cVous avez %reg_count sur %max_acc : %reg_names' -no_perm: '&cVous n''êtes pas autorisé à utiliser cette commande.' -error: '&cUne erreur est apparue, veuillez contacter un administrateur.' -kick_forvip: 'Un joueur VIP a rejoint le serveur à votre place (serveur plein).' +# Errors +error: + denied_command: '&cVous devez être connecté pour pouvoir utiliser cette commande.' + denied_chat: '&cVous devez être connecté pour pouvoir écrire dans le chat.' + unregistered_user: '&cUtilisateur non-inscrit.' + not_logged_in: '&cUtilisateur non connecté !' + no_permission: '&cVous n''êtes pas autorisé à utiliser cette commande.' + unexpected_error: '' + max_registration: 'Vous avez atteint la limite d''inscription !%nl%&cVous avez %reg_count sur %max_acc : %reg_names' + logged_in: '&aVous êtes déjà connecté.' + kick_for_vip: 'Un joueur VIP a rejoint le serveur à votre place (serveur plein).' + tempban_max_logins: '&cVous êtes temporairement banni suite à plusieurs échecs de connexions !' # AntiBot -kick_antibot: 'L''AntiBot est activé, veuillez attendre %m minutes avant de joindre le serveur.' -antibot_auto_enabled: 'L''AntiBot a été activé automatiquement à cause de nombreuses connexions !' -antibot_auto_disabled: 'L''AntiBot a été désactivé automatiquement après %m minutes, espérons que l''invasion se soit arrêtée !' +antibot: + kick_antibot: 'L''AntiBot est activé, veuillez attendre %m minutes avant de joindre le serveur.' + auto_enabled: 'L''AntiBot a été activé automatiquement à cause de nombreuses connexions !' + auto_disabled: 'L''AntiBot a été désactivé automatiquement après %m minutes, espérons que l''invasion se soit arrêtée !' -# Autres messages -unregistered: '&aCompte supprimé !' -accounts_owned_self: 'Vous avez %count comptes:' -accounts_owned_other: 'Le joueur %name a %count comptes:' -two_factor_create: '&aVotre code secret est &2%code&a. Vous pouvez le scanner depuis &2%url' -recovery_code_sent: 'Un code de récupération a été envoyé à votre email afin de réinitialiser votre mot de passe.' -recovery_code_incorrect: '&cLe code de réinitialisation est incorrect! Il vous reste %count% essai(s).' -recovery_tries_exceeded: 'Vous avez atteint le nombre maximum d''essais pour rentrer le code.%nl%Refaites "/email recovery " pour en régénérer à nouveau.' -recovery_code_correct: '&aCode de réinitialisation correct !' -recovery_change_password: 'Veuillez faire "/email setpassword " pour changer votre mot de passe directement.' -vb_nonActiv: '&fCe compte n''est pas actif, consultez vos mails !' -usage_unreg: '&cPour supprimer votre compte, utilisez "/unregister "' -pwd_changed: '&aMot de passe changé avec succès !' -logged_in: '&aVous êtes déjà connecté.' -logout: '&cVous avez été déconnecté !' -reload: '&aAuthMe a été relancé avec succès.' -usage_changepassword: '&cPour changer de mot de passe, utilisez "/changepassword "' +# Unregister +unregister: + success: '&aCompte supprimé !' + command_usage: '&cPour supprimer votre compte, utilisez "/unregister "' -# Messages de session -invalid_session: 'Session expirée suite à un changement d''IP.' -valid_session: '&aVous avez été automatiquement connecté !' +# Other messages +misc: + account_not_activated: '&fCe compte n''est pas actif, consultez vos mails !' + password_changed: '&aMot de passe changé avec succès !' + logout: '&cVous avez été déconnecté !' + reload: '&aAuthMe a été relancé avec succès.' + usage_change_password: '&cPour changer de mot de passe, utilisez "/changepassword "' + two_factor_create: '&aVotre code secret est &2%code&a. Vous pouvez le scanner depuis &2%url' + accounts_owned_self: 'Vous avez %count comptes:' + accounts_owned_other: 'Le joueur %name a %count comptes:' -# Erreurs pour rejoindre le serveur -name_len: 'Votre pseudo est trop long ou trop court.' -regex: 'Caractères de pseudo autorisés: REG_EX' -country_banned: 'Votre pays est banni de ce serveur.' -not_owner_error: 'Vous n''êtes pas le propriétaire de ce compte. Veuillez utiliser un autre pseudo !' -kick_fullserver: '&cLe serveur est actuellement plein, désolé !' -same_nick: 'Un joueur ayant le même pseudo est déjà connecté.' -invalid_name_case: 'Veuillez vous connecter avec "%valid" et non pas avec "%invalid".' -same_ip_online: 'Un joueur avec la même adresse IP joue déjà !' +# Session messages +session: + valid_session: '&aVous avez été automatiquement connecté !' + invalid_session: 'Session expirée suite à un changement d''IP.' + +# Error messages when joining +on_join_validation: + same_ip_online: 'Un joueur avec la même adresse IP joue déjà !' + same_nick_online: 'Un joueur ayant le même pseudo est déjà connecté.' + name_length: 'Votre pseudo est trop long ou trop court.' + characters_in_name: 'Caractères de pseudo autorisés: %valid_chars' + kick_full_server: '&cLe serveur est actuellement plein, désolé !' + country_banned: 'Votre pays est banni de ce serveur.' + not_owner_error: 'Vous n''êtes pas le propriétaire de ce compte. Veuillez utiliser un autre pseudo !' + invalid_name_case: 'Veuillez vous connecter avec "%valid" et non pas avec "%invalid".' # Email -usage_email_add: '&fUsage: /email add ' -usage_email_change: '&fUsage: /email change ' -usage_email_recovery: '&fUsage: /email recovery ' -new_email_invalid: '&cNouvel email invalide !' -old_email_invalid: '&cAncien email invalide !' -email_invalid: '&cL''email inscrit est invalide !' -email_added: '&aEmail enregistré. En cas de perte de MDP, faites "/email recover "' -email_confirm: '&cLa confirmation de l''email est manquante ou éronnée.' -email_changed: '&aVotre email a été mis à jour.' -email_send: '&aMail de récupération envoyé !' -email_show: '&fL''email enregistré pour votre compte est: %email' -incomplete_email_settings: '&cErreur : Tous les paramètres requis ne sont pas présent pour l''envoi de mail, veuillez contacter un admin.' -email_already_used: '&cCet email est déjà utilisé !' -email_send_failure: '&cLe mail n''a pas pu être envoyé. Veuillez contacter un admin.' -show_no_email: '&c&oVous n''avez aucun email enregistré sur votre compte.' -add_email: '&cLiez votre email à votre compte en faisant "/email add "' -recovery_email: '&cVous avez oublié votre Mot de Passe? Utilisez "/email recovery "' -change_password_expired: 'Vous ne pouvez pas changer votre mot de passe avec cette commande.' -email_cooldown_error: '&cUn mail de récupération a déjà été envoyé récemment. Veuillez attendre %time pour le demander de nouveau.' +email: + add_email_request: '&cLiez votre email à votre compte en faisant "/email add "' + usage_email_add: '&fUsage: /email add ' + usage_email_change: '&fUsage: /email change ' + new_email_invalid: '&cNouvel email invalide !' + old_email_invalid: '&cAncien email invalide !' + invalid: '&cL''email inscrit est invalide !' + added: '&aEmail enregistré. En cas de perte de MDP, faites "/email recover "' + request_confirmation: '&cLa confirmation de l''email est manquante ou éronnée.' + changed: '&aVotre email a été mis à jour.' + email_show: '&fL''email enregistré pour votre compte est: %email' + no_email_for_account: '&c&oVous n''avez aucun email enregistré sur votre compte.' + already_used: '&cCet email est déjà utilisé !' + incomplete_settings: '&cErreur : Tous les paramètres requis ne sont pas présent pour l''envoi de mail, veuillez contacter un admin.' + send_failure: '&cLe mail n''a pas pu être envoyé. Veuillez contacter un admin.' + change_password_expired: 'Vous ne pouvez pas changer votre mot de passe avec cette commande.' + email_cooldown_error: '&cUn mail de récupération a déjà été envoyé récemment. Veuillez attendre %time pour le demander de nouveau.' + +# Password recovery by email +recovery: + forgot_password_hint: '&cVous avez oublié votre Mot de Passe? Utilisez "/email recovery "' + command_usage: '&fUsage: /email recovery ' + email_sent: '&aMail de récupération envoyé !' + code: + code_sent: 'Un code de récupération a été envoyé à votre email afin de réinitialiser votre mot de passe.' + incorrect: '&cLe code de réinitialisation est incorrect! Il vous reste %count% essai(s).' + tries_exceeded: 'Vous avez atteint le nombre maximum d''essais pour rentrer le code.%nl%Refaites "/email recovery " pour en régénérer à nouveau.' + correct: '&aCode de réinitialisation correct !' + change_password: 'Veuillez faire "/email setpassword " pour changer votre mot de passe directement.' # Captcha -usage_captcha: '&cTrop de tentatives de connexion ont échoué, faites: /captcha ' -wrong_captcha: '&cCaptcha incorrect, écrivez de nouveau: /captcha THE_CAPTCHA' -valid_captcha: '&aCaptcha validé! Vous pouvez maintenant réessayer de vous connecter.' -captcha_for_registration: 'Avant de vous inscrire veuillez rentrer un captcha en faisant "/captcha "' -register_captcha_valid: '&aCaptcha validé! Vous pouvez maintenant vous inscrire.' +captcha: + usage_captcha: '&cTrop de tentatives de connexion ont échoué, faites: /captcha %captcha_code' + wrong_captcha: '&cCaptcha incorrect, écrivez de nouveau: /captcha %captcha_code' + valid_captcha: '&aCaptcha validé! Vous pouvez maintenant réessayer de vous connecter.' + captcha_for_registration: 'Avant de vous inscrire veuillez rentrer un captcha en faisant "/captcha %captcha_code"' + register_captcha_valid: '&aCaptcha validé! Vous pouvez maintenant vous inscrire.' -# Vérification par code -verification_code_required: '&cCette commande est sensible, elle nécessite donc une confirmation par email.%nl%&cVeuillez suivre les instructions qui viennent de vous être envoyées par email.' -usage_verification_code: '&cUsage: /verification ' -incorrect_verification_code: '&cCode incorrect !%nl%&cVeuillez taper "/verification " dans le chat en utilisant le code reçu par mail.' -verification_code_verified: '&aVotre identité a bien été vérifiée !%nl%&aVous pouvez désormais utiliser la commande souhaitée durant toute la session.' -verification_code_already_verified: '&aVous êtes déjà autorisé à utiliser les commandes sensibles durant votre session actuelle.' -verification_code_expired: '&cVotre code d''identification a expiré !%nl%&cVeuillez re-exécuter une commande sensible pour recevoir un nouveau code.' -verification_code_email_needed: '&cAfin de vérifier votre identité, vous devez avoir un email lié à votre compte.%nl%&cPour cela, faites "/email add "' +# Verification code +verification: + code_required: '&cCette commande est sensible, elle nécessite donc une confirmation par email.%nl%&cVeuillez suivre les instructions qui viennent de vous être envoyées par email.' + command_usage: '&cUsage: /verification ' + incorrect_code: '&cCode incorrect !%nl%&cVeuillez taper "/verification " dans le chat en utilisant le code reçu par mail.' + success: '&aVotre identité a bien été vérifiée !%nl%&aVous pouvez désormais utiliser la commande souhaitée durant toute la session.' + already_verified: '&aVous êtes déjà autorisé à utiliser les commandes sensibles durant votre session actuelle.' + code_expired: '&cVotre code d''identification a expiré !%nl%&cVeuillez re-exécuter une commande sensible pour recevoir un nouveau code.' + email_needed: '&cAfin de vérifier votre identité, vous devez avoir un email lié à votre compte.%nl%&cPour cela, faites "/email add "' -# Unités de temps -second: 'seconde' -seconds: 'secondes' -minute: 'minute' -minutes: 'minutes' -hour: 'heure' -hours: 'heures' -day: 'jour' -days: 'jours' +# Time units +time: + second: 'seconde' + seconds: 'secondes' + minute: 'minute' + minutes: 'minutes' + hour: 'heure' + hours: 'heures' + day: 'jour' + days: 'jours' diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index 76c738004..1ae95174b 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&cPor favor, rexístrate con "/register "' -usage_reg: '&cUso: /register contrasinal confirmarContrasinal' -reg_only: '&fSó xogadores rexistrados! Por favor, visita http://example.com para rexistrarte' -# TODO kicked_admin_registered: 'An admin just registered you; please log in again' -registered: '&cRexistrado con éxito!' -reg_disabled: '&cO rexistro está deshabilitado' -user_regged: '&cEse nome de usuario xa está rexistrado' +registration: + disabled: '&cO rexistro está deshabilitado' + name_taken: '&cEse nome de usuario xa está rexistrado' + register_request: '&cPor favor, rexístrate con "/register "' + command_usage: '&cUso: /register contrasinal confirmarContrasinal' + reg_only: '&fSó xogadores rexistrados! Por favor, visita http://example.com para rexistrarte' + success: '&cRexistrado con éxito!' + kicked_admin_registered: '' # Password errors on registration -password_error: '&fO contrasinal non coincide' -# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' -# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' -# TODO password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX' -pass_len: '&fO teu contrasinal non alcanza a lonxitude mínima ou excede a lonxitude máxima' +password: + match_error: '&fO contrasinal non coincide' + name_in_password: '' + unsafe_password: '' + forbidden_characters: '' + wrong_length: '&fO teu contrasinal non alcanza a lonxitude mínima ou excede a lonxitude máxima' # Login -usage_log: '&cUso: /login ' -wrong_pwd: '&cContrasinal equivocado' -login: '&cIdentificación con éxito!' -login_msg: '&cPor favor, identifícate con "/login "' -timeout: '&fRematou o tempo da autentificación' +login: + command_usage: '&cUso: /login ' + wrong_password: '&cContrasinal equivocado' + success: '' + login_request: '&cPor favor, identifícate con "/login "' + timeout_error: '&fRematou o tempo da autentificación' # Errors -unknown_user: '&cEse nome de usuario non está rexistrado' -# TODO denied_command: '&cIn order to use this command you must be authenticated!' -# TODO denied_chat: '&cIn order to chat you must be authenticated!' -not_logged_in: '&cNon te identificaches!' -# TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&fExcediches o máximo de rexistros para a túa Conta' -no_perm: '&cNon tes o permiso' -error: '&fOcurriu un erro; contacta cun administrador' -kick_forvip: '&cUn xogador VIP uniuse ao servidor cheo!' +error: + denied_command: '' + denied_chat: '' + unregistered_user: '&cEse nome de usuario non está rexistrado' + not_logged_in: '&cNon te identificaches!' + no_permission: '&cNon tes o permiso' + unexpected_error: '' + max_registration: '&fExcediches o máximo de rexistros para a túa Conta' + logged_in: '&cXa estás identificado!' + kick_for_vip: '&cUn xogador VIP uniuse ao servidor cheo!' + tempban_max_logins: '' # AntiBot -# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -antibot_auto_enabled: '[AuthMe] AntiBotMod conectouse automáticamente debido a conexións masivas!' -antibot_auto_disabled: '[AuthMe] AntiBotMod desactivouse automáticamente despois de %m minutos, esperemos que a invasión se detivera' +antibot: + kick_antibot: '' + auto_enabled: '[AuthMe] AntiBotMod conectouse automáticamente debido a conexións masivas!' + auto_disabled: '[AuthMe] AntiBotMod desactivouse automáticamente despois de %m minutos, esperemos que a invasión se detivera' + +# Unregister +unregister: + success: '&cFeito! Xa non estás rexistrado!' + command_usage: '&cUso: /unregister ' # Other messages -unregistered: '&cFeito! Xa non estás rexistrado!' -# TODO accounts_owned_self: 'You own %count accounts:' -# TODO accounts_owned_other: 'The player %name has %count accounts:' -# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&fA túa conta aínda non está activada, comproba a túa bandexa de correo!!' -usage_unreg: '&cUso: /unregister ' -pwd_changed: '&cCambiouse o contrasinal!' -logged_in: '&cXa estás identificado!' -logout: '&cSesión pechada con éxito' -reload: '&fRecargáronse a configuración e a base de datos' -usage_changepassword: '&fUso: /changepassword ' +misc: + account_not_activated: '&fA túa conta aínda non está activada, comproba a túa bandexa de correo!!' + password_changed: '&cCambiouse o contrasinal!' + logout: '&cSesión pechada con éxito' + reload: '&fRecargáronse a configuración e a base de datos' + usage_change_password: '&fUso: /changepassword ' + two_factor_create: '' + accounts_owned_self: '' + accounts_owned_other: '' # Session messages -invalid_session: '&fOs datos de sesión non corresponden, por favor, espere a que remate a sesión' -valid_session: '&cIdentificado mediante a sesión' +session: + valid_session: '&cIdentificado mediante a sesión' + invalid_session: '&fOs datos de sesión non corresponden, por favor, espere a que remate a sesión' # Error messages when joining -name_len: '&cO teu nome é demasiado curto ou demasiado longo' -regex: '&cO teu nome contén caracteres ilegais. Caracteres permitidos: REG_EX' -country_banned: 'O teu país está bloqueado neste servidor' -# TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' -kick_fullserver: '&cO servidor está actualmente cheo, sentímolo!' -same_nick: '&fXa está xogando alguén co mesmo nome' -# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' -# TODO same_ip_online: 'A player with the same IP is already in game!' +on_join_validation: + same_ip_online: '' + same_nick_online: '&fXa está xogando alguén co mesmo nome' + name_length: '&cO teu nome é demasiado curto ou demasiado longo' + characters_in_name: '&cO teu nome contén caracteres ilegais. Caracteres permitidos: %valid_chars' + kick_full_server: '&cO servidor está actualmente cheo, sentímolo!' + country_banned: 'O teu país está bloqueado neste servidor' + not_owner_error: '' + invalid_name_case: '' # Email -usage_email_add: '&fUso: /email add ' -usage_email_change: '&fUso: /email change ' -usage_email_recovery: '&fUso: /email recovery ' -new_email_invalid: '[AuthMe] O novo correo non é válido!' -old_email_invalid: '[AuthMe] O correo vello non é válido!' -email_invalid: '[AuthMe] Correo non válido' -email_added: '[AuthMe] Correo engadido!' -email_confirm: '[AuthMe] Confirma o teu correo!' -email_changed: '[AuthMe] Cambiouse o correo!' -email_send: '[AuthMe] Enviouse o correo de confirmación!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' -# TODO email_already_used: '&4The email address is already being used' -# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&cPor favor, engade o teu correo electrónico con: /email add ' -recovery_email: '&cOlvidaches o contrasinal? Por favor, usa /email recovery ' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + add_email_request: '&cPor favor, engade o teu correo electrónico con: /email add ' + usage_email_add: '&fUso: /email add ' + usage_email_change: '&fUso: /email change ' + new_email_invalid: '[AuthMe] O novo correo non é válido!' + old_email_invalid: '[AuthMe] O correo vello non é válido!' + invalid: '[AuthMe] Correo non válido' + added: '[AuthMe] Correo engadido!' + request_confirmation: '[AuthMe] Confirma o teu correo!' + changed: '[AuthMe] Cambiouse o correo!' + email_show: '' + no_email_for_account: '' + already_used: '' + incomplete_settings: '' + send_failure: '' + change_password_expired: '' + email_cooldown_error: '' + +# Password recovery by email +recovery: + forgot_password_hint: '&cOlvidaches o contrasinal? Por favor, usa /email recovery ' + command_usage: '&fUso: /email recovery ' + email_sent: '[AuthMe] Enviouse o correo de confirmación!' + code: + code_sent: '' + incorrect: '' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&cNecesitas escribir un captcha, por favor escribe: /captcha ' -wrong_captcha: '&cCaptcha equivocado, por favor usa: /captcha THE_CAPTCHA' -valid_captcha: '&cO teu captcha é válido !' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&cNecesitas escribir un captcha, por favor escribe: /captcha %captcha_code' + wrong_captcha: '&cCaptcha equivocado, por favor usa: /captcha %captcha_code' + valid_captcha: '&cO teu captcha é válido !' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -# TODO second: 'second' -# TODO seconds: 'seconds' -# TODO minute: 'minute' -# TODO minutes: 'minutes' -# TODO hour: 'hour' -# TODO hours: 'hours' -# TODO day: 'day' -# TODO days: 'days' +time: + second: '' + seconds: '' + minute: '' + minutes: '' + hour: '' + hours: '' + day: '' + days: '' diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index f2178691d..a5a953415 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&cKérlek, regisztrálj be a következő paranccsal: "&7/register &c".' -usage_reg: '&cHasználat: "&7/register &c".' -reg_only: '&4Csak a regisztrált játékosok tudnak csatlakozni a szerverhez!' -kicked_admin_registered: 'Az adminisztrátor által regisztrálva lettél. Kérlek, lépj be újra a szerverbe!' -registered: '&aA regisztráció sikeres!' -reg_disabled: '&cA regisztráció letiltva!' -user_regged: '&cEz a játékosnév már regisztrálva van!' +registration: + disabled: '&cA regisztráció letiltva!' + name_taken: '&cEz a játékosnév már regisztrálva van!' + register_request: '&cKérlek, regisztrálj be a következő paranccsal: "&7/register &c".' + command_usage: '&cHasználat: "&7/register &c".' + reg_only: '&4Csak a regisztrált játékosok tudnak csatlakozni a szerverhez!' + success: '&aA regisztráció sikeres!' + kicked_admin_registered: 'Az adminisztrátor által regisztrálva lettél. Kérlek, lépj be újra a szerverbe!' # Password errors on registration -password_error: '&cA két jelszó nem egyezik!' -password_error_nick: '&cNem használhatod a felhasználóneved jelszónak, kérlek, válassz másikat...' -password_error_unsafe: '&cA választott jelszó nem biztonságos, kérlek, válassz másikat...' -password_error_chars: '&4A választott jelszó nem engedélyezett karaktereket tartalmaz. Engedélyezett karakterek: REG_EX' -pass_len: 'A jelszavad nem éri el a minimális hosszúságot!' +password: + match_error: '&cA két jelszó nem egyezik!' + name_in_password: '&cNem használhatod a felhasználóneved jelszónak, kérlek, válassz másikat...' + unsafe_password: '&cA választott jelszó nem biztonságos, kérlek, válassz másikat...' + forbidden_characters: '&4A választott jelszó nem engedélyezett karaktereket tartalmaz. Engedélyezett karakterek: %valid_chars' + wrong_length: 'A jelszavad nem éri el a minimális hosszúságot!' # Login -usage_log: '&cBejelentkezés: "&7/login &c".' -wrong_pwd: '&4A jelszó helytelen!' -login: '&aSikeresen beléptél!' -login_msg: '&cKérlek, jelentkezz be: "&7/login &c"!' -timeout: 'Bejelentkezési időtúllépés!' +login: + command_usage: '&cBejelentkezés: "&7/login &c".' + wrong_password: '&4A jelszó helytelen!' + success: '' + login_request: '&cKérlek, jelentkezz be: "&7/login &c"!' + timeout_error: 'Bejelentkezési időtúllépés!' # Errors -unknown_user: '&cEz a felhasználó nincs regisztrálva!' -denied_command: '&cAmíg nem vagy bejelentkezve, nem használhatod ezt a parancsot!' -denied_chat: '&cAmíg nem vagy bejelentkezve, nem használhatod a csevegőt!' -not_logged_in: '&cNem vagy bejelentkezve!' -tempban_max_logins: '&cIdeiglenesen ki lettél tiltva, mert túl sok alkalommal rontottad el a jelszavad!' -max_reg: '&cElérted a maximálisan beregisztrálható karakterek számát. (%reg_count/%max_acc %reg_names)' -no_perm: '&cNincs jogosultságod a használatára!' -error: '&cHiba lépett fel! Lépj kapcsolatba a szerver tulajával!' -kick_forvip: '&3VIP játékos csatlakozott a szerverhez!' +error: + denied_command: '&cAmíg nem vagy bejelentkezve, nem használhatod ezt a parancsot!' + denied_chat: '&cAmíg nem vagy bejelentkezve, nem használhatod a csevegőt!' + unregistered_user: '&cEz a felhasználó nincs regisztrálva!' + not_logged_in: '&cNem vagy bejelentkezve!' + no_permission: '&cNincs jogosultságod a használatára!' + unexpected_error: '' + max_registration: '&cElérted a maximálisan beregisztrálható karakterek számát. (%reg_count/%max_acc %reg_names)' + logged_in: '&cMár be vagy jelentkezve!' + kick_for_vip: '&3VIP játékos csatlakozott a szerverhez!' + tempban_max_logins: '&cIdeiglenesen ki lettél tiltva, mert túl sok alkalommal rontottad el a jelszavad!' # AntiBot -kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérlek, várj pár percet mielőtt csatlakozol.' -antibot_auto_enabled: '&4[AntiBot] Az AntiBot védelem bekapcsolt, mert a megszabott időn belül több felhasználó csatlakozott!' -antibot_auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m perc múlva!' +antibot: + kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérlek, várj pár percet mielőtt csatlakozol.' + auto_enabled: '&4[AntiBot] Az AntiBot védelem bekapcsolt, mert a megszabott időn belül több felhasználó csatlakozott!' + auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m perc múlva!' + +# Unregister +unregister: + success: '&cA regisztráció sikeresen törölve!' + command_usage: '&cHasználat: "&7/unregister &c"' # Other messages -unregistered: '&cA regisztráció sikeresen törölve!' -accounts_owned_self: '%count db regisztrációd van:' -accounts_owned_other: 'A %name nevű játékosnak, %count db regisztrációja van:' -two_factor_create: '&2A titkos kódod a következő: %code. Vagy skenneld be a következő oldalról: %url' -recovery_code_sent: 'A jelszavad visszaállításához szükséges kódot sikeresen elküldtük az email címedre!' -recovery_code_incorrect: 'A visszaállító kód helytelen volt! Még %count lehetőséged maradt.' -recovery_tries_exceeded: 'Elérted a próbálkozások maximális számát. Használd a következő parancsot: "/email recovery [email címed]" egy új generálásához.' -recovery_code_correct: 'A visszaállító kód helyes!' -recovery_change_password: 'Használd a következő parancsot: "/email setpassword <új jelszó>", hogy azonnal megváltoztasd a jelszavad.' -vb_nonActiv: '&cA felhasználód aktiválása még nem történt meg, ellenőrizd a megadott emailed!' -usage_unreg: '&cHasználat: "&7/unregister &c"' -pwd_changed: '&cA jelszó sikeresen megváltoztatva!' -logged_in: '&cMár be vagy jelentkezve!' -logout: '&cSikeresen kijelentkeztél!' -reload: 'Beállítások és az adatbázis újratöltve!' -usage_changepassword: 'Használat: "/changepassword <új jelszó>".' +misc: + account_not_activated: '&cA felhasználód aktiválása még nem történt meg, ellenőrizd a megadott emailed!' + password_changed: '&cA jelszó sikeresen megváltoztatva!' + logout: '&cSikeresen kijelentkeztél!' + reload: 'Beállítások és az adatbázis újratöltve!' + usage_change_password: 'Használat: "/changepassword <új jelszó>".' + two_factor_create: '&2A titkos kódod a következő: %code. Vagy skenneld be a következő oldalról: %url' + accounts_owned_self: '%count db regisztrációd van:' + accounts_owned_other: 'A %name nevű játékosnak, %count db regisztrációja van:' # Session messages -invalid_session: '&cAz IP címed megváltozott, ezért a visszacsatlakozási időkereted lejárt.' -valid_session: '&2A megadott időkereten belül csatlakoztál vissza, így a rendszer automatikusan beléptetett.' +session: + valid_session: '&2A megadott időkereten belül csatlakoztál vissza, így a rendszer automatikusan beléptetett.' + invalid_session: '&cAz IP címed megváltozott, ezért a visszacsatlakozási időkereted lejárt.' # Error messages when joining -name_len: '&4A felhasználóneved túl hosszú, vagy túl rövid! Kérlek, válassz másikat!' -regex: '&4A felhasználóneved nem engedélyezett karaktereket tartalmaz. Engedélyezett karakterek: REG_EX' -country_banned: '&4Az országod a tiltólistán van ezen a szerveren!' -not_owner_error: 'Ez nem a te felhasználód. Kérlek, válassz másik nevet!' -kick_fullserver: '&4A szerver megtelt, próbálj csatlakozni később!' -same_nick: 'Ezzel a játékosnévvel már játszanak a szerveren.' -invalid_name_case: '%valid a felhasználó neved nem? Akkor ne %invalid névvel próbálj feljönni.' -same_ip_online: 'Már valaki csatlakozott a szerverhez ezzel az IP címmel!' +on_join_validation: + same_ip_online: 'Már valaki csatlakozott a szerverhez ezzel az IP címmel!' + same_nick_online: 'Ezzel a játékosnévvel már játszanak a szerveren.' + name_length: '&4A felhasználóneved túl hosszú, vagy túl rövid! Kérlek, válassz másikat!' + characters_in_name: '&4A felhasználóneved nem engedélyezett karaktereket tartalmaz. Engedélyezett karakterek: %valid_chars' + kick_full_server: '&4A szerver megtelt, próbálj csatlakozni később!' + country_banned: '&4Az országod a tiltólistán van ezen a szerveren!' + not_owner_error: 'Ez nem a te felhasználód. Kérlek, válassz másik nevet!' + invalid_name_case: '%valid a felhasználó neved nem? Akkor ne %invalid névvel próbálj feljönni.' # Email -usage_email_add: '&cHasználat: "&7/email add &c".' -usage_email_change: '&cHasználat: "&7/email change <új email>&c".' -usage_email_recovery: '&cHasználat: "&7/email recovery &c".' -new_email_invalid: '&cHibás az új email cím, próbáld újra!' -old_email_invalid: '&cHibás a régi email cím, próbáld újra!' -email_invalid: '&cHibás az email cím, próbáld újra!' -email_added: '&2Az email címed rögzítése sikeresen megtörtént!' -email_confirm: '&cKérlek, ellenőrízd az email címedet!' -email_changed: '&2Az email cím cseréje sikeresen megtörtént!' -email_send: '&2A jelszó visszaállításhoz szükséges emailt elküldtük! Ellenőrizd a leveleidet!' -email_show: '&2A jelenlegi email-ed a következő: &f%email' -incomplete_email_settings: 'Hiba: nem lett beállítva az összes szükséges beállítás az email küldéshez. Vedd fel a kapcsolatot egy adminnal.' -email_already_used: '&4Ez az email cím már használatban van!' -email_send_failure: 'Nem sikerült elküldeni az emailt. Lépj kapcsolatba egy adminnal.' -show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.' -add_email: '&3Kérlek, rendeld hozzá a felhasználódhoz az email címedet "&7/email add &3".' -recovery_email: '&3Ha elfelejtetted a jelszavad, használd az "&7/email recovery &3".' -change_password_expired: 'Ezzel a paranccsal már nem módosíthatja jelszavát.' -email_cooldown_error: '&cEgy emailt már kiküldtünk. Következő email küldése előtt várnod kell: %time.' +email: + add_email_request: '&3Kérlek, rendeld hozzá a felhasználódhoz az email címedet "&7/email add &3".' + usage_email_add: '&cHasználat: "&7/email add &c".' + usage_email_change: '&cHasználat: "&7/email change <új email>&c".' + new_email_invalid: '&cHibás az új email cím, próbáld újra!' + old_email_invalid: '&cHibás a régi email cím, próbáld újra!' + invalid: '&cHibás az email cím, próbáld újra!' + added: '&2Az email címed rögzítése sikeresen megtörtént!' + request_confirmation: '&cKérlek, ellenőrízd az email címedet!' + changed: '&2Az email cím cseréje sikeresen megtörtént!' + email_show: '&2A jelenlegi email-ed a következő: &f%email' + no_email_for_account: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.' + already_used: '&4Ez az email cím már használatban van!' + incomplete_settings: 'Hiba: nem lett beállítva az összes szükséges beállítás az email küldéshez. Vedd fel a kapcsolatot egy adminnal.' + send_failure: 'Nem sikerült elküldeni az emailt. Lépj kapcsolatba egy adminnal.' + change_password_expired: 'Ezzel a paranccsal már nem módosíthatja jelszavát.' + email_cooldown_error: '&cEgy emailt már kiküldtünk. Következő email küldése előtt várnod kell: %time.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Ha elfelejtetted a jelszavad, használd az "&7/email recovery &3".' + command_usage: '&cHasználat: "&7/email recovery &c".' + email_sent: '&2A jelszó visszaállításhoz szükséges emailt elküldtük! Ellenőrizd a leveleidet!' + code: + code_sent: 'A jelszavad visszaállításához szükséges kódot sikeresen elküldtük az email címedre!' + incorrect: 'A visszaállító kód helytelen volt! Még %count lehetőséged maradt.' + tries_exceeded: 'Elérted a próbálkozások maximális számát. Használd a következő parancsot: "/email recovery [email címed]" egy új generálásához.' + correct: 'A visszaállító kód helyes!' + change_password: 'Használd a következő parancsot: "/email setpassword <új jelszó>", hogy azonnal megváltoztasd a jelszavad.' # Captcha -usage_captcha: '&3A bejelentkezéshez CAPTCHA szükséges, kérlek, használd a következő parancsot: "&7/captcha &3".' -wrong_captcha: '&cHibás CAPTCHA, kérlek, írd be a következő parancsot: "&7/captcha THE_CAPTCHA&c"!' -valid_captcha: '&2A CAPTCHA sikeresen feloldva!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3A bejelentkezéshez CAPTCHA szükséges, kérlek, használd a következő parancsot: "&7/captcha %captcha_code&3".' + wrong_captcha: '&cHibás CAPTCHA, kérlek, írd be a következő parancsot: "&7/captcha %captcha_code&c"!' + valid_captcha: '&2A CAPTCHA sikeresen feloldva!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -verification_code_required: '&3Ez a parancs érzékeny, és e-mailes igazolást igényel! Ellenőrizze a bejövő postafiókot, és kövesse az e-mail utasításait.' -usage_verification_code: '&cHasználat: /verification ' -incorrect_verification_code: '&cHelytelen kód, írd be "/verification " be a chatbe, az e-mailben kapott kód használatával' -verification_code_verified: '&2Az Ön személyazonosságát ellenőrizték! Mostantól végrehajthatja az összes parancsot az aktuális munkamenetben!' -verification_code_already_verified: '&2Már minden érzékeny parancsot végrehajthat az aktuális munkameneten belül!' -verification_code_expired: '&3A kód lejárt! Végezzen el egy másik érzékeny parancsot, hogy új kódot kapjon!' -verification_code_email_needed: '&3A személyazonosságának igazolásához e-mail címet kell csatolnia fiókjához!' +verification: + code_required: '&3Ez a parancs érzékeny, és e-mailes igazolást igényel! Ellenőrizze a bejövő postafiókot, és kövesse az e-mail utasításait.' + command_usage: '&cHasználat: /verification ' + incorrect_code: '&cHelytelen kód, írd be "/verification " be a chatbe, az e-mailben kapott kód használatával' + success: '&2Az Ön személyazonosságát ellenőrizték! Mostantól végrehajthatja az összes parancsot az aktuális munkamenetben!' + already_verified: '&2Már minden érzékeny parancsot végrehajthat az aktuális munkameneten belül!' + code_expired: '&3A kód lejárt! Végezzen el egy másik érzékeny parancsot, hogy új kódot kapjon!' + email_needed: '&3A személyazonosságának igazolásához e-mail címet kell csatolnia fiókjához!' # Time units -second: 'másodperc' -seconds: 'másodperc' -minute: 'perc' -minutes: 'perc' -hour: 'óra' -hours: 'óra' -day: 'nap' -days: 'nap' +time: + second: 'másodperc' + seconds: 'másodperc' + minute: 'perc' + minutes: 'perc' + hour: 'óra' + hours: 'óra' + day: 'nap' + days: 'nap' diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 289c563e2..4d6c641e2 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&3Silahkan mendaftar ke server menggunakan command "/register "' -# TODO usage_reg: '&cUsage: /register ' -reg_only: '&4Hanya pengguna terdaftar yg bisa bergabung! Silahkan kunjungi http://example.com untuk mendaftar!' -# TODO kicked_admin_registered: 'An admin just registered you; please log in again' -registered: '&2Register berhasil!' -reg_disabled: '&cRegister dalam game tidak diaktifkan!' -user_regged: '&cKamu telah mendaftarkan username ini!' +registration: + disabled: '&cRegister dalam game tidak diaktifkan!' + name_taken: '&cKamu telah mendaftarkan username ini!' + register_request: '&3Silahkan mendaftar ke server menggunakan command "/register "' + command_usage: '' + reg_only: '&4Hanya pengguna terdaftar yg bisa bergabung! Silahkan kunjungi http://example.com untuk mendaftar!' + success: '&2Register berhasil!' + kicked_admin_registered: '' # Password errors on registration -password_error: '&cPassword tidak cocok, silahkan periksa dan ulangi kembali!' -password_error_nick: '&cKamu tidak bisa menggunakan namamu sebagai password, silahkan coba yg lain...' -password_error_unsafe: '&cPassword yg kamu pilih tidak aman, silahkan coba yg lain...' -# TODO password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX' -pass_len: '&cPassword kamu terlalu panjang/pendek! Silahkan pilih yg lain!' +password: + match_error: '&cPassword tidak cocok, silahkan periksa dan ulangi kembali!' + name_in_password: '&cKamu tidak bisa menggunakan namamu sebagai password, silahkan coba yg lain...' + unsafe_password: '&cPassword yg kamu pilih tidak aman, silahkan coba yg lain...' + forbidden_characters: '' + wrong_length: '&cPassword kamu terlalu panjang/pendek! Silahkan pilih yg lain!' # Login -usage_log: '&cUsage: /login ' -wrong_pwd: '&cPassword salah!' -login: '&2Login berhasil!' -login_msg: '&cSilahkan login menggunakan command "/login "' -timeout: '&4Jangka waktu login telah habis, kamu di keluarkan dari server. Silahkan coba lagi!' +login: + command_usage: '&cUsage: /login ' + wrong_password: '&cPassword salah!' + success: '' + login_request: '&cSilahkan login menggunakan command "/login "' + timeout_error: '&4Jangka waktu login telah habis, kamu di keluarkan dari server. Silahkan coba lagi!' # Errors -unknown_user: '&cUser ini belum terdaftar!' -# TODO denied_command: '&cIn order to use this command you must be authenticated!' -# TODO denied_chat: '&cIn order to chat you must be authenticated!' -not_logged_in: '&cKamu belum login!' -# TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&Kamu telah mencapai batas maksimum pendaftaran di server ini!' -no_perm: '&4Kamu tidak mempunyai izin melakukan ini!' -error: '&4Terjadi kesalahan tak dikenal, silahkan hubungi Administrator!' -kick_forvip: '&3Player VIP mencoba masuk pada saat server sedang penuh!' +error: + denied_command: '' + denied_chat: '' + unregistered_user: '&cUser ini belum terdaftar!' + not_logged_in: '&cKamu belum login!' + no_permission: '&4Kamu tidak mempunyai izin melakukan ini!' + unexpected_error: '' + max_registration: '&Kamu telah mencapai batas maksimum pendaftaran di server ini!' + logged_in: '&cKamu telah login!' + kick_for_vip: '&3Player VIP mencoba masuk pada saat server sedang penuh!' + tempban_max_logins: '' # AntiBot -# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -antibot_auto_enabled: '&4[AntiBotService] AntiBot diaktifkan dikarenakan banyak koneksi yg diterima!' -antibot_auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!' +antibot: + kick_antibot: '' + auto_enabled: '&4[AntiBotService] AntiBot diaktifkan dikarenakan banyak koneksi yg diterima!' + auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!' + +# Unregister +unregister: + success: '&cUnregister berhasil!' + command_usage: '' # Other messages -unregistered: '&cUnregister berhasil!' -# TODO accounts_owned_self: 'You own %count accounts:' -# TODO accounts_owned_other: 'The player %name has %count accounts:' -# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&cAkunmu belum diaktifkan, silahkan periksa email kamu!' -# TODO usage_unreg: '&cUsage: /unregister ' -pwd_changed: '&2Berhasil mengubah password!' -logged_in: '&cKamu telah login!' -logout: '&2Berhasil logout!' -reload: '&2Konfigurasi dan database telah dimuat ulang!' -usage_changepassword: '&cUsage: /changepassword ' +misc: + account_not_activated: '&cAkunmu belum diaktifkan, silahkan periksa email kamu!' + password_changed: '&2Berhasil mengubah password!' + logout: '&2Berhasil logout!' + reload: '&2Konfigurasi dan database telah dimuat ulang!' + usage_change_password: '&cUsage: /changepassword ' + two_factor_create: '' + accounts_owned_self: '' + accounts_owned_other: '' # Session messages -invalid_session: '&cIP kamu telah berubah, dan sesi kamu telah berakhir!' -valid_session: '&2Otomatis login, karena sesi masih terhubung.' +session: + valid_session: '&2Otomatis login, karena sesi masih terhubung.' + invalid_session: '&cIP kamu telah berubah, dan sesi kamu telah berakhir!' # Error messages when joining -name_len: '&4Username kamu terlalu panjang atau terlalu pendek!' -regex: '&4Username kamu mengandung karakter illegal. Karakter yg diijinkan: REG_EX' -# TODO country_banned: '&4Your country is banned from this server!' -# TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' -kick_fullserver: '&4Server sedang penuh, silahkan coba lagi nanti!' -same_nick: '&4Username yg sama telah bermain di server ini!' -# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' -# TODO same_ip_online: 'A player with the same IP is already in game!' +on_join_validation: + same_ip_online: '' + same_nick_online: '&4Username yg sama telah bermain di server ini!' + name_length: '&4Username kamu terlalu panjang atau terlalu pendek!' + characters_in_name: '&4Username kamu mengandung karakter illegal. Karakter yg diijinkan: %valid_chars' + kick_full_server: '&4Server sedang penuh, silahkan coba lagi nanti!' + country_banned: '' + not_owner_error: '' + invalid_name_case: '' # Email -usage_email_add: '&cUsage: /email add ' -usage_email_change: '&cUsage: /email change ' -usage_email_recovery: '&cUsage: /email recovery ' -new_email_invalid: '&cEmail baru tidak valid, coba lagi!' -old_email_invalid: '&cEmail lama tidak valid, coba lagi!' -email_invalid: '&cAlamat email tidak valid, coba lagi!' -email_added: '&2Berhasil menambahkan alamat email ke akunmu!' -email_confirm: '&cSilahkan konfirmasi alamat email kamu!' -email_changed: '&2Alamat email telah diubah dengan benar!' -email_send: '&2Email pemulihan akun telah dikirim! Silahkan periksa kotak masuk emailmu!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' -# TODO email_already_used: '&4The email address is already being used' -# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&3Silahkan tambahkan email ke akunmu menggunakan command "/email add "' -recovery_email: '&3Lupa password? silahkan gunakan command "/email recovery "' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + add_email_request: '&3Silahkan tambahkan email ke akunmu menggunakan command "/email add "' + usage_email_add: '&cUsage: /email add ' + usage_email_change: '&cUsage: /email change ' + new_email_invalid: '&cEmail baru tidak valid, coba lagi!' + old_email_invalid: '&cEmail lama tidak valid, coba lagi!' + invalid: '&cAlamat email tidak valid, coba lagi!' + added: '&2Berhasil menambahkan alamat email ke akunmu!' + request_confirmation: '&cSilahkan konfirmasi alamat email kamu!' + changed: '&2Alamat email telah diubah dengan benar!' + email_show: '' + no_email_for_account: '' + already_used: '' + incomplete_settings: '' + send_failure: '' + change_password_expired: '' + email_cooldown_error: '' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Lupa password? silahkan gunakan command "/email recovery "' + command_usage: '&cUsage: /email recovery ' + email_sent: '&2Email pemulihan akun telah dikirim! Silahkan periksa kotak masuk emailmu!' + code: + code_sent: '' + incorrect: '' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&3Kamu harus menyelesaikan kode captcha untuk login, silahkan gunakan command "/captcha "' -wrong_captcha: '&cCaptcha salah, gunakan command "/captcha THE_CAPTCHA" pada chat!' -valid_captcha: '&2Kode captcha terselesaikan!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Kamu harus menyelesaikan kode captcha untuk login, silahkan gunakan command "/captcha %captcha_code"' + wrong_captcha: '&cCaptcha salah, gunakan command "/captcha %captcha_code" pada chat!' + valid_captcha: '&2Kode captcha terselesaikan!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -# TODO second: 'second' -# TODO seconds: 'seconds' -# TODO minute: 'minute' -# TODO minutes: 'minutes' -# TODO hour: 'hour' -# TODO hours: 'hours' -# TODO day: 'day' -# TODO days: 'days' +time: + second: '' + seconds: '' + minute: '' + minutes: '' + hour: '' + hours: '' + day: '' + days: '' diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index c739bec93..fd59e5fa3 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -1,119 +1,138 @@ # Lingua Italiana creata da Maxetto e sgdc3. -# Registrazione -reg_msg: '&3Per favore, esegui la registrazione con il comando: /register ' -usage_reg: '&cUtilizzo: /register ' -reg_only: '&4Puoi giocare in questo server solo dopo aver eseguito la registrazione attraverso il sito web! Per favore, vai su http://esempio.it per procedere!' -kicked_admin_registered: 'Un amministratore ti ha appena registrato, per favore rientra nel server' -registered: '&2Registrato correttamente!' -reg_disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.' -user_regged: '&cHai già eseguito la registrazione, non puoi eseguirla nuovamente.' +# Registration +registration: + disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.' + name_taken: '&cHai già eseguito la registrazione, non puoi eseguirla nuovamente.' + register_request: '&3Per favore, esegui la registrazione con il comando: /register ' + command_usage: '&cUtilizzo: /register ' + reg_only: '&4Puoi giocare in questo server solo dopo aver eseguito la registrazione attraverso il sito web! Per favore, vai su http://esempio.it per procedere!' + success: '&2Registrato correttamente!' + kicked_admin_registered: 'Un amministratore ti ha appena registrato, per favore rientra nel server' -# Errori della password durante la registrazione -password_error: '&cLe password non corrispondono!' -password_error_nick: '&cNon puoi usare il tuo nome utente come password, per favore scegline un''altra...' -password_error_unsafe: '&cLa password che hai inserito non è sicura, per favore scegline un''altra...' -password_error_chars: '&4La tua password contiene caratteri non consentiti. I caratteri consentiti sono: REG_EX' -pass_len: '&cLa password che hai inserito è troppo corta o troppo lunga, per favore scegline un''altra...' +# Password errors on registration +password: + match_error: '&cLe password non corrispondono!' + name_in_password: '&cNon puoi usare il tuo nome utente come password, per favore scegline un''altra...' + unsafe_password: '&cLa password che hai inserito non è sicura, per favore scegline un''altra...' + forbidden_characters: '&4La tua password contiene caratteri non consentiti. I caratteri consentiti sono: %valid_chars' + wrong_length: '&cLa password che hai inserito è troppo corta o troppo lunga, per favore scegline un''altra...' -# Autenticazione -usage_log: '&cUtilizzo: /login ' -wrong_pwd: '&cPassword non corretta!' -login: '&2Autenticazione eseguita correttamente!' -login_msg: '&cPer favore, esegui l''autenticazione con il comando: /login ' -timeout: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!' +# Login +login: + command_usage: '&cUtilizzo: /login ' + wrong_password: '&cPassword non corretta!' + success: '' + login_request: '&cPer favore, esegui l''autenticazione con il comando: /login ' + timeout_error: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!' -# Errori -unknown_user: '&cL''utente non ha ancora eseguito la registrazione.' -denied_command: '&cPer poter usare questo comando devi essere autenticato!' -denied_chat: '&cPer poter scrivere messaggi in chat devi essere autenticato!' -not_logged_in: '&cNon hai ancora eseguito l''autenticazione!' -tempban_max_logins: '&cSei stato temporaneamente bandito per aver fallito l''autenticazione troppe volte.' -max_reg: '&cHai raggiunto il numero massimo di registrazioni (%reg_count/%max_acc %reg_names) per questo indirizzo IP!' -no_perm: '&4Non hai il permesso di eseguire questa operazione.' -error: '&4Qualcosa è andato storto, riporta questo errore ad un amministratore!' -kick_forvip: '&3Un utente VIP è entrato mentre il server era pieno e ha preso il tuo posto!' +# Errors +error: + denied_command: '&cPer poter usare questo comando devi essere autenticato!' + denied_chat: '&cPer poter scrivere messaggi in chat devi essere autenticato!' + unregistered_user: '&cL''utente non ha ancora eseguito la registrazione.' + not_logged_in: '&cNon hai ancora eseguito l''autenticazione!' + no_permission: '&4Non hai il permesso di eseguire questa operazione.' + unexpected_error: '' + max_registration: '&cHai raggiunto il numero massimo di registrazioni (%reg_count/%max_acc %reg_names) per questo indirizzo IP!' + logged_in: '&cHai già eseguito l''autenticazione, non è necessario eseguirla nuovamente!' + kick_for_vip: '&3Un utente VIP è entrato mentre il server era pieno e ha preso il tuo posto!' + tempban_max_logins: '&cSei stato temporaneamente bandito per aver fallito l''autenticazione troppe volte.' # AntiBot -kick_antibot: 'Il servizio di AntiBot è attualmente attivo! Devi aspettare qualche minuto prima di poter entrare nel server.' -antibot_auto_enabled: '&4Il servizio di AntiBot è stato automaticamente abilitato a seguito delle numerose connessioni!' -antibot_auto_disabled: "&2Il servizio di AntiBot è stato automaticamente disabilitato dopo %m minuti!" +antibot: + kick_antibot: 'Il servizio di AntiBot è attualmente attivo! Devi aspettare qualche minuto prima di poter entrare nel server.' + auto_enabled: '&4Il servizio di AntiBot è stato automaticamente abilitato a seguito delle numerose connessioni!' + auto_disabled: '&2Il servizio di AntiBot è stato automaticamente disabilitato dopo %m minuti!' -# Altri messaggi -unregistered: '&2Sei stato correttamente rimosso dal database!' -accounts_owned_self: 'Possiedi %count account:' -accounts_owned_other: 'Il giocatore %name possiede %count account:' -two_factor_create: '&2Il tuo codice segreto è: &f%code%%nl%&2Puoi anche scannerizzare il codice QR da qui: &f%url' -recovery_code_sent: 'Una email contenente il codice di recupero per reimpostare la tua password è stata appena inviata al tuo indirizzo email.' -recovery_code_incorrect: 'Il codice di recupero inserito non è corretto! Hai altri %count tentativi rimanenti.' -recovery_tries_exceeded: 'Hai superato il numero massimo di tentativi per inserire il codice di recupero. Scrivi "/email recovery [email]" per generarne uno nuovo.' -recovery_code_correct: 'Codice di recupero inserito correttamente!' -recovery_change_password: 'Per favore usa il comando "/email setpassword " per cambiare immediatamente la tua password.' -vb_nonActiv: '&cIl tuo account non è stato ancora verificato, controlla fra le tue email per scoprire come attivarlo!' -usage_unreg: '&cUtilizzo: /unregister ' -pwd_changed: '&2Password cambiata correttamente!' -logged_in: '&cHai già eseguito l''autenticazione, non è necessario eseguirla nuovamente!' -logout: '&2Disconnessione avvenuta correttamente!' -reload: '&2La configurazione e il database sono stati ricaricati correttamente!' -usage_changepassword: '&cUtilizzo: /changepassword ' +# Unregister +unregister: + success: '&2Sei stato correttamente rimosso dal database!' + command_usage: '&cUtilizzo: /unregister ' -# Messaggi della sessione -invalid_session: '&cIl tuo indirizzo IP è cambiato e la tua sessione è stata terminata!' -valid_session: '&2Autenticato automaticamente attraverso la precedente sessione!' +# Other messages +misc: + account_not_activated: '&cIl tuo account non è stato ancora verificato, controlla fra le tue email per scoprire come attivarlo!' + password_changed: '&2Password cambiata correttamente!' + logout: '&2Disconnessione avvenuta correttamente!' + reload: '&2La configurazione e il database sono stati ricaricati correttamente!' + usage_change_password: '&cUtilizzo: /changepassword ' + two_factor_create: '&2Il tuo codice segreto è: &f%code%%nl%&2Puoi anche scannerizzare il codice QR da qui: &f%url' + accounts_owned_self: 'Possiedi %count account:' + accounts_owned_other: 'Il giocatore %name possiede %count account:' -# Messaggi di errore durante il collegamento -name_len: '&4Il tuo nome utente è troppo corto o troppo lungo!' -regex: '&4Il tuo nome utente contiene caratteri non consentiti. I caratteri consentiti sono: REG_EX' -country_banned: '&4Il tuo paese è bandito da questo server!' -not_owner_error: 'Non sei il proprietario di questo account. Per favore scegli un altro nome!' -kick_fullserver: '&4Il server è attualmente pieno, riprova più tardi!' -same_nick: '&4Un giocatore con il tuo stesso nome utente è già connesso sul server!' -invalid_name_case: 'Dovresti entrare con questo nome utente "%valid", al posto di "%invalid".' -same_ip_online: 'Un giocatore con il tuo stesso IP è già connesso sul server!' +# Session messages +session: + valid_session: '&2Autenticato automaticamente attraverso la precedente sessione!' + invalid_session: '&cIl tuo indirizzo IP è cambiato e la tua sessione è stata terminata!' + +# Error messages when joining +on_join_validation: + same_ip_online: 'Un giocatore con il tuo stesso IP è già connesso sul server!' + same_nick_online: '&4Un giocatore con il tuo stesso nome utente è già connesso sul server!' + name_length: '&4Il tuo nome utente è troppo corto o troppo lungo!' + characters_in_name: '&4Il tuo nome utente contiene caratteri non consentiti. I caratteri consentiti sono: %valid_chars' + kick_full_server: '&4Il server è attualmente pieno, riprova più tardi!' + country_banned: '&4Il tuo paese è bandito da questo server!' + not_owner_error: 'Non sei il proprietario di questo account. Per favore scegli un altro nome!' + invalid_name_case: 'Dovresti entrare con questo nome utente "%valid", al posto di "%invalid".' # Email -usage_email_add: '&cUtilizzo: /email add ' -usage_email_change: '&cUtilizzo: /email change ' -usage_email_recovery: '&cUtilizzo: /email recovery ' -new_email_invalid: '&cIl nuovo indirizzo email inserito non è valido, riprova!' -old_email_invalid: '&cIl vecchio indirizzo email inserito non è valido, riprova!' -email_invalid: '&cL''indirizzo email inserito non è valido, riprova!' -email_added: '&2Indirizzo email aggiunto correttamente al tuo account!' -email_confirm: '&cPer favore, conferma il tuo indirizzo email!' -email_changed: '&2Indirizzo email cambiato correttamente!' -email_send: '&2Una email di recupero è stata appena inviata al tuo indirizzo email!' -email_show: '&2Il tuo indirizzo email al momento è: &f%email' -incomplete_email_settings: 'Errore: non tutte le impostazioni richieste per inviare le email sono state impostate. Per favore contatta un amministratore.' -email_already_used: '&4L''indirizzo email inserito è già in uso' -email_send_failure: 'Non è stato possibile inviare l''email di recupero. Per favore contatta un amministratore.' -show_no_email: '&2Al momento non hai nessun indirizzo email associato al tuo account.' -add_email: '&3Per poter recuperare la password in futuro, aggiungi un indirizzo email al tuo account con il comando: /email add ' -recovery_email: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery ' -change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' -email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' +email: + add_email_request: '&3Per poter recuperare la password in futuro, aggiungi un indirizzo email al tuo account con il comando: /email add ' + usage_email_add: '&cUtilizzo: /email add ' + usage_email_change: '&cUtilizzo: /email change ' + new_email_invalid: '&cIl nuovo indirizzo email inserito non è valido, riprova!' + old_email_invalid: '&cIl vecchio indirizzo email inserito non è valido, riprova!' + invalid: '&cL''indirizzo email inserito non è valido, riprova!' + added: '&2Indirizzo email aggiunto correttamente al tuo account!' + request_confirmation: '&cPer favore, conferma il tuo indirizzo email!' + changed: '&2Indirizzo email cambiato correttamente!' + email_show: '&2Il tuo indirizzo email al momento è: &f%email' + no_email_for_account: '&2Al momento non hai nessun indirizzo email associato al tuo account.' + already_used: '&4L''indirizzo email inserito è già in uso' + incomplete_settings: 'Errore: non tutte le impostazioni richieste per inviare le email sono state impostate. Per favore contatta un amministratore.' + send_failure: 'Non è stato possibile inviare l''email di recupero. Per favore contatta un amministratore.' + change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' + email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery ' + command_usage: '&cUtilizzo: /email recovery ' + email_sent: '&2Una email di recupero è stata appena inviata al tuo indirizzo email!' + code: + code_sent: 'Una email contenente il codice di recupero per reimpostare la tua password è stata appena inviata al tuo indirizzo email.' + incorrect: 'Il codice di recupero inserito non è corretto! Hai altri %count tentativi rimanenti.' + tries_exceeded: 'Hai superato il numero massimo di tentativi per inserire il codice di recupero. Scrivi "/email recovery [email]" per generarne uno nuovo.' + correct: 'Codice di recupero inserito correttamente!' + change_password: 'Per favore usa il comando "/email setpassword " per cambiare immediatamente la tua password.' # Captcha -usage_captcha: '&3Per poterti autenticare devi risolvere un captcha, per favore scrivi: /captcha ' -wrong_captcha: '&cCaptcha sbagliato, per favore riprova scrivendo "/captcha THE_CAPTCHA" in chat!' -valid_captcha: '&2Il captcha inserito è valido!' -captcha_for_registration: 'Per poterti registrare devi prima risolvere un captcha, per favore scrivi: /captcha ' -register_captcha_valid: '&2Il captcha inserito è valido! Ora puoi eseguire la registrazione con: /register ' +captcha: + usage_captcha: '&3Per poterti autenticare devi risolvere un captcha, per favore scrivi: /captcha %captcha_code' + wrong_captcha: '&cCaptcha sbagliato, per favore riprova scrivendo "/captcha %captcha_code" in chat!' + valid_captcha: '&2Il captcha inserito è valido!' + captcha_for_registration: 'Per poterti registrare devi prima risolvere un captcha, per favore scrivi: /captcha %captcha_code' + register_captcha_valid: '&2Il captcha inserito è valido! Ora puoi eseguire la registrazione con: /register ' -# Codice di verifica -verification_code_required: '&3Questo comando va a modificare dati sensibili e richiede una verifica tramite email! Controlla la tua posta in arrivo e segui le istruzioni nell''email.' -usage_verification_code: '&cUtilizzo: /verification ' -incorrect_verification_code: '&cCodice sbagliato, per favore riprova scrivendo "/verification " in chat, usando il codice che hai ricevuto tramite email' -verification_code_verified: '&2La tua identità è stata verificata! Ora puoi eseguire tutti i comandi che modificano dati sensibili per questa sessione!' -verification_code_already_verified: '&2Puoi già eseguire tutti i comandi che modificano dati sensibili per questa sessione!' -verification_code_expired: '&3Il tuo codice è scaduto! Esegui nuovamente un comando che modifica dati sensibili per ricevere uno nuovo codice!' -verification_code_email_needed: '&3Per verificare la tua identità devi collegare un indirizzo email al tuo account!' +# Verification code +verification: + code_required: '&3Questo comando va a modificare dati sensibili e richiede una verifica tramite email! Controlla la tua posta in arrivo e segui le istruzioni nell''email.' + command_usage: '&cUtilizzo: /verification ' + incorrect_code: '&cCodice sbagliato, per favore riprova scrivendo "/verification " in chat, usando il codice che hai ricevuto tramite email' + success: '&2La tua identità è stata verificata! Ora puoi eseguire tutti i comandi che modificano dati sensibili per questa sessione!' + already_verified: '&2Puoi già eseguire tutti i comandi che modificano dati sensibili per questa sessione!' + code_expired: '&3Il tuo codice è scaduto! Esegui nuovamente un comando che modifica dati sensibili per ricevere uno nuovo codice!' + email_needed: '&3Per verificare la tua identità devi collegare un indirizzo email al tuo account!' -# Unità di tempo -second: 'secondo' -seconds: 'secondi' -minute: 'minuto' -minutes: 'minuti' -hour: 'ora' -hours: 'ore' -day: 'giorno' -days: 'giorni' +# Time units +time: + second: 'secondo' + seconds: 'secondi' + minute: 'minuto' + minutes: 'minuti' + hour: 'ora' + hours: 'ore' + day: 'giorno' + days: 'giorni' diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index 2bf52b109..cdc4f02d6 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -1,121 +1,139 @@ #Translated by Kirito (kds123321@naver.com), System32(me@syst32.com) #14.05.2017 Thanks for use -# 회원가입 -reg_msg: '&3다음 명령어로 서버에 가입해주세요: /register <비밀번호> <비밀번호 확인>' -usage_reg: '&c사용법: /register <비밀번호> <비밀번호 확인>' -reg_only: '&4등록된 유저만 서버에 접속할 수 있습니다! 서버 홈페이지에 방문하여 가입해 주세요!' -kicked_admin_registered: '관리자가 방금 이 닉네임을 등록했습니다. 다시 로그인 해주세요' -registered: '&2회원가입이 완료되었습니다!' -reg_disabled: '&c게임 안에서의 회원가입은 비활성화 되어 있습니다!' -user_regged: '&c이미 이 닉네임으로 회원가입이 되어 있습니다!' +# Registration +registration: + disabled: '&c게임 안에서의 회원가입은 비활성화 되어 있습니다!' + name_taken: '&c이미 이 닉네임으로 회원가입이 되어 있습니다!' + register_request: '&3다음 명령어로 서버에 가입해주세요: /register <비밀번호> <비밀번호 확인>' + command_usage: '&c사용법: /register <비밀번호> <비밀번호 확인>' + reg_only: '&4등록된 유저만 서버에 접속할 수 있습니다! 서버 홈페이지에 방문하여 가입해 주세요!' + success: '&2회원가입이 완료되었습니다!' + kicked_admin_registered: '관리자가 방금 이 닉네임을 등록했습니다. 다시 로그인 해주세요' -# 회원가입중 비밀번호 오류 -password_error: '&c비밀번호가 일치하지 않습니다, 다시 확인해주세요!' -password_error_nick: '&c자신의 닉네임을 비밀번호로 사용할 수 없습니다, 다른 비밀번호를 사용하세요...' -password_error_unsafe: '&c이 비밀번호는 안전하지 않습니다, 다른 비밀번호를 사용하세요...' -password_error_chars: '&4비밀번호에 잘못된 문자가 있습니다. 허용된 문자: REG_EX' -pass_len: '&c비밀번호가 너무 짧거나 너무 깁니다!' +# Password errors on registration +password: + match_error: '&c비밀번호가 일치하지 않습니다, 다시 확인해주세요!' + name_in_password: '&c자신의 닉네임을 비밀번호로 사용할 수 없습니다, 다른 비밀번호를 사용하세요...' + unsafe_password: '&c이 비밀번호는 안전하지 않습니다, 다른 비밀번호를 사용하세요...' + forbidden_characters: '&4비밀번호에 잘못된 문자가 있습니다. 허용된 문자: %valid_chars' + wrong_length: '&c비밀번호가 너무 짧거나 너무 깁니다!' -# 로그인 -usage_log: '&c사용법: /login <비밀번호>' -wrong_pwd: '&c비밀번호가 잘못되었습니다!' -login: '&2로그인 되었습니다!' -login_msg: '&c다음 명령어로 로그인 해주세요: /login <비밀번호>' -timeout: '&4로그인 시간이 초과 되어 서버에서 추방당했습니다. 다시 시도하세요!' +# Login +login: + command_usage: '&c사용법: /login <비밀번호>' + wrong_password: '&c비밀번호가 잘못되었습니다!' + success: '' + login_request: '&c다음 명령어로 로그인 해주세요: /login <비밀번호>' + timeout_error: '&4로그인 시간이 초과 되어 서버에서 추방당했습니다. 다시 시도하세요!' -# 오류 -unknown_user: '&c이 유저는 등록되지 않았습니다!' -denied_command: '&c이 명령어를 사용하려면 로그인해야 합니다!' -denied_chat: '&c채팅을 하려면 로그인해야 합니다!' -not_logged_in: '&c로그인이 되어있지 않습니다!' -tempban_max_logins: '&c너무 많이 로그인에 실패하여 잠시 서버에서 차단되었습니다.' -max_reg: '&c당신은 가입할 수 있는 계정 한도를 초과했습니다 (%reg_count/%max_acc %reg_names)!' -no_perm: '&4이 작업을 수행할 수 있는 권한이 없습니다!' -error: '&4예기치 않은 오류가 발생했습니다, 관리자에게 알려주세요!' -kick_forvip: '&3서버가 꽉 차있을땐 VIP 플레이어만 접속이 가능합니다!' +# Errors +error: + denied_command: '&c이 명령어를 사용하려면 로그인해야 합니다!' + denied_chat: '&c채팅을 하려면 로그인해야 합니다!' + unregistered_user: '&c이 유저는 등록되지 않았습니다!' + not_logged_in: '&c로그인이 되어있지 않습니다!' + no_permission: '&4이 작업을 수행할 수 있는 권한이 없습니다!' + unexpected_error: '' + max_registration: '&c당신은 가입할 수 있는 계정 한도를 초과했습니다 (%reg_count/%max_acc %reg_names)!' + logged_in: '&c이미 로그인되어 있습니다!' + kick_for_vip: '&3서버가 꽉 차있을땐 VIP 플레이어만 접속이 가능합니다!' + tempban_max_logins: '&c너무 많이 로그인에 실패하여 잠시 서버에서 차단되었습니다.' -# 봇테러 방지 -kick_antibot: 'AntiBot 보호 모드가 활성화 되었습니다! 서버에 접속하려면 몇 분 기다려야 합니다.' -antibot_auto_enabled: '&4[AntiBotService] 엄청나게 많은 연결이 감지 되어 AntiBot이 활성화 되었습니다!' -antibot_auto_disabled: '&2[AntiBotService] %m 분 후에 AntiBot이 비활성화 됩니다!' +# AntiBot +antibot: + kick_antibot: 'AntiBot 보호 모드가 활성화 되었습니다! 서버에 접속하려면 몇 분 기다려야 합니다.' + auto_enabled: '&4[AntiBotService] 엄청나게 많은 연결이 감지 되어 AntiBot이 활성화 되었습니다!' + auto_disabled: '&2[AntiBotService] %m 분 후에 AntiBot이 비활성화 됩니다!' -# 기타 메세지 -unregistered: '&c회원 탈퇴가 완료되었습니다!' -accounts_owned_self: '%count 개의 계정을 소유하고 있습니다.' -accounts_owned_other: '플레이어 %name 는 %count 개의 계정을 소유하고 있습니다:' -two_factor_create: '&2당신의 비밀 코드는 %code 입니다. %url 에서 스캔할 수 있습니다' -recovery_code_sent: '비밀번호 재설정을 위한 복구 코드가 이메일로 전송되었습니다.' -# TODO: Missing tags %count -recovery_code_incorrect: '복구 코드가 올바르지 않습니다! "/email recovery [이메일 주소]"를 이용하여 새로 생성하세요.' -recovery_tries_exceeded: '잘못된 복구 코드를 너무 많이 입력했습니다. "/email recovery [이메일 주소]"를 통해 새로 생성하세요.' -recovery_code_correct: '복구 코드가 정상적으로 입력되었습니다!' -recovery_change_password: '비밀번호를 변경하려면 /email setpassword <새 비밀번호>를 입력해주세요.' -vb_nonActiv: '&c계정이 아직 활성화되지 않았습니다. 이메일을 확인해보세요!' -usage_unreg: '&c사용법: /unregister <비밀번호>' -pwd_changed: '&2비밀번호가 변경되었습니다!' -logged_in: '&c이미 로그인되어 있습니다!' -logout: '&2로그아웃 되었습니다!' -reload: '&2설정과 데이터 베이스가 새로고침 되었습니다!' -usage_changepassword: '&c사용법: /changepassword <예전 비밀번호> <새 비밀번호>' +# Unregister +unregister: + success: '&c회원 탈퇴가 완료되었습니다!' + command_usage: '&c사용법: /unregister <비밀번호>' -# 세션 메세지 -invalid_session: '&cIP가 변경되어 세션이 만료되었습니다!' -valid_session: '&2세션 재 연결로 인해 로그인 되었습니다.' +# Other messages +misc: + account_not_activated: '&c계정이 아직 활성화되지 않았습니다. 이메일을 확인해보세요!' + password_changed: '&2비밀번호가 변경되었습니다!' + logout: '&2로그아웃 되었습니다!' + reload: '&2설정과 데이터 베이스가 새로고침 되었습니다!' + usage_change_password: '&c사용법: /changepassword <예전 비밀번호> <새 비밀번호>' + two_factor_create: '&2당신의 비밀 코드는 %code 입니다. %url 에서 스캔할 수 있습니다' + accounts_owned_self: '%count 개의 계정을 소유하고 있습니다.' + accounts_owned_other: '플레이어 %name 는 %count 개의 계정을 소유하고 있습니다:' -# 접속 오류 메세지 -name_len: '&4닉네임이 너무 짧거나 너무 깁니다!' -regex: '&4닉네임에 잘못된 문자가 있습니다. 허용된 문자: REG_EX' -country_banned: '&4당신의 국가에서는 이 서버를 이용하실 수 없습니다!' -not_owner_error: '이 계정의 소유자가 아닙니다. 다른 닉네임을 선택하세요!' -kick_fullserver: '&4서버가 꽉 찼습니다. 나중에 다시 접속해보세요!' -same_nick: '&4똑같은 닉네임이 이미 이 서버에 접속해 있습니다!' -invalid_name_case: '%invalid가 아닌, %valid 사용하여 접속해야 합니다.' -same_ip_online: '똑같은 IP가 이미 이 서버에 접속해 있습니다!' +# Session messages +session: + valid_session: '&2세션 재 연결로 인해 로그인 되었습니다.' + invalid_session: '&cIP가 변경되어 세션이 만료되었습니다!' -# 이메일 -usage_email_add: '&c사용법: /email add <이메일 주소> <이메일 주소 확인>' -usage_email_change: '&c사용법: /email change <예전 이메일 주소> <새 이메일 주소>' -usage_email_recovery: '&c사용법: /email recovery <이메일 주소>' -new_email_invalid: '&c새 이메일 주소가 잘못되었습니다. 다시 시도해보세요!' -old_email_invalid: '&c예전 이메일 주소가 잘못되었습니다. 다시 시도해보세요!' -email_invalid: '&c이메일 주소가 잘못되었습니다. 다시 시도해보세요!' -email_added: '&2계정에 이메일 주소를 추가했습니다!' -email_confirm: '&c이메일 주소를 확인해주세요!' -email_changed: '&2이메일 주소가 변경되었습니다!' -email_send: '&2복구 이메일을 보냈습니다! 메일함을 확인해보세요!' -email_show: '&2현재 이메일 주소: &f%email' -incomplete_email_settings: '오류: 메일을 보내기 위해 필요한 설정이 되어 있지 않습니다. 관리자에게 알려주세요.' -email_already_used: '&4이메일 주소가 이미 사용 중입니다.' -email_send_failure: '이메일을 보낼 수 없습니다. 관리자에게 알려주세요.' -show_no_email: '&2현재 이 계정과 연결된 이메일 주소가 없습니다.' -add_email: '&3다음 명령어로 계정에 이메일 주소를 추가하세요: /email add <이메일 주소> <이메일 주소 확인>' -recovery_email: '&3비밀번호를 잊으셨나요? 이 명령어를 사용해보세요: /email recovery <이메일 주소>' -change_password_expired: '더 이상 이 명령어를 통해 비밀번호를 변경할 수 없습니다.' -email_cooldown_error: '&c이메일을 이미 발송했습니다. %time 후에 다시 발송할 수 있습니다.' +# Error messages when joining +on_join_validation: + same_ip_online: '똑같은 IP가 이미 이 서버에 접속해 있습니다!' + same_nick_online: '&4똑같은 닉네임이 이미 이 서버에 접속해 있습니다!' + name_length: '&4닉네임이 너무 짧거나 너무 깁니다!' + characters_in_name: '&4닉네임에 잘못된 문자가 있습니다. 허용된 문자: %valid_chars' + kick_full_server: '&4서버가 꽉 찼습니다. 나중에 다시 접속해보세요!' + country_banned: '&4당신의 국가에서는 이 서버를 이용하실 수 없습니다!' + not_owner_error: '이 계정의 소유자가 아닙니다. 다른 닉네임을 선택하세요!' + invalid_name_case: '%invalid가 아닌, %valid 사용하여 접속해야 합니다.' -# 캡챠 -usage_captcha: '&3로그인 하려면 CAPTCHA 코드를 입력해야 합니다. 이 명령어를 사용하세요: /captcha ' -wrong_captcha: '&c잘못된 CAPTCHA 코드 입니다. "/captcha THE_CAPTCHA" 형태로 입력해주세요!' -valid_captcha: '&2CAPTCHA 코드가 확인되었습니다!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +# Email +email: + add_email_request: '&3다음 명령어로 계정에 이메일 주소를 추가하세요: /email add <이메일 주소> <이메일 주소 확인>' + usage_email_add: '&c사용법: /email add <이메일 주소> <이메일 주소 확인>' + usage_email_change: '&c사용법: /email change <예전 이메일 주소> <새 이메일 주소>' + new_email_invalid: '&c새 이메일 주소가 잘못되었습니다. 다시 시도해보세요!' + old_email_invalid: '&c예전 이메일 주소가 잘못되었습니다. 다시 시도해보세요!' + invalid: '&c이메일 주소가 잘못되었습니다. 다시 시도해보세요!' + added: '&2계정에 이메일 주소를 추가했습니다!' + request_confirmation: '&c이메일 주소를 확인해주세요!' + changed: '&2이메일 주소가 변경되었습니다!' + email_show: '&2현재 이메일 주소: &f%email' + no_email_for_account: '&2현재 이 계정과 연결된 이메일 주소가 없습니다.' + already_used: '&4이메일 주소가 이미 사용 중입니다.' + incomplete_settings: '오류: 메일을 보내기 위해 필요한 설정이 되어 있지 않습니다. 관리자에게 알려주세요.' + send_failure: '이메일을 보낼 수 없습니다. 관리자에게 알려주세요.' + change_password_expired: '더 이상 이 명령어를 통해 비밀번호를 변경할 수 없습니다.' + email_cooldown_error: '&c이메일을 이미 발송했습니다. %time 후에 다시 발송할 수 있습니다.' -# 인증 코드 -verification_code_required: '&3이 명령어는 매우 민감하게 작동되며, 이메일 인증을 필요로 합니다. 이메일을 확인하고 지시에 따르십시오.' -usage_verification_code: '&c사용법: /verification <인증 코드>' -incorrect_verification_code: '&c코드가 올바르지 않습니다. "/verification <인증 코드>" 형태로 입력해주세요.' -verification_code_verified: '&2신원이 확인되었습니다. 귀하는 이 세션이 만료되기 전까지 모든 명령어를 사용할 수 있습니다.' -verification_code_already_verified: '&2이 인증 코드는 이미 사용되었습니다!' -verification_code_expired: '&3이 인증 코드는 만료되었습니다! 새 인증 코드를 발급받아주세요.' -verification_code_email_needed: '&3귀하의 신원을 확인하기 위해서는 귀하의 계정과 이메일 주소가 연결되어 있어야 합니다.' +# Password recovery by email +recovery: + forgot_password_hint: '&3비밀번호를 잊으셨나요? 이 명령어를 사용해보세요: /email recovery <이메일 주소>' + command_usage: '&c사용법: /email recovery <이메일 주소>' + email_sent: '&2복구 이메일을 보냈습니다! 메일함을 확인해보세요!' + code: + code_sent: '비밀번호 재설정을 위한 복구 코드가 이메일로 전송되었습니다.' + incorrect: '복구 코드가 올바르지 않습니다! "/email recovery [이메일 주소]"를 이용하여 새로 생성하세요.' + tries_exceeded: '잘못된 복구 코드를 너무 많이 입력했습니다. "/email recovery [이메일 주소]"를 통해 새로 생성하세요.' + correct: '복구 코드가 정상적으로 입력되었습니다!' + change_password: '비밀번호를 변경하려면 /email setpassword <새 비밀번호>를 입력해주세요.' -# 시간 단위 -second: '초' -seconds: '초' -minute: '분' -minutes: '분' -hour: '시간' -hours: '시간' -day: '일' -days: '일' +# Captcha +captcha: + usage_captcha: '&3로그인 하려면 CAPTCHA 코드를 입력해야 합니다. 이 명령어를 사용하세요: /captcha %captcha_code' + wrong_captcha: '&c잘못된 CAPTCHA 코드 입니다. "/captcha %captcha_code" 형태로 입력해주세요!' + valid_captcha: '&2CAPTCHA 코드가 확인되었습니다!' + captcha_for_registration: '' + register_captcha_valid: '' + +# Verification code +verification: + code_required: '&3이 명령어는 매우 민감하게 작동되며, 이메일 인증을 필요로 합니다. 이메일을 확인하고 지시에 따르십시오.' + command_usage: '&c사용법: /verification <인증 코드>' + incorrect_code: '&c코드가 올바르지 않습니다. "/verification <인증 코드>" 형태로 입력해주세요.' + success: '&2신원이 확인되었습니다. 귀하는 이 세션이 만료되기 전까지 모든 명령어를 사용할 수 있습니다.' + already_verified: '&2이 인증 코드는 이미 사용되었습니다!' + code_expired: '&3이 인증 코드는 만료되었습니다! 새 인증 코드를 발급받아주세요.' + email_needed: '&3귀하의 신원을 확인하기 위해서는 귀하의 계정과 이메일 주소가 연결되어 있어야 합니다.' + +# Time units +time: + second: '초' + seconds: '초' + minute: '분' + minutes: '분' + hour: '시간' + hours: '시간' + day: '일' + days: '일' diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index 6db5f5480..67fba54eb 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&ePrasome prisiregistruoti: /register slaptazodis pakartotiSlaptazodi' -usage_reg: '&eNaudojimas: /register slaptazodis pakartotiSlaptazodi' -reg_only: '&cTik prisiregistravusiem zaidejams: apsilankykite: http://example.com tam kad uzsiregistruoti.' -# TODO kicked_admin_registered: 'An admin just registered you; please log in again' -registered: '&aSekmingai prisiregistravote.' -reg_disabled: '&6Registracija yra isjungta' -user_regged: '&cVartotojo vardas jau uzregistruotas' +registration: + disabled: '&6Registracija yra isjungta' + name_taken: '&cVartotojo vardas jau uzregistruotas' + register_request: '&ePrasome prisiregistruoti: /register slaptazodis pakartotiSlaptazodi' + command_usage: '&eNaudojimas: /register slaptazodis pakartotiSlaptazodi' + reg_only: '&cTik prisiregistravusiem zaidejams: apsilankykite: http://example.com tam kad uzsiregistruoti.' + success: '&aSekmingai prisiregistravote.' + kicked_admin_registered: '' # Password errors on registration -password_error: '&cSlaptazodziai nesutampa' -# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' -# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' -# TODO password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX' -pass_len: '&cJusu slaptazodis buvo per ilgas arba per trumpas.' +password: + match_error: '&cSlaptazodziai nesutampa' + name_in_password: '' + unsafe_password: '' + forbidden_characters: '' + wrong_length: '&cJusu slaptazodis buvo per ilgas arba per trumpas.' # Login -usage_log: '&eKomandos panaudojimas: /login slaptazodis' -wrong_pwd: '&cNeteisingas slaptazosdis' -login: '&aSekmingai prisijungete' -login_msg: '&ePrasome prisijungti: /login slaptazodis' -timeout: '&cNespejote prisijungti' +login: + command_usage: '&eKomandos panaudojimas: /login slaptazodis' + wrong_password: '&cNeteisingas slaptazosdis' + success: '' + login_request: '&ePrasome prisijungti: /login slaptazodis' + timeout_error: '&cNespejote prisijungti' # Errors -unknown_user: '&cVartotojas neprisiregistraves' -# TODO denied_command: '&cIn order to use this command you must be authenticated!' -# TODO denied_chat: '&cIn order to chat you must be authenticated!' -not_logged_in: '&cTu neprisijunges!' -# TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&cJus pasiekete maksimalu registraciju skaiciu.' -no_perm: '&cNera leidimo' -error: '&cAtsirado klaida, praneskite adminstratoriui.' -kick_forvip: '&cA VIP prisijunge i pilna serveri!' +error: + denied_command: '' + denied_chat: '' + unregistered_user: '&cVartotojas neprisiregistraves' + not_logged_in: '&cTu neprisijunges!' + no_permission: '&cNera leidimo' + unexpected_error: '' + max_registration: '&cJus pasiekete maksimalu registraciju skaiciu.' + logged_in: '&cTu aju prisijunges!' + kick_for_vip: '&cA VIP prisijunge i pilna serveri!' + tempban_max_logins: '' # AntiBot -# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' -# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' +antibot: + kick_antibot: '' + auto_enabled: '' + auto_disabled: '' + +# Unregister +unregister: + success: '&aSekmingai issiregistravote!' + command_usage: '&ePanaikinti registracija: "/unregister slaptazodis"' # Other messages -unregistered: '&aSekmingai issiregistravote!' -# TODO accounts_owned_self: 'You own %count accounts:' -# TODO accounts_owned_other: 'The player %name has %count accounts:' -# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&aJusu vartotojas nera patvirtintas, patikrinkite el.pasta.' -usage_unreg: '&ePanaikinti registracija: "/unregister slaptazodis"' -pwd_changed: '&aSlaptazodis pakeistas' -logged_in: '&cTu aju prisijunges!' -logout: '&aSekmingai atsijungete' -reload: '&aNustatymai ir duomenu baze buvo perkrauta.' -usage_changepassword: '&ePanaudojimas: /changepassword senasSlaptazodis naujasSlaptazodis' +misc: + account_not_activated: '&aJusu vartotojas nera patvirtintas, patikrinkite el.pasta.' + password_changed: '&aSlaptazodis pakeistas' + logout: '&aSekmingai atsijungete' + reload: '&aNustatymai ir duomenu baze buvo perkrauta.' + usage_change_password: '&ePanaudojimas: /changepassword senasSlaptazodis naujasSlaptazodis' + two_factor_create: '' + accounts_owned_self: '' + accounts_owned_other: '' # Session messages -invalid_session: '&cSesijos laikai nesutampa, prasome palaukti kol secija baigsis.' -valid_session: '&aSesijos prisijungimas' +session: + valid_session: '&aSesijos prisijungimas' + invalid_session: '&cSesijos laikai nesutampa, prasome palaukti kol secija baigsis.' # Error messages when joining -name_len: '&cJusu varsdas yra per ilgas arba per trumpas.' -regex: '&cJusu varde yra neledziamu simboliu, leidziami: REG_EX' -# TODO country_banned: '&4Your country is banned from this server!' -# TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' -kick_fullserver: '&cServeris yra pilnas, Atsiprasome.' -same_nick: '&cKazkas situo vardu jau zaidzia.' -# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' -# TODO same_ip_online: 'A player with the same IP is already in game!' +on_join_validation: + same_ip_online: '' + same_nick_online: '&cKazkas situo vardu jau zaidzia.' + name_length: '&cJusu varsdas yra per ilgas arba per trumpas.' + characters_in_name: '&cJusu varde yra neledziamu simboliu, leidziami: %valid_chars' + kick_full_server: '&cServeris yra pilnas, Atsiprasome.' + country_banned: '' + not_owner_error: '' + invalid_name_case: '' # Email -# TODO usage_email_add: '&cUsage: /email add ' -# TODO usage_email_change: '&cUsage: /email change ' -# TODO usage_email_recovery: '&cUsage: /email recovery ' -# TODO new_email_invalid: '&cInvalid new email, try again!' -# TODO old_email_invalid: '&cInvalid old email, try again!' -# TODO email_invalid: '&cInvalid email address, try again!' -# TODO email_added: '&2Email address successfully added to your account!' -# TODO email_confirm: '&cPlease confirm your email address!' -# TODO email_changed: '&2Email address changed correctly!' -# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' -# TODO email_already_used: '&4The email address is already being used' -# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&ePrasau pridekite savo el.pasta : /email add Email confirmEmail' -recovery_email: '&cPamirsote slaptazodi? Rasykite: /email recovery el.pastas' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + add_email_request: '&ePrasau pridekite savo el.pasta : /email add Email confirmEmail' + usage_email_add: '' + usage_email_change: '' + new_email_invalid: '' + old_email_invalid: '' + invalid: '' + added: '' + request_confirmation: '' + changed: '' + email_show: '' + no_email_for_account: '' + already_used: '' + incomplete_settings: '' + send_failure: '' + change_password_expired: '' + email_cooldown_error: '' + +# Password recovery by email +recovery: + forgot_password_hint: '&cPamirsote slaptazodi? Rasykite: /email recovery el.pastas' + command_usage: '' + email_sent: '' + code: + code_sent: '' + incorrect: '' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&cPanaudojimas: /captcha ' -wrong_captcha: '&cNeteisinga Captcha, naudokite : /captcha THE_CAPTCHA' -valid_captcha: '&cJusu captcha Teisinga!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&cPanaudojimas: /captcha %captcha_code' + wrong_captcha: '&cNeteisinga Captcha, naudokite : /captcha %captcha_code' + valid_captcha: '&cJusu captcha Teisinga!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -# TODO second: 'second' -# TODO seconds: 'seconds' -# TODO minute: 'minute' -# TODO minutes: 'minutes' -# TODO hour: 'hour' -# TODO hours: 'hours' -# TODO day: 'day' -# TODO days: 'days' +time: + second: '' + seconds: '' + minute: '' + minutes: '' + hour: '' + hours: '' + day: '' + days: '' diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index 14214c55f..b0e0f30b5 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -1,117 +1,136 @@ -# Registratie -reg_msg: '&cRegistreer met het commando: /register ' -usage_reg: '&cGebruik: /register ' -reg_only: 'Alleen voor geregistreerde spelers! Bezoek http://example.com om te registreren!' -kicked_admin_registered: 'Een administrator heeft je net geregistreerd; log alsjeblieft opnieuw in.' -registered: '&cSuccesvol geregistreerd!' -reg_disabled: '&cRegistratie is uitgeschakeld!' -user_regged: '&cJe hebt deze gebruikersnaam al geregistreerd!' +# Registration +registration: + disabled: '&cRegistratie is uitgeschakeld!' + name_taken: '&cJe hebt deze gebruikersnaam al geregistreerd!' + register_request: '&cRegistreer met het commando: /register ' + command_usage: '&cGebruik: /register ' + reg_only: 'Alleen voor geregistreerde spelers! Bezoek http://example.com om te registreren!' + success: '&cSuccesvol geregistreerd!' + kicked_admin_registered: 'Een administrator heeft je net geregistreerd; log alsjeblieft opnieuw in.' -# Wachtwoord fouten tijdens registratie -password_error: 'Jouw wachtwoorden komen niet overeen, controleer ze opnieuw!' -password_error_nick: '&fJe kunt je gebruikersnaam niet als wachtwoord gebruiken, kies een ander wachtwoord...' -password_error_unsafe: '&fDit wachtwoord is onveilig, kies een ander wachtwoord...' -password_error_chars: '&cJouw wachtwoord bevat ongeldige tekens. Toegestane karakters zijn: REG_EX' -pass_len: 'Jouw gekozen wachtwoord voldoet niet aan de minimum of maximum lengte, kies een ander wachtwoord...' +# Password errors on registration +password: + match_error: 'Jouw wachtwoorden komen niet overeen, controleer ze opnieuw!' + name_in_password: '&fJe kunt je gebruikersnaam niet als wachtwoord gebruiken, kies een ander wachtwoord...' + unsafe_password: '&fDit wachtwoord is onveilig, kies een ander wachtwoord...' + forbidden_characters: '&cJouw wachtwoord bevat ongeldige tekens. Toegestane karakters zijn: %valid_chars' + wrong_length: 'Jouw gekozen wachtwoord voldoet niet aan de minimum of maximum lengte, kies een ander wachtwoord...' -# Inloggen -usage_log: '&cGebruik: /login ' -wrong_pwd: '&cFout wachtwoord' -login: '&cSuccesvol ingelogd!' -login_msg: '&cLog in met: /login ' -timeout: 'Login time-out: het duurde te lang tot je inlogde.' +# Login +login: + command_usage: '&cGebruik: /login ' + wrong_password: '&cFout wachtwoord' + success: '' + login_request: '&cLog in met: /login ' + timeout_error: 'Login time-out: het duurde te lang tot je inlogde.' -# Fouten -unknown_user: '&cDeze gebruikersnaam is niet geregistreerd!' -denied_command: '&cJe moet ingelogd zijn om dit commando te gebruiken!' -denied_chat: '&cJe moet ingelogd zijn om te kunnen chatten!' -not_logged_in: '&cNiet ingelogd!' -tempban_max_logins: '&cJe bent tijdelijk gebanned omdat het inloggen te vaak mislukt is.' -max_reg: 'Je hebt het maximum aantal registraties overschreden (%reg_count/%max_acc: %reg_names).' -no_perm: '&cJe hebt geen rechten om deze actie uit te voeren!' -error: 'Er is een onverwachte fout opgetreden, neem contact op met een administrator!' -kick_forvip: '&cEen VIP-gebruiker heeft ingelogd toen de server vol was!' +# Errors +error: + denied_command: '&cJe moet ingelogd zijn om dit commando te gebruiken!' + denied_chat: '&cJe moet ingelogd zijn om te kunnen chatten!' + unregistered_user: '&cDeze gebruikersnaam is niet geregistreerd!' + not_logged_in: '&cNiet ingelogd!' + no_permission: '&cJe hebt geen rechten om deze actie uit te voeren!' + unexpected_error: '' + max_registration: 'Je hebt het maximum aantal registraties overschreden (%reg_count/%max_acc: %reg_names).' + logged_in: '&cJe bent al ingelogd!' + kick_for_vip: '&cEen VIP-gebruiker heeft ingelogd toen de server vol was!' + tempban_max_logins: '&cJe bent tijdelijk gebanned omdat het inloggen te vaak mislukt is.' # AntiBot -kick_antibot: 'AntiBot is aangezet! Wacht alsjeblieft enkele minuten voor je je met de server verbindt.' -antibot_auto_enabled: '[AuthMe] AntiBotMod automatisch aangezet vanwege te veel verbindingen!' -antibot_auto_disabled: '[AuthMe] AntiBotMod automatisch uitgezet na %m minuten!' +antibot: + kick_antibot: 'AntiBot is aangezet! Wacht alsjeblieft enkele minuten voor je je met de server verbindt.' + auto_enabled: '[AuthMe] AntiBotMod automatisch aangezet vanwege te veel verbindingen!' + auto_disabled: '[AuthMe] AntiBotMod automatisch uitgezet na %m minuten!' -# Overige berichten -unregistered: '&cSuccesvol afgemeld!' -accounts_owned_self: 'Je bezit %count accounts:' -accounts_owned_other: 'De speler %name heeft %count accounts:' -two_factor_create: '&2Je geheime code is %code. Je kunt hem scannen op %url' -recovery_code_sent: 'Een herstelcode voor je wachtwoord is naar je mailbox gestuurd.' -recovery_code_incorrect: 'De herstelcode is niet correct! Je kunt het nog %count keer proberen.' -recovery_tries_exceeded: 'Je hebt het maximum aantal pogingen om een herstel code in te vullen bereikt. Gebruik "/email recovery [e-mail]" om een nieuwe code te krijgen' -recovery_code_correct: 'De herstelcode is correct!' -recovery_change_password: 'Gebruik alsjeblieft het commando /email setpassword om je wachtwoord direct te veranderen.' -vb_nonActiv: 'Je account is nog niet geactiveerd, controleer je mailbox!' -usage_unreg: '&cGebruik: /unregister [wachtwoord]' -pwd_changed: '&cWachtwoord succesvol aangepast!' -logged_in: '&cJe bent al ingelogd!' -logout: '&2Je bent succesvol uitgelogd!' -reload: '&2De configuratie en database zijn succesvol herladen!' -usage_changepassword: '&cGebruik: /changepassword ' +# Unregister +unregister: + success: '&cSuccesvol afgemeld!' + command_usage: '&cGebruik: /unregister [wachtwoord]' -# Sessie berichten -invalid_session: '&cJouw IP-adres is veranderd en je sessie is verlopen!' -valid_session: '&2Ingelogd wegens sessie-herverbinding.' +# Other messages +misc: + account_not_activated: 'Je account is nog niet geactiveerd, controleer je mailbox!' + password_changed: '&cWachtwoord succesvol aangepast!' + logout: '&2Je bent succesvol uitgelogd!' + reload: '&2De configuratie en database zijn succesvol herladen!' + usage_change_password: '&cGebruik: /changepassword ' + two_factor_create: '&2Je geheime code is %code. Je kunt hem scannen op %url' + accounts_owned_self: 'Je bezit %count accounts:' + accounts_owned_other: 'De speler %name heeft %count accounts:' -# Fout berichten tijdens het inloggen -name_len: '&4Jouw gebruikersnaam is te kort of te lang!' -regex: '&4Jouw gebruikersnaam bevat ongeldige tekens. Toegestane karakters zijn: REG_EX' -country_banned: '&4Jouw land is gebanned op deze server!' -not_owner_error: 'Jij bent niet de eigenaar van dit account. Kies alsjeblieft een andere naam!' -kick_fullserver: '&4De server is vol, probeer het later opnieuw!' -same_nick: '&4Er is al iemand met jouw gebruikersnaam online.' -invalid_name_case: 'Verbind je alsjeblieft als %valid, niet als %invalid.' -same_ip_online: 'Een speler met hetzelfde IP-adres is al online!' +# Session messages +session: + valid_session: '&2Ingelogd wegens sessie-herverbinding.' + invalid_session: '&cJouw IP-adres is veranderd en je sessie is verlopen!' -# E-mail -usage_email_add: '&cGebruik: /email add ' -usage_email_change: '&cGebruik: /email change ' -usage_email_recovery: '&cGebruik: /email recovery ' -new_email_invalid: '&cOngeldig nieuw e-mailadres, probeer het opnieuw!' -old_email_invalid: '&cOngeldig oud e-mailadres, probeer het opnieuw!' -email_invalid: '&cOngeldig E-mailadres, probeer het opnieuw!' -email_added: '&2Het e-mailadres is succesvol toegevoegd aan je account!' -email_confirm: '&cVerifiëer je e-mailadres alsjeblieft!' -email_changed: '&2Het e-mailadres is succesvol veranderd!' -email_send: '&2Een herstel e-mail is verzonden! Check alsjeblieft je mailbox!' -email_show: '&2Jouw huidige e-mailadres is: %email' -incomplete_email_settings: 'Fout; er moeten nog enkele opties ingevuld worden om mails te kunnen sturen. Neem contact op met een administrator.' -email_already_used: '&4Dit e-mailadres wordt al gebruikt' -email_send_failure: 'De e-mail kon niet verzonden worden. Neem contact op met een administrator.' -show_no_email: '&2Je hebt nog geen e-mailadres toegevoegd aan dit account.' -add_email: '&3Voeg jouw e-mailadres alsjeblieft toe met: /email add ' -recovery_email: '&3Wachtwoord vergeten? Gebruik alsjeblieft het commando: /email recovery ' -change_password_expired: 'Je kunt je wachtwoord niet meer veranderen met dit commando.' -email_cooldown_error: '&cEr is recent al een e-mail verzonden. Je moet %time wachten voordat je een nieuw bericht kunt versturen.' +# Error messages when joining +on_join_validation: + same_ip_online: 'Een speler met hetzelfde IP-adres is al online!' + same_nick_online: '&4Er is al iemand met jouw gebruikersnaam online.' + name_length: '&4Jouw gebruikersnaam is te kort of te lang!' + characters_in_name: '&4Jouw gebruikersnaam bevat ongeldige tekens. Toegestane karakters zijn: %valid_chars' + kick_full_server: '&4De server is vol, probeer het later opnieuw!' + country_banned: '&4Jouw land is gebanned op deze server!' + not_owner_error: 'Jij bent niet de eigenaar van dit account. Kies alsjeblieft een andere naam!' + invalid_name_case: 'Verbind je alsjeblieft als %valid, niet als %invalid.' + +# Email +email: + add_email_request: '&3Voeg jouw e-mailadres alsjeblieft toe met: /email add ' + usage_email_add: '&cGebruik: /email add ' + usage_email_change: '&cGebruik: /email change ' + new_email_invalid: '&cOngeldig nieuw e-mailadres, probeer het opnieuw!' + old_email_invalid: '&cOngeldig oud e-mailadres, probeer het opnieuw!' + invalid: '&cOngeldig E-mailadres, probeer het opnieuw!' + added: '&2Het e-mailadres is succesvol toegevoegd aan je account!' + request_confirmation: '&cVerifiëer je e-mailadres alsjeblieft!' + changed: '&2Het e-mailadres is succesvol veranderd!' + email_show: '&2Jouw huidige e-mailadres is: %email' + no_email_for_account: '&2Je hebt nog geen e-mailadres toegevoegd aan dit account.' + already_used: '&4Dit e-mailadres wordt al gebruikt' + incomplete_settings: 'Fout; er moeten nog enkele opties ingevuld worden om mails te kunnen sturen. Neem contact op met een administrator.' + send_failure: 'De e-mail kon niet verzonden worden. Neem contact op met een administrator.' + change_password_expired: 'Je kunt je wachtwoord niet meer veranderen met dit commando.' + email_cooldown_error: '&cEr is recent al een e-mail verzonden. Je moet %time wachten voordat je een nieuw bericht kunt versturen.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Wachtwoord vergeten? Gebruik alsjeblieft het commando: /email recovery ' + command_usage: '&cGebruik: /email recovery ' + email_sent: '&2Een herstel e-mail is verzonden! Check alsjeblieft je mailbox!' + code: + code_sent: 'Een herstelcode voor je wachtwoord is naar je mailbox gestuurd.' + incorrect: 'De herstelcode is niet correct! Je kunt het nog %count keer proberen.' + tries_exceeded: 'Je hebt het maximum aantal pogingen om een herstel code in te vullen bereikt. Gebruik "/email recovery [e-mail]" om een nieuwe code te krijgen' + correct: 'De herstelcode is correct!' + change_password: 'Gebruik alsjeblieft het commando /email setpassword om je wachtwoord direct te veranderen.' # Captcha -usage_captcha: '&3Om in te loggen moet je een captcha-code oplossen, gebruik het commando: /captcha ' -wrong_captcha: '&cVerkeerde captcha-code, typ alsjeblieft "/captcha THE_CAPTCHA" in de chat!' -valid_captcha: '&2De captcha-code is geldig!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Om in te loggen moet je een captcha-code oplossen, gebruik het commando: /captcha %captcha_code' + wrong_captcha: '&cVerkeerde captcha-code, typ alsjeblieft "/captcha %captcha_code" in de chat!' + valid_captcha: '&2De captcha-code is geldig!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'seconde' -seconds: 'seconden' -minute: 'minuut' -minutes: 'minuten' -hour: 'uur' -hours: 'uren' -day: 'dag' -days: 'dagen' +time: + second: 'seconde' + seconds: 'seconden' + minute: 'minuut' + minutes: 'minuten' + hour: 'uur' + hours: 'uren' + day: 'dag' + days: 'dagen' diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 7909eb966..755c9cca7 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&2Proszę się zarejestrować przy użyciu &6/register ' -usage_reg: '&4Użycie: /register ' -reg_only: '&fTylko zarejestrowani użytkownicy mają do tego dostęp!' -kicked_admin_registered: 'Administrator zarejestrował Ciebie, możesz się zalogować.' -registered: '&aPomyślnie zarejestrowany!' -reg_disabled: '&4Rejestracja jest wyłączona.' -user_regged: '&4Gracz już jest zarejestrowany.' +registration: + disabled: '&4Rejestracja jest wyłączona.' + name_taken: '&4Gracz już jest zarejestrowany.' + register_request: '&2Proszę się zarejestrować przy użyciu &6/register ' + command_usage: '&4Użycie: /register ' + reg_only: '&fTylko zarejestrowani użytkownicy mają do tego dostęp!' + success: '&aPomyślnie zarejestrowany!' + kicked_admin_registered: 'Administrator zarejestrował Ciebie, możesz się zalogować.' # Password errors on registration -password_error: '&fHasło niepoprawne!' -password_error_nick: '&cNie możesz używać nicku jako hasła, wybierz inne...' -password_error_unsafe: '&cTwoje hasło nie jest bezpieczne, wybierz inne...' -password_error_chars: '&4W twoim haśle występują niedozwolone znaki, dozwolone znaki to: REG_EX' -pass_len: '&fTwoje hasło jest za krótkie lub za długie! Spróbuj ponownie...' +password: + match_error: '&fHasło niepoprawne!' + name_in_password: '&cNie możesz używać nicku jako hasła, wybierz inne...' + unsafe_password: '&cTwoje hasło nie jest bezpieczne, wybierz inne...' + forbidden_characters: '&4W twoim haśle występują niedozwolone znaki, dozwolone znaki to: %valid_chars' + wrong_length: '&fTwoje hasło jest za krótkie lub za długie! Spróbuj ponownie...' # Login -usage_log: '&cUżycie: /login hasło' -wrong_pwd: '&cNiepoprawne hasło.' -login: '&aHasło zaakceptowane!' -login_msg: '&2Proszę się zalogować przy użyciu &6/login ' -timeout: '&cUpłynął limit czasu zalogowania' +login: + command_usage: '&cUżycie: /login hasło' + wrong_password: '&cNiepoprawne hasło.' + success: '' + login_request: '&2Proszę się zalogować przy użyciu &6/login ' + timeout_error: '&cUpłynął limit czasu zalogowania' # Errors -unknown_user: '&fGracz nie jest zarejestrowany.' -denied_command: '&cAby użyć tej komendy musisz się zalogować!' -denied_chat: '&cAby pisać na chacie musisz się zalogować!' -not_logged_in: '&4Nie jesteś zalogowany!' -tempban_max_logins: '&cZostałeś tymczasowo zbanowany za dużą liczbę nieudanych logowań!' -max_reg: '&cPrzekroczyłeś limit zarejestrowanych kont na serwerze &8(&e%reg_count/%max_acc %reg_names&8) &cdla twojego połączenia.' -no_perm: '&4Nie posiadasz wymaganych uprawnień.' -error: '&fWystąpił błąd, prosimy skontaktować się z administracją serwera.' -kick_forvip: '&cGracz VIP dołączył do gry!' +error: + denied_command: '&cAby użyć tej komendy musisz się zalogować!' + denied_chat: '&cAby pisać na chacie musisz się zalogować!' + unregistered_user: '&fGracz nie jest zarejestrowany.' + not_logged_in: '&4Nie jesteś zalogowany!' + no_permission: '&4Nie posiadasz wymaganych uprawnień.' + unexpected_error: '' + max_registration: '&cPrzekroczyłeś limit zarejestrowanych kont na serwerze &8(&e%reg_count/%max_acc %reg_names&8) &cdla twojego połączenia.' + logged_in: '&fJesteś już zalogowany!' + kick_for_vip: '&cGracz VIP dołączył do gry!' + tempban_max_logins: '&cZostałeś tymczasowo zbanowany za dużą liczbę nieudanych logowań!' # AntiBot -kick_antibot: '&cAntyBot został włączony, musisz poczekać minutę przed dołączeniem do serwera.' -antibot_auto_enabled: '&4[AntiBot] &aAntyBot włączony z powodu dużej liczby połączeń!' -antibot_auto_disabled: '&2[AntiBot] &aAntyBot zostanie wyłączony za &7%m &aminut!' +antibot: + kick_antibot: '&cAntyBot został włączony, musisz poczekać minutę przed dołączeniem do serwera.' + auto_enabled: '&4[AntiBot] &aAntyBot włączony z powodu dużej liczby połączeń!' + auto_disabled: '&2[AntiBot] &aAntyBot zostanie wyłączony za &7%m &aminut!' + +# Unregister +unregister: + success: '&4Pomyślnie wyrejestrowany!' + command_usage: '&cUżycie: /unregister hasło' # Other messages -unregistered: '&4Pomyślnie wyrejestrowany!' -accounts_owned_self: '&7Posiadasz %count kont:' -accounts_owned_other: '&7Gracz %name posiada %count kont:' -two_factor_create: '&2Twój sekretny kod to %code. Możesz zeskanować go tutaj: %url' -recovery_code_sent: 'Kod odzyskiwania hasła został wysłany na adres e-mail przypisany do konta.' -recovery_code_incorrect: '&cKod odzyskiwania hasła jest błędny! &4Pozostałe próby: %count.' -recovery_tries_exceeded: 'Osiągnięto limit prób wpisania kodu odzyskiwania. Wpisz /email recovery [e-mail] aby wygenerować nowy kod.' -recovery_code_correct: '&aKod odzyskiwania został wpisany pomyślnie!' -recovery_change_password: 'Wpisz komendę /email setpassword aby zmienić hasło do konta.' -vb_nonActiv: '&fTwoje konto nie zostało aktywowane! Sprawdź maila.' -usage_unreg: '&cUżycie: /unregister hasło' -pwd_changed: '&fHasło zostało zmienione!' -logged_in: '&fJesteś już zalogowany!' -logout: '&cPomyślnie wylogowany' -reload: '&fKonfiguracja bazy danych została przeładowana.' -usage_changepassword: '&fUżycie: /changepassword ' +misc: + account_not_activated: '&fTwoje konto nie zostało aktywowane! Sprawdź maila.' + password_changed: '&fHasło zostało zmienione!' + logout: '&cPomyślnie wylogowany' + reload: '&fKonfiguracja bazy danych została przeładowana.' + usage_change_password: '&fUżycie: /changepassword ' + two_factor_create: '&2Twój sekretny kod to %code. Możesz zeskanować go tutaj: %url' + accounts_owned_self: '&7Posiadasz %count kont:' + accounts_owned_other: '&7Gracz %name posiada %count kont:' # Session messages -invalid_session: '&fSesja logowania zakończona!' -valid_session: '&aZalogowano automatycznie z powodu sesji logowania.' +session: + valid_session: '&aZalogowano automatycznie z powodu sesji logowania.' + invalid_session: '&fSesja logowania zakończona!' # Error messages when joining -name_len: '&cTwoje konto ma za długa bądź za krotką nazwę.' -regex: '&cTwoje konto ma w nazwie niedozwolone znaki. Dozwolone znaki: REG_EX' -country_banned: '&4Ten kraj jest zbanowany na tym serwerze' -not_owner_error: '&cNie jesteś właścicielem tego konta, wybierz inny nick!' -kick_fullserver: '&cSerwer jest teraz zapełniony, przepraszamy!' -same_nick: '&fTen nick już gra na serwerze!' -invalid_name_case: '&cPowinieneś dołączyć do serwera z nicku %valid, a nie %invalid.' -same_ip_online: '&cGracz z takim samym adresem ip jest aktualnie w grze!' +on_join_validation: + same_ip_online: '&cGracz z takim samym adresem ip jest aktualnie w grze!' + same_nick_online: '&fTen nick już gra na serwerze!' + name_length: '&cTwoje konto ma za długa bądź za krotką nazwę.' + characters_in_name: '&cTwoje konto ma w nazwie niedozwolone znaki. Dozwolone znaki: %valid_chars' + kick_full_server: '&cSerwer jest teraz zapełniony, przepraszamy!' + country_banned: '&4Ten kraj jest zbanowany na tym serwerze' + not_owner_error: '&cNie jesteś właścicielem tego konta, wybierz inny nick!' + invalid_name_case: '&cPowinieneś dołączyć do serwera z nicku %valid, a nie %invalid.' # Email -usage_email_add: '&fWpisz: /email add ' -usage_email_change: '&fWpisz: /email change ' -usage_email_recovery: '&fWpisz: /email recovery ' -new_email_invalid: '[AuthMe] Nowy e-mail niepoprawny!' -old_email_invalid: '[AuthMe] Stary e-mail niepoprawny!' -email_invalid: '[AuthMe] Nieprawidłowy adres e-mail.' -email_added: '[AuthMe] E-mail został dodany do Twojego konta!' -email_confirm: '[AuthMe] Potwierdź swój adres e-mail!' -email_changed: '[AuthMe] E-mail został zmieniony!' -email_send: '[AuthMe] E-mail z odzyskaniem wysłany!' -email_show: '&2Twój aktualny adres e-mail to: &f%email' -incomplete_email_settings: 'Błąd: Nie wszystkie opcje odpowiedzialne za wysyłanie e-maili zostały skonfigurowane. Skontaktuj się z administracją.' -email_already_used: '&4Ten adres e-mail jest aktualnie używany!' -email_send_failure: 'Nie można wysłać e-maila. Skontaktuj się z administracją.' -show_no_email: '&2Nie posiadasz adresu e-mail przypisanego do tego konta.' -add_email: '&cProszę dodać swój e-mail: /email add ' -recovery_email: '&cZapomniałeś hasła? Proszę użyj komendy /email recovery ' -change_password_expired: 'Nie zmienisz już hasła przy użyciu tej komendy.' -email_cooldown_error: '&cE-mail został wysłany, musisz poczekać %time przed wysłaniem następnego.' +email: + add_email_request: '&cProszę dodać swój e-mail: /email add ' + usage_email_add: '&fWpisz: /email add ' + usage_email_change: '&fWpisz: /email change ' + new_email_invalid: '[AuthMe] Nowy e-mail niepoprawny!' + old_email_invalid: '[AuthMe] Stary e-mail niepoprawny!' + invalid: '[AuthMe] Nieprawidłowy adres e-mail.' + added: '[AuthMe] E-mail został dodany do Twojego konta!' + request_confirmation: '[AuthMe] Potwierdź swój adres e-mail!' + changed: '[AuthMe] E-mail został zmieniony!' + email_show: '&2Twój aktualny adres e-mail to: &f%email' + no_email_for_account: '&2Nie posiadasz adresu e-mail przypisanego do tego konta.' + already_used: '&4Ten adres e-mail jest aktualnie używany!' + incomplete_settings: 'Błąd: Nie wszystkie opcje odpowiedzialne za wysyłanie e-maili zostały skonfigurowane. Skontaktuj się z administracją.' + send_failure: 'Nie można wysłać e-maila. Skontaktuj się z administracją.' + change_password_expired: 'Nie zmienisz już hasła przy użyciu tej komendy.' + email_cooldown_error: '&cE-mail został wysłany, musisz poczekać %time przed wysłaniem następnego.' + +# Password recovery by email +recovery: + forgot_password_hint: '&cZapomniałeś hasła? Proszę użyj komendy /email recovery ' + command_usage: '&fWpisz: /email recovery ' + email_sent: '[AuthMe] E-mail z odzyskaniem wysłany!' + code: + code_sent: 'Kod odzyskiwania hasła został wysłany na adres e-mail przypisany do konta.' + incorrect: '&cKod odzyskiwania hasła jest błędny! &4Pozostałe próby: %count.' + tries_exceeded: 'Osiągnięto limit prób wpisania kodu odzyskiwania. Wpisz /email recovery [e-mail] aby wygenerować nowy kod.' + correct: '&aKod odzyskiwania został wpisany pomyślnie!' + change_password: 'Wpisz komendę /email setpassword aby zmienić hasło do konta.' # Captcha -usage_captcha: '&cWpisz: /captcha ' -wrong_captcha: '&cZły kod, proszę wpisać: /captcha THE_CAPTCHA' -valid_captcha: '&cTwój kod jest nieprawidłowy!' -captcha_for_registration: '&cAby się zarejestrować, musisz przepisać kod captacha. Użyj komendy: /captcha ' -register_captcha_valid: '&2Prawidłowy kod captacha! Teraz możesz się zarejestrować komendą /register' +captcha: + usage_captcha: '&cWpisz: /captcha %captcha_code' + wrong_captcha: '&cZły kod, proszę wpisać: /captcha %captcha_code' + valid_captcha: '&cTwój kod jest nieprawidłowy!' + captcha_for_registration: '&cAby się zarejestrować, musisz przepisać kod captacha. Użyj komendy: /captcha %captcha_code' + register_captcha_valid: '&2Prawidłowy kod captacha! Teraz możesz się zarejestrować komendą /register' # Verification code -verification_code_required: '&3Ta komenda wymaga weryfikacji przez e-mail! Sprawdź swoją skrzynkę odbiorczą i postępuj zgodnie z instrukcjami w wiadomości.' -usage_verification_code: '&cUżycie: /verification ' -incorrect_verification_code: '&cNieprawidłowy kod z e-maila, wpisz komende "/verification ". W miejscu wpisz swój kod z emaila.' -verification_code_verified: '&2Twoja tożsamość została zweryfikowana! Możesz teraz wykonywać wszystkie komendy w bieżącej sesji!' -verification_code_already_verified: '&2Możesz już wykonać każdą komende w bieżącej sesji!' -verification_code_expired: '&3Twój kod wygasł! Wykonaj ponownie komende, aby uzyskać nowy kod!' -verification_code_email_needed: '&3Aby zweryfikować swoją tożsamość, musisz połączyć adres e-mail z kontem!' +verification: + code_required: '&3Ta komenda wymaga weryfikacji przez e-mail! Sprawdź swoją skrzynkę odbiorczą i postępuj zgodnie z instrukcjami w wiadomości.' + command_usage: '&cUżycie: /verification ' + incorrect_code: '&cNieprawidłowy kod z e-maila, wpisz komende "/verification ". W miejscu wpisz swój kod z emaila.' + success: '&2Twoja tożsamość została zweryfikowana! Możesz teraz wykonywać wszystkie komendy w bieżącej sesji!' + already_verified: '&2Możesz już wykonać każdą komende w bieżącej sesji!' + code_expired: '&3Twój kod wygasł! Wykonaj ponownie komende, aby uzyskać nowy kod!' + email_needed: '&3Aby zweryfikować swoją tożsamość, musisz połączyć adres e-mail z kontem!' # Time units -second: 'sekundy' -seconds: 'sekund' -minute: 'minuty' -minutes: 'minut' -hour: 'godziny' -hours: 'godzin' -day: 'dzień' -days: 'dni' +time: + second: 'sekundy' + seconds: 'sekund' + minute: 'minuty' + minutes: 'minut' + hour: 'godziny' + hours: 'godzin' + day: 'dzień' + days: 'dni' diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index ac9e9af67..df5604152 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -1,119 +1,136 @@ # Registration -reg_msg: '&cPor favor registe-se com "/register "' -usage_reg: '&cUse: /register ' -reg_only: '&fApenas jogadores registados podem entrar no servidor! Visite http://example.com para se registar' -kicked_admin_registered: 'Um administrador registou-te, por favor entre novamente' -registered: '&cRegistado com sucesso!' -reg_disabled: '&cRegisto de novos utilizadores desactivado' -user_regged: '&cUtilizador já registado' +registration: + disabled: '&cRegisto de novos utilizadores desactivado' + name_taken: '&cUtilizador já registado' + register_request: '&cPor favor registe-se com "/register "' + command_usage: '&cUse: /register ' + reg_only: '&fApenas jogadores registados podem entrar no servidor! Visite http://example.com para se registar' + success: '&cRegistado com sucesso!' + kicked_admin_registered: 'Um administrador registou-te, por favor entre novamente' # Password errors on registration -password_error: '&fAs passwords não coincidem' -password_error_nick: '&cNão pode o usar seu nome como senha, por favor, escolha outra ...' -password_error_unsafe: '&cA senha escolhida não é segura, por favor, escolha outra ...' -password_error_chars: '&4Sua senha contém caracteres ilegais. Caracteres permitidos: REG_EX' -pass_len: '&fPassword demasiado curta ou longa! Por favor escolhe outra outra!' +password: + match_error: '&fAs passwords não coincidem' + name_in_password: '&cNão pode o usar seu nome como senha, por favor, escolha outra ...' + unsafe_password: '&cA senha escolhida não é segura, por favor, escolha outra ...' + forbidden_characters: '&4Sua senha contém caracteres ilegais. Caracteres permitidos: %valid_chars' + wrong_length: '&fPassword demasiado curta ou longa! Por favor escolhe outra outra!' # Login -usage_log: '&cUse: /login ' -wrong_pwd: '&cPassword errada!' -login: '&bAutenticado com sucesso!' -login_msg: '&cIdentifique-se com "/login "' -timeout: '&fExcedeu o tempo para autenticação' +login: + command_usage: '&cUse: /login ' + wrong_password: '&cPassword errada!' + success: '' + login_request: '&cIdentifique-se com "/login "' + timeout_error: '&fExcedeu o tempo para autenticação' # Errors -unknown_user: '&cUsername não registado' -denied_command: '&cPara utilizar este comando é necessário estar logado!' -denied_chat: '&cPara usar o chat deve estar logado!' -not_logged_in: '&cNão autenticado!' -tempban_max_logins: '&cVocê foi temporariamente banido por falhar muitas vezes o login.' -# TODO: Missing tags %reg_names -max_reg: '&cAtingiu o numero máximo de %reg_count contas registas, maximo de contas %max_acc' -no_perm: '&cSem Permissões' -error: '&fOcorreu um erro; Por favor contacte um administrador' -kick_forvip: '&cUm jogador VIP entrou no servidor cheio!' +error: + denied_command: '&cPara utilizar este comando é necessário estar logado!' + denied_chat: '&cPara usar o chat deve estar logado!' + unregistered_user: '&cUsername não registado' + not_logged_in: '&cNão autenticado!' + no_permission: '&cSem Permissões' + unexpected_error: '' + max_registration: '&cAtingiu o numero máximo de %reg_count contas registas, maximo de contas %max_acc' + logged_in: '&cJá se encontra autenticado!' + kick_for_vip: '&cUm jogador VIP entrou no servidor cheio!' + tempban_max_logins: '&cVocê foi temporariamente banido por falhar muitas vezes o login.' # AntiBot -kick_antibot: 'Modo de protecção anti-Bot está habilitado! Tem que espere alguns minutos antes de entrar no servidor.' -antibot_auto_enabled: '[AuthMe] AntiBotMod activado automaticamente devido a um aumento anormal de tentativas de ligação!' -antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automaticamente após %m minutos, esperamos que a invasão tenha parado' +antibot: + kick_antibot: 'Modo de protecção anti-Bot está habilitado! Tem que espere alguns minutos antes de entrar no servidor.' + auto_enabled: '[AuthMe] AntiBotMod activado automaticamente devido a um aumento anormal de tentativas de ligação!' + auto_disabled: '[AuthMe] AntiBotMod desactivado automaticamente após %m minutos, esperamos que a invasão tenha parado' + +# Unregister +unregister: + success: '&cRegisto eliminado com sucesso!' + command_usage: '&cUse: /unregister ' # Other messages -unregistered: '&cRegisto eliminado com sucesso!' -accounts_owned_self: 'Você possui %count contas:' -accounts_owned_other: 'O jogador %name possui %count contas:' -two_factor_create: '&2O seu código secreto é o %code. Você pode verificá-lo a partir daqui %url' -recovery_code_sent: 'O codigo para redefinir a senha foi enviado para o seu e-mail.' -# TODO: Missing tags %count -recovery_code_incorrect: 'O codigo de recuperação está incorreto! Use "/email recovery [email]" para gerar um novo' -recovery_tries_exceeded: 'Você excedeu o numero maximo de tentativas para introduzir o codigo de recuperação. Use "/email recovery [email]" para gerar um novo.' -recovery_code_correct: 'O codigo de recuperação foi introduzido corretamente!' -recovery_change_password: 'Por favor use o comando /email setpassword para mudar a password imediatamente.' -vb_nonActiv: '&fA sua conta não foi ainda activada, verifique o seu email onde irá receber indicações para activação de conta. ' -usage_unreg: '&cUse: /unregister ' -pwd_changed: '&cPassword alterada!' -logged_in: '&cJá se encontra autenticado!' -logout: '&cSaida com sucesso' -reload: '&fConfiguração e base de dados foram recarregadas' -usage_changepassword: '&fUse: /changepassword ' +misc: + account_not_activated: '&fA sua conta não foi ainda activada, verifique o seu email onde irá receber indicações para activação de conta. ' + password_changed: '&cPassword alterada!' + logout: '&cSaida com sucesso' + reload: '&fConfiguração e base de dados foram recarregadas' + usage_change_password: '&fUse: /changepassword ' + two_factor_create: '&2O seu código secreto é o %code. Você pode verificá-lo a partir daqui %url' + accounts_owned_self: 'Você possui %count contas:' + accounts_owned_other: 'O jogador %name possui %count contas:' # Session messages -invalid_session: '&fDados de sessão não correspondem. Por favor aguarde o fim da sessão' -valid_session: '&cSessão válida' +session: + valid_session: '&cSessão válida' + invalid_session: '&fDados de sessão não correspondem. Por favor aguarde o fim da sessão' # Error messages when joining -name_len: '&cO seu nick é demasiado curto ou muito longo.' -regex: '&cO seu nickname contém caracteres não permitidos. Permitido: REG_EX' -country_banned: 'O seu país está banido deste servidor' -not_owner_error: 'Não é o proprietário da conta. Por favor, escolha outro nome!' -kick_fullserver: '&cO servidor está actualmente cheio, lamentamos!' -same_nick: '&fO mesmo nickname já se encontra a jogar no servidor' -invalid_name_case: 'Deve se juntar usando nome de usuário %valid, não %invalid.' -same_ip_online: 'Um jogador com o mesmo IP já está em jogo!' +on_join_validation: + same_ip_online: 'Um jogador com o mesmo IP já está em jogo!' + same_nick_online: '&fO mesmo nickname já se encontra a jogar no servidor' + name_length: '&cO seu nick é demasiado curto ou muito longo.' + characters_in_name: '&cO seu nickname contém caracteres não permitidos. Permitido: %valid_chars' + kick_full_server: '&cO servidor está actualmente cheio, lamentamos!' + country_banned: 'O seu país está banido deste servidor' + not_owner_error: 'Não é o proprietário da conta. Por favor, escolha outro nome!' + invalid_name_case: 'Deve se juntar usando nome de usuário %valid, não %invalid.' # Email -usage_email_add: '&fUse: /email add ' -usage_email_change: '&fUse: /email change ' -usage_email_recovery: '&fUse: /email recovery ' -new_email_invalid: 'Novo email inválido!' -old_email_invalid: 'Email antigo inválido!' -email_invalid: 'Email inválido!' -email_added: 'Email adicionado com sucesso!' -email_confirm: 'Confirme o seu email!' -email_changed: 'Email alterado com sucesso!' -email_send: 'Nova palavra-passe enviada para o seu email!' -email_show: '&2O seu endereço de email atual é &f%email' -incomplete_email_settings: 'Erro: nem todas as definições necessarias para enviar email foram preenchidas. Por favor contate um administrador.' -email_already_used: '&4O endereço de e-mail já está sendo usado' -email_send_failure: 'Não foi possivel enviar o email. Por favor contate um administrador.' -show_no_email: '&2Você atualmente não tem um endereço de email associado a essa conta.' -add_email: '&cPor favor adicione o seu email com : /email add ' -recovery_email: '&cPerdeu a sua password? Para a recuperar escreva /email recovery ' -change_password_expired: 'Você não pode mais alterar a sua password usando este comando.' -email_cooldown_error: '&cUm email já foi enviado recentemente.Por favor, espere %time antes de enviar novamente' +email: + add_email_request: '&cPor favor adicione o seu email com : /email add ' + usage_email_add: '&fUse: /email add ' + usage_email_change: '&fUse: /email change ' + new_email_invalid: 'Novo email inválido!' + old_email_invalid: 'Email antigo inválido!' + invalid: 'Email inválido!' + added: 'Email adicionado com sucesso!' + request_confirmation: 'Confirme o seu email!' + changed: 'Email alterado com sucesso!' + email_show: '&2O seu endereço de email atual é &f%email' + no_email_for_account: '&2Você atualmente não tem um endereço de email associado a essa conta.' + already_used: '&4O endereço de e-mail já está sendo usado' + incomplete_settings: 'Erro: nem todas as definições necessarias para enviar email foram preenchidas. Por favor contate um administrador.' + send_failure: 'Não foi possivel enviar o email. Por favor contate um administrador.' + change_password_expired: 'Você não pode mais alterar a sua password usando este comando.' + email_cooldown_error: '&cUm email já foi enviado recentemente.Por favor, espere %time antes de enviar novamente' + +# Password recovery by email +recovery: + forgot_password_hint: '&cPerdeu a sua password? Para a recuperar escreva /email recovery ' + command_usage: '&fUse: /email recovery ' + email_sent: 'Nova palavra-passe enviada para o seu email!' + code: + code_sent: 'O codigo para redefinir a senha foi enviado para o seu e-mail.' + incorrect: 'O codigo de recuperação está incorreto! Use "/email recovery [email]" para gerar um novo' + tries_exceeded: 'Você excedeu o numero maximo de tentativas para introduzir o codigo de recuperação. Use "/email recovery [email]" para gerar um novo.' + correct: 'O codigo de recuperação foi introduzido corretamente!' + change_password: 'Por favor use o comando /email setpassword para mudar a password imediatamente.' # Captcha -usage_captcha: '&cPrecisa digitar um captcha, escreva: /captcha ' -wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha THE_CAPTCHA' -valid_captcha: '&cO seu captcha é válido!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&cPrecisa digitar um captcha, escreva: /captcha %captcha_code' + wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha %captcha_code' + valid_captcha: '&cO seu captcha é válido!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'segundo' -seconds: 'segundos' -minute: 'minuto' -minutes: 'minutos' -hour: 'hora' -hours: 'horas' -day: 'dia' -days: 'dias' +time: + second: 'segundo' + seconds: 'segundos' + minute: 'minuto' + minutes: 'minutos' + hour: 'hora' + hours: 'horas' + day: 'dia' + days: 'dias' diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml index 0c20f95f9..11d77a1d9 100644 --- a/src/main/resources/messages/messages_ro.yml +++ b/src/main/resources/messages/messages_ro.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&3Te rugam sa te inregistrezi folosind comanda "/register "' -usage_reg: '&cFoloseste comanda: /register ' -reg_only: '&4Doar jucatori inregistrati pot intra pe server! Te rugam foloseste http://example.com pentru a te inregistra!' -kicked_admin_registered: 'Un administrator tocmai te-a inregistrat, te rog autentifica-te din nou.' -registered: '&2Te-ai inregistrat cu succes!' -reg_disabled: '&cInregistrarea in joc nu este activata!' -user_regged: '&cCineva este inregistrat cu acest nume!' +registration: + disabled: '&cInregistrarea in joc nu este activata!' + name_taken: '&cCineva este inregistrat cu acest nume!' + register_request: '&3Te rugam sa te inregistrezi folosind comanda "/register "' + command_usage: '&cFoloseste comanda: /register ' + reg_only: '&4Doar jucatori inregistrati pot intra pe server! Te rugam foloseste http://example.com pentru a te inregistra!' + success: '&2Te-ai inregistrat cu succes!' + kicked_admin_registered: 'Un administrator tocmai te-a inregistrat, te rog autentifica-te din nou.' # Password errors on registration -password_error: '&cParolele nu corespund, verifica-le din nou!' -password_error_nick: '&cNu poti folosi numele ca si parola. Te rugam sa folosesti alta parola...' -password_error_unsafe: '&cParola aleasa nu este sigura. Te rugam sa folosesti alta parola...' -password_error_chars: '&4Parola ta contine caractere nepermise. Caractere permise: REG_EX' -pass_len: '&cParola ta este prea scurta pentru a te inregistra! Te rugam incearca alta parola!' +password: + match_error: '&cParolele nu corespund, verifica-le din nou!' + name_in_password: '&cNu poti folosi numele ca si parola. Te rugam sa folosesti alta parola...' + unsafe_password: '&cParola aleasa nu este sigura. Te rugam sa folosesti alta parola...' + forbidden_characters: '&4Parola ta contine caractere nepermise. Caractere permise: %valid_chars' + wrong_length: '&cParola ta este prea scurta pentru a te inregistra! Te rugam incearca alta parola!' # Login -usage_log: '&cFoloseste comanda "/login " pentru a te autentifica.' -wrong_pwd: '&cParola gresita!' -login: '&2Te-ai autentificat cu succes!' -login_msg: '&cTe rugam sa te autentifici folosind comanda "/login "' -timeout: '&4A expirat timpul de autentificare si ai fost dat afara de server, te rugam incearca din nou!' +login: + command_usage: '&cFoloseste comanda "/login " pentru a te autentifica.' + wrong_password: '&cParola gresita!' + success: '' + login_request: '&cTe rugam sa te autentifici folosind comanda "/login "' + timeout_error: '&4A expirat timpul de autentificare si ai fost dat afara de server, te rugam incearca din nou!' # Errors -unknown_user: '&cAcest jucator nu este inregistrat!' -denied_command: '&cPentru a folosi comanda trebuie sa te autentifici!' -denied_chat: 'Pentru a folosi chat-ul trebuie sa te autentifici!' -not_logged_in: '&cNu te-ai autentificat!' -tempban_max_logins: '&cAi fost interzis temporar deoarece ai esuat sa te autentifici de prea multe ori.' -max_reg: '&cTe-ai inregistrat cu prea multe conturi pe acelasi ip! (%reg_count/%max_acc %reg_names)' -no_perm: '&4Nu ai permisiunea!' -error: '&4A aparut o eroare, te rugam contacteaza un administrator!' -kick_forvip: '&3Un V.I.P a intrat pe server cand era plin!' +error: + denied_command: '&cPentru a folosi comanda trebuie sa te autentifici!' + denied_chat: 'Pentru a folosi chat-ul trebuie sa te autentifici!' + unregistered_user: '&cAcest jucator nu este inregistrat!' + not_logged_in: '&cNu te-ai autentificat!' + no_permission: '&4Nu ai permisiunea!' + unexpected_error: '' + max_registration: '&cTe-ai inregistrat cu prea multe conturi pe acelasi ip! (%reg_count/%max_acc %reg_names)' + logged_in: '&cEsti deja autentificat!' + kick_for_vip: '&3Un V.I.P a intrat pe server cand era plin!' + tempban_max_logins: '&cAi fost interzis temporar deoarece ai esuat sa te autentifici de prea multe ori.' # AntiBot -kick_antibot: 'Protectia AntiBot este activata! Trebuie sa astepti cateva minute pentru a intra pe server.' -antibot_auto_enabled: '&4[Protectie AntiBot] AntiBot-ul a fost activat din cauza numarului mare de conexiuni!' -antibot_auto_disabled: '&2[Protectie AntiBot] AntiBot-ul a fost dezactivat dupa %m minute!' +antibot: + kick_antibot: 'Protectia AntiBot este activata! Trebuie sa astepti cateva minute pentru a intra pe server.' + auto_enabled: '&4[Protectie AntiBot] AntiBot-ul a fost activat din cauza numarului mare de conexiuni!' + auto_disabled: '&2[Protectie AntiBot] AntiBot-ul a fost dezactivat dupa %m minute!' + +# Unregister +unregister: + success: '&cTi-ai sters contul cu succes!' + command_usage: '&cFoloseste comanda: /unregister ' # Other messages -unregistered: '&cTi-ai sters contul cu succes!' -accounts_owned_self: 'Detii %count conturi:' -accounts_owned_other: 'Jucatorul %name are %count conturi:' -two_factor_create: '&2Codul tau secret este %code. Il poti scana de aici %url' -recovery_code_sent: 'Un cod de recuperare a parolei a fost trimis catre email-ul tau.' -recovery_code_incorrect: 'Codul de recuperare nu este corect! Mai ai %count incercari ramase.' -recovery_tries_exceeded: 'Ai atins numarul maxim de incercari pentru introducerea codului de recuperare. Foloseste "/email recovery [email]" pentru a genera unul nou.' -recovery_code_correct: 'Cod de recuperare corect!' -recovery_change_password: 'Foloseste imediat comanda /email setpassword pentru a-ti schimba parola.' -vb_nonActiv: '&cContul tau nu este activat, te rugam verifica-ti email-ul!' -usage_unreg: '&cFoloseste comanda: /unregister ' -pwd_changed: '&2Parola a fost salvata cu succes!' -logged_in: '&cEsti deja autentificat!' -logout: '&2Te-ai dezautentificat cu succes!' -reload: '&2Configuratiile si baza de date sau reincarcat corect!' -usage_changepassword: '&cFoloseste comanda: /changepassword ' +misc: + account_not_activated: '&cContul tau nu este activat, te rugam verifica-ti email-ul!' + password_changed: '&2Parola a fost salvata cu succes!' + logout: '&2Te-ai dezautentificat cu succes!' + reload: '&2Configuratiile si baza de date sau reincarcat corect!' + usage_change_password: '&cFoloseste comanda: /changepassword ' + two_factor_create: '&2Codul tau secret este %code. Il poti scana de aici %url' + accounts_owned_self: 'Detii %count conturi:' + accounts_owned_other: 'Jucatorul %name are %count conturi:' # Session messages -invalid_session: '&cIP-ul tau a fost schimbat si sesiunea ta a expirat!' -valid_session: '&2Ai fost reconectat automat.' +session: + valid_session: '&2Ai fost reconectat automat.' + invalid_session: '&cIP-ul tau a fost schimbat si sesiunea ta a expirat!' # Error messages when joining -name_len: '&4Numele tau este prea scurt pentru a te putea conecta!' -regex: '&4Numele tau contine caractere ilegale. Caractere permise: REG_EX' -country_banned: '&4Tara ta este interzisa pe acest server!' -not_owner_error: 'Tu nu esti detinatorul acestui cont. Te rugam alege alt nume!' -kick_fullserver: '&4Server-ul este plin, te rugam incearca din nou mai tarziu!' -same_nick: '&4Acelasi nume apartine unui jucator care e deja pe server!' -invalid_name_case: 'Ar trebui sa intri cu numele %valid, nu %invalid' -same_ip_online: 'Deja este un jucator care are aceiasi adresa ip cu a ta!' +on_join_validation: + same_ip_online: 'Deja este un jucator care are aceiasi adresa ip cu a ta!' + same_nick_online: '&4Acelasi nume apartine unui jucator care e deja pe server!' + name_length: '&4Numele tau este prea scurt pentru a te putea conecta!' + characters_in_name: '&4Numele tau contine caractere ilegale. Caractere permise: %valid_chars' + kick_full_server: '&4Server-ul este plin, te rugam incearca din nou mai tarziu!' + country_banned: '&4Tara ta este interzisa pe acest server!' + not_owner_error: 'Tu nu esti detinatorul acestui cont. Te rugam alege alt nume!' + invalid_name_case: 'Ar trebui sa intri cu numele %valid, nu %invalid' # Email -usage_email_add: '&cFoloseste comanda: /email add ' -usage_email_change: '&cFoloseste comanda: /email change ' -usage_email_recovery: '&cFoloseste comanda: /email recovery ' -new_email_invalid: '&cNoul email este invalid, incearca din nou!' -old_email_invalid: '&cEmail-ul vechi este invalid, incearca din nou!' -email_invalid: '&cEmail-ul este invalid, incearca din nou!' -email_added: '&2Email-ul a fost adaugat cu succes la contul tau!' -email_confirm: '&cTe rugam sa confirmi adresa ta de email!' -email_changed: '&2Email-ul a fost schimbat cu succes!' -email_send: ' &2Ti-am trimis un mail cu codul necesar recuperarii parolei tale!' -email_show: '&2Adresa ta curenta de email este: &f%email' -incomplete_email_settings: 'Eroare: Nu indeplinesti conditiile trimiterii unui email! Te rugam contacteaza un administrator.' -email_already_used: '&4Email-ul acesta este deja folosit de altcineva' -email_send_failure: 'Email-ul nu a putut fi trimis. Ta rugam contactatezi un administrator.' -show_no_email: '&2Nu ai nici o adresa de email asociata cu acest cont.' -add_email: '&3Te rugam adaugati email-ul la contul tau folosind comanda "/email add "' -recovery_email: '&3Ti-ai uitat parola? Te rugam foloseste comanda "/email recovery "' -change_password_expired: 'Nu mai iti poti schimba parola folosind aceasta comanda.' -email_cooldown_error: '&cAi primit deja un mail pentru schimbarea parolei. Trebuie sa astepti %time inainte de a trimite unul nou.' +email: + add_email_request: '&3Te rugam adaugati email-ul la contul tau folosind comanda "/email add "' + usage_email_add: '&cFoloseste comanda: /email add ' + usage_email_change: '&cFoloseste comanda: /email change ' + new_email_invalid: '&cNoul email este invalid, incearca din nou!' + old_email_invalid: '&cEmail-ul vechi este invalid, incearca din nou!' + invalid: '&cEmail-ul este invalid, incearca din nou!' + added: '&2Email-ul a fost adaugat cu succes la contul tau!' + request_confirmation: '&cTe rugam sa confirmi adresa ta de email!' + changed: '&2Email-ul a fost schimbat cu succes!' + email_show: '&2Adresa ta curenta de email este: &f%email' + no_email_for_account: '&2Nu ai nici o adresa de email asociata cu acest cont.' + already_used: '&4Email-ul acesta este deja folosit de altcineva' + incomplete_settings: 'Eroare: Nu indeplinesti conditiile trimiterii unui email! Te rugam contacteaza un administrator.' + send_failure: 'Email-ul nu a putut fi trimis. Ta rugam contactatezi un administrator.' + change_password_expired: 'Nu mai iti poti schimba parola folosind aceasta comanda.' + email_cooldown_error: '&cAi primit deja un mail pentru schimbarea parolei. Trebuie sa astepti %time inainte de a trimite unul nou.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Ti-ai uitat parola? Te rugam foloseste comanda "/email recovery "' + command_usage: '&cFoloseste comanda: /email recovery ' + email_sent: ' &2Ti-am trimis un mail cu codul necesar recuperarii parolei tale!' + code: + code_sent: 'Un cod de recuperare a parolei a fost trimis catre email-ul tau.' + incorrect: 'Codul de recuperare nu este corect! Mai ai %count incercari ramase.' + tries_exceeded: 'Ai atins numarul maxim de incercari pentru introducerea codului de recuperare. Foloseste "/email recovery [email]" pentru a genera unul nou.' + correct: 'Cod de recuperare corect!' + change_password: 'Foloseste imediat comanda /email setpassword pentru a-ti schimba parola.' # Captcha -usage_captcha: '&3Pentru a te autentifica, te rugam sa folosesti comanda "/captcha "' -wrong_captcha: '&cCodul de verificare este gresit, te rugam foloseste comanda "/captcha THE_CAPTCHA"!' -valid_captcha: '&2Codul de verificare a fost scris corect!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Pentru a te autentifica, te rugam sa folosesti comanda "/captcha %captcha_code"' + wrong_captcha: '&cCodul de verificare este gresit, te rugam foloseste comanda "/captcha %captcha_code"!' + valid_captcha: '&2Codul de verificare a fost scris corect!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'secunda' -seconds: 'secunde' -minute: 'minut' -minutes: 'minute' -hour: 'ora' -hours: 'ore' -day: 'zi' -days: 'zile' +time: + second: 'secunda' + seconds: 'secunde' + minute: 'minut' + minutes: 'minute' + hour: 'ora' + hours: 'ore' + day: 'zi' + days: 'zile' diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 58c6fadb5..6378e2036 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -1,117 +1,136 @@ -# Регистрация -reg_msg: '&3Регистрация: /reg <пароль> <повтор пароля>' -usage_reg: '&cИспользование: /reg <пароль> <повтор пароля>' -reg_only: '&4Вход только для зарегистрированных! Посетите http://сайт_сервера.ru для регистрации.' -kicked_admin_registered: 'Администратор зарегистрировал вас. Авторизируйтесь снова.' -registered: '&2Вы успешно зарегистрировались!' -reg_disabled: '&cРегистрация отключена.' -user_regged: '&cИгрок с таким именем уже зарегистрирован.' +# Registration +registration: + disabled: '&cРегистрация отключена.' + name_taken: '&cИгрок с таким именем уже зарегистрирован.' + register_request: '&3Регистрация: /reg <пароль> <повтор пароля>' + command_usage: '&cИспользование: /reg <пароль> <повтор пароля>' + reg_only: '&4Вход только для зарегистрированных! Посетите http://сайт_сервера.ru для регистрации.' + success: '&2Вы успешно зарегистрировались!' + kicked_admin_registered: 'Администратор зарегистрировал вас. Авторизируйтесь снова.' -# Ошибки с паролем при регистрации -password_error: '&cПароли не совпадают.' -password_error_nick: '&cНельзя использовать своё имя в качестве пароля.' -password_error_unsafe: '&cТакой пароль небезопасен.' -password_error_chars: '&4Пароль содержит запрещённые символы. Разрешённые: REG_EX' -pass_len: '&cПароль слишком длинный/короткий.' +# Password errors on registration +password: + match_error: '&cПароли не совпадают.' + name_in_password: '&cНельзя использовать своё имя в качестве пароля.' + unsafe_password: '&cТакой пароль небезопасен.' + forbidden_characters: '&4Пароль содержит запрещённые символы. Разрешённые: %valid_chars' + wrong_length: '&cПароль слишком длинный/короткий.' -# Вход -usage_log: '&cИспользование: /login <пароль>' -wrong_pwd: '&cНеправильный пароль!' -login: '&2Вы успешно вошли!' -login_msg: '&3Авторизация: /login <Пароль>' -timeout: '&4Время авторизации истекло.' +# Login +login: + command_usage: '&cИспользование: /login <пароль>' + wrong_password: '&cНеправильный пароль!' + success: '' + login_request: '&3Авторизация: /login <Пароль>' + timeout_error: '&4Время авторизации истекло.' -# Ошибки -unknown_user: '&cИгрок с таким именем не зарегистрирован.' -denied_command: '&cНеобходимо авторизоваться для использования этой команды!' -denied_chat: '&cНеобходимо авторизоваться, чтобы писать в чат!' -not_logged_in: '&cВы ещё не вошли!' -tempban_max_logins: '&cВы временно заблокированы из-за большого количества неудачных попыток авторизоваться.' -max_reg: '&cПревышено максимальное количество регистраций на сервере! (%reg_count/%max_acc %reg_names)' -no_perm: '&4Недостаточно прав.' -error: '&cПроизошла ошибка. Свяжитесь с администратором.' -kick_forvip: '&3VIP-игрок зашёл на переполненный сервер.' +# Errors +error: + denied_command: '&cНеобходимо авторизоваться для использования этой команды!' + denied_chat: '&cНеобходимо авторизоваться, чтобы писать в чат!' + unregistered_user: '&cИгрок с таким именем не зарегистрирован.' + not_logged_in: '&cВы ещё не вошли!' + no_permission: '&4Недостаточно прав.' + unexpected_error: '' + max_registration: '&cПревышено максимальное количество регистраций на сервере! (%reg_count/%max_acc %reg_names)' + logged_in: '&cВы уже авторизированы!' + kick_for_vip: '&3VIP-игрок зашёл на переполненный сервер.' + tempban_max_logins: '&cВы временно заблокированы из-за большого количества неудачных попыток авторизоваться.' -# АнтиБот (Защита от ботов) -kick_antibot: 'Сработала защита против ботов! Необходимо подождать перед следующим входом на сервер.' -antibot_auto_enabled: '&4[AuthMe] AntiBot-режим включился из-за большого количества входов!' -antibot_auto_disabled: '&2[AuthMe] AntiBot-режим отключился спустя %m мин.' +# AntiBot +antibot: + kick_antibot: 'Сработала защита против ботов! Необходимо подождать перед следующим входом на сервер.' + auto_enabled: '&4[AuthMe] AntiBot-режим включился из-за большого количества входов!' + auto_disabled: '&2[AuthMe] AntiBot-режим отключился спустя %m мин.' -# Другие сообщения -unregistered: '&cУчётная запись успешно удалена!' -accounts_owned_self: 'У вас %count уч. записей:' -accounts_owned_other: 'У игрока %name %count уч. записей:' -two_factor_create: '&2Ваш секретный код — %code. Просканируйте его здесь: %url' -recovery_code_sent: 'Код восстановления для сброса пароля был отправлен на электронную почт.' -recovery_code_incorrect: 'Неверный код восстановления! Попыток осталось: %count.' -recovery_tries_exceeded: 'Вы слишком много раз неверно ввели код восстановления. Используйте «/email recovery [эл. почта]», чтобы получить новый код.' -recovery_code_correct: 'Код восстановления введён верно!' -recovery_change_password: 'Используйте «/email setpassword <новый пароль>», чтобы сменить свой пароль.' -vb_nonActiv: '&cВаша уч. запись ещё не активирована. Проверьте электронную почту!' -usage_unreg: '&cИспользование: /unregister <пароль>' -pwd_changed: '&2Пароль изменён!' -logged_in: '&cВы уже авторизированы!' -logout: '&2Вы успешно вышли.' -reload: '&6Конфигурация и база данных перезагружены.' -usage_changepassword: '&cИспользование: /changepassword <пароль> <новый пароль>' +# Unregister +unregister: + success: '&cУчётная запись успешно удалена!' + command_usage: '&cИспользование: /unregister <пароль>' -# Сообщения сессии -invalid_session: '&cСессия некорректна. Дождитесь, пока она закончится.' -valid_session: '&2Вы автоматически авторизовались!' +# Other messages +misc: + account_not_activated: '&cВаша уч. запись ещё не активирована. Проверьте электронную почту!' + password_changed: '&2Пароль изменён!' + logout: '&2Вы успешно вышли.' + reload: '&6Конфигурация и база данных перезагружены.' + usage_change_password: '&cИспользование: /changepassword <пароль> <новый пароль>' + two_factor_create: '&2Ваш секретный код — %code. Просканируйте его здесь: %url' + accounts_owned_self: 'У вас %count уч. записей:' + accounts_owned_other: 'У игрока %name %count уч. записей:' -# Ошибки при входе -name_len: '&4Ваше имя слишком длинное/короткое.' -regex: '&4Пароль содержит запрещённые символы. Разрешённые: REG_EX' -country_banned: '&4Вход с IP-адресов вашей страны запрещён на этом сервере.' -not_owner_error: 'Вы не являетесь владельцем данной уч. записи. Выберите себе другое имя!' -kick_fullserver: '&4Сервер полон. Попробуйте зайти позже!' -same_nick: '&4Игрок с данным именем уже играет на сервере!' -invalid_name_case: 'Неверное имя! Зайдите под именем %valid, а не %invalid.' -same_ip_online: 'Игрок с данным IP-адресом уже играет на сервере!' +# Session messages +session: + valid_session: '&2Вы автоматически авторизовались!' + invalid_session: '&cСессия некорректна. Дождитесь, пока она закончится.' -# Почта -usage_email_add: '&cИспользование: /email add <эл. почта> <повтор эл. почты>' -usage_email_change: '&cИспользование: /email change <эл. почта> <новая эл. почта>' -usage_email_recovery: '&cИспользование: /email recovery <эл. почта>' -new_email_invalid: '&cНедействительная новая электронная почта!' -old_email_invalid: '&cНедействительная старая электронная почта!' -email_invalid: '&cНедействительный адрес электронной почты!' -email_added: '&2Электронная почта успешно добавлена!' -email_confirm: '&cПодтвердите свою электронную почту!' -email_changed: '&2Адрес электронной почты изменён!' -email_send: '&2Письмо с инструкциями для восстановления было отправлено на вашу электронную почту!' -email_show: '&2Текущий адрес электронной почты — &f%email' -incomplete_email_settings: 'Ошибка: не все необходимые параметры установлены для отправки электронной почты. Свяжитесь с администратором.' -email_already_used: '&4Эта электронная почта уже используется.' -email_send_failure: 'Письмо не может быть отправлено. Свяжитесь в администратором.' -show_no_email: '&2К вашей уч. записи не привязана электронная почта.' -add_email: '&3Добавьте электронную почту: /email add <эл. почта> <повтор эл. почты>' -recovery_email: '&Забыли пароль? Используйте «/email recovery <эл. почта>».' -change_password_expired: 'Больше нельзя сменить свой пароль, используя эту команду.' -email_cooldown_error: '&cПисьмо было отправлено недавно. Подождите %time, прежде чем отправить новое.' +# Error messages when joining +on_join_validation: + same_ip_online: 'Игрок с данным IP-адресом уже играет на сервере!' + same_nick_online: '&4Игрок с данным именем уже играет на сервере!' + name_length: '&4Ваше имя слишком длинное/короткое.' + characters_in_name: '&4Пароль содержит запрещённые символы. Разрешённые: %valid_chars' + kick_full_server: '&4Сервер полон. Попробуйте зайти позже!' + country_banned: '&4Вход с IP-адресов вашей страны запрещён на этом сервере.' + not_owner_error: 'Вы не являетесь владельцем данной уч. записи. Выберите себе другое имя!' + invalid_name_case: 'Неверное имя! Зайдите под именем %valid, а не %invalid.' -# Каптча -usage_captcha: '&3Необходимо ввести текст с каптчи. Используйте «/captcha »' -wrong_captcha: '&cНеверно! Используйте «/captcha THE_CAPTCHA».' -valid_captcha: '&2Вы успешно решили каптчу!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +# Email +email: + add_email_request: '&3Добавьте электронную почту: /email add <эл. почта> <повтор эл. почты>' + usage_email_add: '&cИспользование: /email add <эл. почта> <повтор эл. почты>' + usage_email_change: '&cИспользование: /email change <эл. почта> <новая эл. почта>' + new_email_invalid: '&cНедействительная новая электронная почта!' + old_email_invalid: '&cНедействительная старая электронная почта!' + invalid: '&cНедействительный адрес электронной почты!' + added: '&2Электронная почта успешно добавлена!' + request_confirmation: '&cПодтвердите свою электронную почту!' + changed: '&2Адрес электронной почты изменён!' + email_show: '&2Текущий адрес электронной почты — &f%email' + no_email_for_account: '&2К вашей уч. записи не привязана электронная почта.' + already_used: '&4Эта электронная почта уже используется.' + incomplete_settings: 'Ошибка: не все необходимые параметры установлены для отправки электронной почты. Свяжитесь с администратором.' + send_failure: 'Письмо не может быть отправлено. Свяжитесь в администратором.' + change_password_expired: 'Больше нельзя сменить свой пароль, используя эту команду.' + email_cooldown_error: '&cПисьмо было отправлено недавно. Подождите %time, прежде чем отправить новое.' -# Код подтверждения -verification_code_required: '&3Эта команда чувствительна и требует подтверждения электронной почты! Проверьте свою почту и следуйте инструкциям в письме.' -usage_verification_code: '&cИспользование: /verification <код>' -incorrect_verification_code: '&cНеверный код, используйте «/verification <код>», подставив код из полученного письма.' -verification_code_verified: '&2Ваша личность подтверждена! Теперь можно выполнять все чувствительные команды в текущем сеансе!' -verification_code_already_verified: '&2Вы уже можете выполнять все чувствительные команды в текущем сеансе!' -verification_code_expired: '&3Срок действия кода истёк! Выполните чувствительную команду, чтобы получить новый код!' -verification_code_email_needed: '&3Чтобы подтвердить вашу личность, необходимо привязать электронную почту к учётной записи!!' +# Password recovery by email +recovery: + forgot_password_hint: '&Забыли пароль? Используйте «/email recovery <эл. почта>».' + command_usage: '&cИспользование: /email recovery <эл. почта>' + email_sent: '&2Письмо с инструкциями для восстановления было отправлено на вашу электронную почту!' + code: + code_sent: 'Код восстановления для сброса пароля был отправлен на электронную почт.' + incorrect: 'Неверный код восстановления! Попыток осталось: %count.' + tries_exceeded: 'Вы слишком много раз неверно ввели код восстановления. Используйте «/email recovery [эл. почта]», чтобы получить новый код.' + correct: 'Код восстановления введён верно!' + change_password: 'Используйте «/email setpassword <новый пароль>», чтобы сменить свой пароль.' -# Единицы времени -second: 'с.' -seconds: 'с.' -minute: 'мин.' -minutes: 'мин.' -hour: 'ч.' -hours: 'ч.' -day: 'дн.' -days: 'дн.' +# Captcha +captcha: + usage_captcha: '&3Необходимо ввести текст с каптчи. Используйте «/captcha %captcha_code»' + wrong_captcha: '&cНеверно! Используйте «/captcha %captcha_code».' + valid_captcha: '&2Вы успешно решили каптчу!' + captcha_for_registration: '' + register_captcha_valid: '' + +# Verification code +verification: + code_required: '&3Эта команда чувствительна и требует подтверждения электронной почты! Проверьте свою почту и следуйте инструкциям в письме.' + command_usage: '&cИспользование: /verification <код>' + incorrect_code: '&cНеверный код, используйте «/verification <код>», подставив код из полученного письма.' + success: '&2Ваша личность подтверждена! Теперь можно выполнять все чувствительные команды в текущем сеансе!' + already_verified: '&2Вы уже можете выполнять все чувствительные команды в текущем сеансе!' + code_expired: '&3Срок действия кода истёк! Выполните чувствительную команду, чтобы получить новый код!' + email_needed: '&3Чтобы подтвердить вашу личность, необходимо привязать электронную почту к учётной записи!!' + +# Time units +time: + second: 'с.' + seconds: 'с.' + minute: 'мин.' + minutes: 'мин.' + hour: 'ч.' + hours: 'ч.' + day: 'дн.' + days: 'дн.' diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index bba72aa99..652fa2bc4 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -6,119 +6,138 @@ # check Translators on GitHub Wiki. # # Registration -reg_msg: '&cZaregistruj sa príkazom "/register ".' -usage_reg: '&cPoužitie: /register ' -reg_only: '&fIba zaregistrovaný hráči sa môžu pripojiť na tento server! Navštív http://example.com pre registráciu.' -kicked_admin_registered: 'Admin ťa zaregistroval; prosím prihlás sa znovu.' -registered: '&cBol si úspešne zaregistrovaný.' -reg_disabled: '&cRegistrácia nie je povolená.' -user_regged: '&cZadané meno je už zaregistrované.' +registration: + disabled: '&cRegistrácia nie je povolená.' + name_taken: '&cZadané meno je už zaregistrované.' + register_request: '&cZaregistruj sa príkazom "/register ".' + command_usage: '&cPoužitie: /register ' + reg_only: '&fIba zaregistrovaný hráči sa môžu pripojiť na tento server! Navštív http://example.com pre registráciu.' + success: '&cBol si úspešne zaregistrovaný.' + kicked_admin_registered: 'Admin ťa zaregistroval; prosím prihlás sa znovu.' # Password errors on registration -password_error: '&fHeslá sa nezhodujú.' -password_error_nick: '&cNemôžeš použiť tvoje meno ako heslo, prosím zvoľ si iné...' -password_error_unsafe: '&cTvoje heslo nieje bezpečné, prosím zvoľ si iné...' -password_error_chars: '&4Tvoje heslo obsahuje zakázané znaky. Povolené znaky: REG_EX' -pass_len: '&fHeslo je veľmi krátke alebo veľmi dlhé.' +password: + match_error: '&fHeslá sa nezhodujú.' + name_in_password: '&cNemôžeš použiť tvoje meno ako heslo, prosím zvoľ si iné...' + unsafe_password: '&cTvoje heslo nieje bezpečné, prosím zvoľ si iné...' + forbidden_characters: '&4Tvoje heslo obsahuje zakázané znaky. Povolené znaky: %valid_chars' + wrong_length: '&fHeslo je veľmi krátke alebo veľmi dlhé.' # Login -usage_log: '&cPoužitie: /login ' -wrong_pwd: '&cZadal si zlé heslo.' -login: '&cBol si úspešne prihlásený!' -login_msg: '&cPrihlás sa príkazom "/login ".' -timeout: '&fVypršal čas na prihlásenie, pripoj sa a skús to znovu.' +login: + command_usage: '&cPoužitie: /login ' + wrong_password: '&cZadal si zlé heslo.' + success: '' + login_request: '&cPrihlás sa príkazom "/login ".' + timeout_error: '&fVypršal čas na prihlásenie, pripoj sa a skús to znovu.' # Errors -unknown_user: '&cZadané meno nie je zaregistrované!' -denied_command: '&cPre použitie tohto príkazu sa musíš prihlásiť!' -denied_chat: '&cMusíš byť prihlásený ak chceš použiť chat!' -not_logged_in: '&cNie si ešte prihlásený!' -tempban_max_logins: '&cBol si dočasne zabanovaný za opakované zadanie zlého hesla.' -max_reg: '&fPrekročil si maximum registrovaných účtov(%reg_count/%max_acc|%reg_names).' -no_perm: '&cNemáš dostatočné práva na vykonanie tejto činnosti.' -error: '&fNastala chyba, prosím kontaktuj Administrátora.' -kick_forvip: '&3Uvoľnil si miesto pre VIP hráča!' +error: + denied_command: '&cPre použitie tohto príkazu sa musíš prihlásiť!' + denied_chat: '&cMusíš byť prihlásený ak chceš použiť chat!' + unregistered_user: '&cZadané meno nie je zaregistrované!' + not_logged_in: '&cNie si ešte prihlásený!' + no_permission: '&cNemáš dostatočné práva na vykonanie tejto činnosti.' + unexpected_error: '' + max_registration: '&fPrekročil si maximum registrovaných účtov(%reg_count/%max_acc|%reg_names).' + logged_in: '&cAktuálne si už prihlásený!' + kick_for_vip: '&3Uvoľnil si miesto pre VIP hráča!' + tempban_max_logins: '&cBol si dočasne zabanovaný za opakované zadanie zlého hesla.' # AntiBot -kick_antibot: 'AntiBot je zapnutý! Musíš počkať niekoľko minút pred znovupripojením sa na server.' -antibot_auto_enabled: '&4[AntiBotService] AntiBot bol zapnutý kôli masívnym pokusom o pripojenie!' -antibot_auto_disabled: '&2[AntiBotService] AntiBot bol vypnutý po %m minútach!' +antibot: + kick_antibot: 'AntiBot je zapnutý! Musíš počkať niekoľko minút pred znovupripojením sa na server.' + auto_enabled: '&4[AntiBotService] AntiBot bol zapnutý kôli masívnym pokusom o pripojenie!' + auto_disabled: '&2[AntiBotService] AntiBot bol vypnutý po %m minútach!' + +# Unregister +unregister: + success: '&cÚčet bol vymazaný!' + command_usage: '&cPoužitie: /unregister heslo' # Other messages -unregistered: '&cÚčet bol vymazaný!' -accounts_owned_self: 'Vlastníš tieto účty(%count): ' -accounts_owned_other: 'Hráč %name vlastní tieto účty(%count): ' -two_factor_create: '&2Tvoj tajný kód je %code. Môžeš ho oskenovať tu: %url' -recovery_code_sent: 'Kód na obnovenie účtu bol poslaný na tvoj e-mail.' -recovery_code_incorrect: 'Nesprávny kód! Zostávajúce pokusy: %count.' -recovery_tries_exceeded: 'Prekročil si maximálny počet pokusov o zadanie kódu pre obnovenie účtu. Použi "/email recovery [email]" pre vygenerovanie nového kódu.' -recovery_code_correct: 'Správny kód!' -recovery_change_password: 'Prosím hneď použi príkaz /email setpassword pre zmenenie tvojeho hesla.' -vb_nonActiv: '&fTvoj účet nie je aktivovaný. Prezri si svoj e-mail!' -usage_unreg: '&cPoužitie: /unregister heslo' -pwd_changed: '&cHeslo zmenené!' -logged_in: '&cAktuálne si už prihlásený!' -logout: '&cBol si úspešne odhlásený.' -reload: '&fZnovu načítanie konfigurácie a databázy bolo úspešné.' -usage_changepassword: '&fPoužitie: /changepassword ' +misc: + account_not_activated: '&fTvoj účet nie je aktivovaný. Prezri si svoj e-mail!' + password_changed: '&cHeslo zmenené!' + logout: '&cBol si úspešne odhlásený.' + reload: '&fZnovu načítanie konfigurácie a databázy bolo úspešné.' + usage_change_password: '&fPoužitie: /changepassword ' + two_factor_create: '&2Tvoj tajný kód je %code. Môžeš ho oskenovať tu: %url' + accounts_owned_self: 'Vlastníš tieto účty(%count): ' + accounts_owned_other: 'Hráč %name vlastní tieto účty(%count): ' # Session messages -invalid_session: '&fTvoja IP sa zmenila a tvoje prihlásenie(relácia) vypršalo(/a).' -valid_session: '&cAutomatické prihlásenie z dôvodu pokračovania relácie.' +session: + valid_session: '&cAutomatické prihlásenie z dôvodu pokračovania relácie.' + invalid_session: '&fTvoja IP sa zmenila a tvoje prihlásenie(relácia) vypršalo(/a).' # Error messages when joining -name_len: '&cTvoje meno je veľmi krátke alebo veľmi dlhé.' -regex: '&cTvoje meno obsahuje zakázané znaky. Povolené znaky: REG_EX' -country_banned: '&4Tvoja krajina je zabanovaná na tomto serveri!' -not_owner_error: 'Niesi majiteľ tohto účtu. Prosím zvoľ si iné meno!' -kick_fullserver: '&4Server je plný, skús znovu neskôr!' -same_nick: '&fHrác s týmto nickom už hrá!' -invalid_name_case: 'Mal by si sa pripojiť s nickom %valid, nie %invalid -pozor na veľké a malé písmená.' -same_ip_online: 'Hráč s tvojou IP už hrá na tomto serveri!' +on_join_validation: + same_ip_online: 'Hráč s tvojou IP už hrá na tomto serveri!' + same_nick_online: '&fHrác s týmto nickom už hrá!' + name_length: '&cTvoje meno je veľmi krátke alebo veľmi dlhé.' + characters_in_name: '&cTvoje meno obsahuje zakázané znaky. Povolené znaky: %valid_chars' + kick_full_server: '&4Server je plný, skús znovu neskôr!' + country_banned: '&4Tvoja krajina je zabanovaná na tomto serveri!' + not_owner_error: 'Niesi majiteľ tohto účtu. Prosím zvoľ si iné meno!' + invalid_name_case: 'Mal by si sa pripojiť s nickom %valid, nie %invalid -pozor na veľké a malé písmená.' # Email -usage_email_add: '&cPoužitie: /email add ' -usage_email_change: '&cPoužitie: /email change ' -usage_email_recovery: '&cPoužitie: /email recovery ' -new_email_invalid: '&cNeplatný nový email, skús to znovu!' -old_email_invalid: '&cNeplatný starý email, skús to znovu!' -email_invalid: '&cNeplatná emailová adresa, skús to znovu!' -email_added: '&2Emailová adresa bola úspešne pridaná k tvojmu účtu!' -email_confirm: '&cProsím potvrď svoju emailovú adresu!' -email_changed: '&2Emailová adresa bola úspešne zmenená!' -email_send: '&2Email na obnovenie bol úspešne odoslaný! Prosím skontroluj si svoju emailovú schránku!' -email_show: '&2Tvoja súčastná emailová adresa je: &f%email' -incomplete_email_settings: 'Chyba: nie všetky potrebné nastavenia sú nastavené pre posielanie emailov. Prosím kontaktuj Administrátora.' -email_already_used: '&4Túto emailovú adresu už niekto používa.' -email_send_failure: 'Email nemohol byť poslaný. Prosím kontaktuj Administrátora.' -show_no_email: '&2Momentálne nemáš emailovú adresu spojenú s týmto účtom.' -add_email: '&cPridaj svoj e-mail príkazom "/email add ".' -recovery_email: '&cZabudol si heslo? Použi príkaz /email recovery ' -change_password_expired: 'Už nemôžeš zmeniť svoje heslo týmto príkazom.' -email_cooldown_error: '&cEmail bol nedávno poslaný. Musíš počkať %time predtým ako ti pošleme nový.' +email: + add_email_request: '&cPridaj svoj e-mail príkazom "/email add ".' + usage_email_add: '&cPoužitie: /email add ' + usage_email_change: '&cPoužitie: /email change ' + new_email_invalid: '&cNeplatný nový email, skús to znovu!' + old_email_invalid: '&cNeplatný starý email, skús to znovu!' + invalid: '&cNeplatná emailová adresa, skús to znovu!' + added: '&2Emailová adresa bola úspešne pridaná k tvojmu účtu!' + request_confirmation: '&cProsím potvrď svoju emailovú adresu!' + changed: '&2Emailová adresa bola úspešne zmenená!' + email_show: '&2Tvoja súčastná emailová adresa je: &f%email' + no_email_for_account: '&2Momentálne nemáš emailovú adresu spojenú s týmto účtom.' + already_used: '&4Túto emailovú adresu už niekto používa.' + incomplete_settings: 'Chyba: nie všetky potrebné nastavenia sú nastavené pre posielanie emailov. Prosím kontaktuj Administrátora.' + send_failure: 'Email nemohol byť poslaný. Prosím kontaktuj Administrátora.' + change_password_expired: 'Už nemôžeš zmeniť svoje heslo týmto príkazom.' + email_cooldown_error: '&cEmail bol nedávno poslaný. Musíš počkať %time predtým ako ti pošleme nový.' + +# Password recovery by email +recovery: + forgot_password_hint: '&cZabudol si heslo? Použi príkaz /email recovery ' + command_usage: '&cPoužitie: /email recovery ' + email_sent: '&2Email na obnovenie bol úspešne odoslaný! Prosím skontroluj si svoju emailovú schránku!' + code: + code_sent: 'Kód na obnovenie účtu bol poslaný na tvoj e-mail.' + incorrect: 'Nesprávny kód! Zostávajúce pokusy: %count.' + tries_exceeded: 'Prekročil si maximálny počet pokusov o zadanie kódu pre obnovenie účtu. Použi "/email recovery [email]" pre vygenerovanie nového kódu.' + correct: 'Správny kód!' + change_password: 'Prosím hneď použi príkaz /email setpassword pre zmenenie tvojeho hesla.' # Captcha -usage_captcha: '&3Pre prihlásenie musíš vyriešiť captcha kód, prosím použi príkaz: /captcha ' -wrong_captcha: '&cNesprávny kód captcha, prosím napíš "/captcha THE_CAPTCHA" do chatu!' -valid_captcha: '&2Správne si vyriešil captcha kód!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Pre prihlásenie musíš vyriešiť captcha kód, prosím použi príkaz: /captcha %captcha_code' + wrong_captcha: '&cNesprávny kód captcha, prosím napíš "/captcha %captcha_code" do chatu!' + valid_captcha: '&2Správne si vyriešil captcha kód!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'sek.' -seconds: 'sek.' -minute: 'min.' -minutes: 'min.' -hour: 'hod.' -hours: 'hod.' -day: 'd.' -days: 'd.' +time: + second: 'sek.' + seconds: 'sek.' + minute: 'min.' + minutes: 'min.' + hour: 'hod.' + hours: 'hod.' + day: 'd.' + days: 'd.' diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index c65afc46b..c92ae3028 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -1,118 +1,136 @@ -# Kayit mesajlari -reg_msg: '&3Lutfen kayit komutunu kullanin "/register "' -usage_reg: '&cKullanim: /register ' -reg_only: '&4Sunucuya kayit sadece internet uzerinden yapilmakta! Lutfen http://ornek.com sitesini kayit icin ziyaret edin!' -kicked_admin_registered: 'Bir yetkili seni kayit etti; tekrardan giris yap' -registered: '&2Basariyla kaydoldun!' -reg_disabled: '&cOyun icin kayit olma kapatildi!' -user_regged: '&cSenin adinda daha once birisi kaydolmus!' +# Registration +registration: + disabled: '&cOyun icin kayit olma kapatildi!' + name_taken: '&cSenin adinda daha once birisi kaydolmus!' + register_request: '&3Lutfen kayit komutunu kullanin "/register "' + command_usage: '&cKullanim: /register ' + reg_only: '&4Sunucuya kayit sadece internet uzerinden yapilmakta! Lutfen http://ornek.com sitesini kayit icin ziyaret edin!' + success: '&2Basariyla kaydoldun!' + kicked_admin_registered: 'Bir yetkili seni kayit etti; tekrardan giris yap' -# Kayit aninda sifre hatalari -password_error: '&cSifre eslesmiyor, tekrar deneyin!' -password_error_nick: '&cSifrenize adinizi koyamazsiniz, lutfen farkli bir sifre secin...' -password_error_unsafe: '&cSectiginiz sifre guvenli degil, lutfen farkli bir sifre secin...' -password_error_chars: '&4Sifrenizde izin verilmeyen karakterler bulunmakta. Izin verilen karakterler: REG_EX' -pass_len: '&cSenin sifren ya cok kisa yada cok uzun! Lutfen farkli birsey dene!' +# Password errors on registration +password: + match_error: '&cSifre eslesmiyor, tekrar deneyin!' + name_in_password: '&cSifrenize adinizi koyamazsiniz, lutfen farkli bir sifre secin...' + unsafe_password: '&cSectiginiz sifre guvenli degil, lutfen farkli bir sifre secin...' + forbidden_characters: '&4Sifrenizde izin verilmeyen karakterler bulunmakta. Izin verilen karakterler: %valid_chars' + wrong_length: '&cSenin sifren ya cok kisa yada cok uzun! Lutfen farkli birsey dene!' -# Oturuma giris -usage_log: '&cKullanim: /login ' -wrong_pwd: '&cYanlis sifre!' -login: '&2Giris basarili!' -login_msg: '&cLutfen giris komutunu kullanin "/login "' -timeout: '&4Giris izni icin verilen zaman suresini astigin icin sunucudan atildin, tekrar deneyin!' +# Login +login: + command_usage: '&cKullanim: /login ' + wrong_password: '&cYanlis sifre!' + success: '' + login_request: '&cLutfen giris komutunu kullanin "/login "' + timeout_error: '&4Giris izni icin verilen zaman suresini astigin icin sunucudan atildin, tekrar deneyin!' -# Hata mesajlari -unknown_user: '&cBu oyuncu kayitli degil!' -denied_command: '&cSuanda bu komutu kullanamazsin!' -denied_chat: '&cSuanda sohbeti kullanamazsin!' -not_logged_in: '&cGiris yapmadin!' -tempban_max_logins: '&cBir cok kez yanlis giris yaptiginiz icin gecici olarak banlandiniz.' -max_reg: '&cSen maksimum kayit sinirini astin (%reg_count/%max_acc %reg_names)!' -no_perm: '&4Bunu yapmak icin iznin yok!' -error: '&4Beklenmedik bir hata olustu, yetkili ile iletisime gecin!' -kick_forvip: '&3Bir VIP oyuna giris yaptigi icin atildin!' +# Errors +error: + denied_command: '&cSuanda bu komutu kullanamazsin!' + denied_chat: '&cSuanda sohbeti kullanamazsin!' + unregistered_user: '&cBu oyuncu kayitli degil!' + not_logged_in: '&cGiris yapmadin!' + no_permission: '&4Bunu yapmak icin iznin yok!' + unexpected_error: '' + max_registration: '&cSen maksimum kayit sinirini astin (%reg_count/%max_acc %reg_names)!' + logged_in: '&cZaten giris yaptin!' + kick_for_vip: '&3Bir VIP oyuna giris yaptigi icin atildin!' + tempban_max_logins: '&cBir cok kez yanlis giris yaptiginiz icin gecici olarak banlandiniz.' # AntiBot -kick_antibot: 'AntiBot koruma modu aktif! Birkac dakika sonra tekrar girmeyi deneyin.' -antibot_auto_enabled: '&4[AntiBotServis] Saldiri oldugu icin AntiBot aktif edildi!' -antibot_auto_disabled: '&2[AntiBotServis] AntiBot, %m dakika sonra deaktif edilecek!' +antibot: + kick_antibot: 'AntiBot koruma modu aktif! Birkac dakika sonra tekrar girmeyi deneyin.' + auto_enabled: '&4[AntiBotServis] Saldiri oldugu icin AntiBot aktif edildi!' + auto_disabled: '&2[AntiBotServis] AntiBot, %m dakika sonra deaktif edilecek!' -# Baska mesajlar -unregistered: '&cKayit basariyla kaldirildi!' -accounts_owned_self: 'Sen %count hesaba sahipsin:' -accounts_owned_other: 'Oyuncu %name %count hesaba sahip:' -two_factor_create: '&2Gizli kodunuz %code. Buradan test edebilirsin, %url' -recovery_code_sent: 'Sifre sifirlama kodu eposta adresinize gonderildi.' -# TODO: Missing tags %count -recovery_code_incorrect: 'Kod dogru degil! Kullanim "/email recovery [eposta]" ile yeni bir kod olustur' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&cHeabiniz henuz aktif edilmemis, e-postanizi kontrol edin!' -usage_unreg: '&cKullanim: /unregister ' -pwd_changed: '&2Sifre basariyla degistirildi!' -logged_in: '&cZaten giris yaptin!' -logout: '&2Basariyla cikis yaptin!' -reload: '&2Ayarlar ve veritabani yenilendi!' -usage_changepassword: '&cKullanim: /changepassword ' +# Unregister +unregister: + success: '&cKayit basariyla kaldirildi!' + command_usage: '&cKullanim: /unregister ' -# Otomatik giris -invalid_session: '&cIP adresin degistirildi ve oturum suren doldu!' -valid_session: '&2Oturuma girisiniz otomatikmen yapilmistir.' +# Other messages +misc: + account_not_activated: '&cHeabiniz henuz aktif edilmemis, e-postanizi kontrol edin!' + password_changed: '&2Sifre basariyla degistirildi!' + logout: '&2Basariyla cikis yaptin!' + reload: '&2Ayarlar ve veritabani yenilendi!' + usage_change_password: '&cKullanim: /changepassword ' + two_factor_create: '&2Gizli kodunuz %code. Buradan test edebilirsin, %url' + accounts_owned_self: 'Sen %count hesaba sahipsin:' + accounts_owned_other: 'Oyuncu %name %count hesaba sahip:' -# Servore giris aninda hata mesajlari -name_len: '&4Senin ismin ya cok kisa yada cok uzun!' -regex: '&4Senin isminde uygunsuz karakterler bulunmakta. Izin verilen karakterler: REG_EX' -country_banned: '&4Senin bolgen sunucudan yasaklandi!' -not_owner_error: 'Bu hesabin sahibi degilsin. Lutfen farkli bir isim sec!' -kick_fullserver: '&4Sunucu suanda dolu, daha sonra tekrar deneyin!' -same_nick: '&4Senin isminde bir oyuncu suncuda bulunmakta!' -invalid_name_case: 'Oyuna %valid isminde katilmalisin. %invalid ismini kullanarak katilamazsin.' -same_ip_online: 'Oyunda sizin ipnizden giren biri bulunmakta!' +# Session messages +session: + valid_session: '&2Oturuma girisiniz otomatikmen yapilmistir.' + invalid_session: '&cIP adresin degistirildi ve oturum suren doldu!' + +# Error messages when joining +on_join_validation: + same_ip_online: 'Oyunda sizin ipnizden giren biri bulunmakta!' + same_nick_online: '&4Senin isminde bir oyuncu suncuda bulunmakta!' + name_length: '&4Senin ismin ya cok kisa yada cok uzun!' + characters_in_name: '&4Senin isminde uygunsuz karakterler bulunmakta. Izin verilen karakterler: %valid_chars' + kick_full_server: '&4Sunucu suanda dolu, daha sonra tekrar deneyin!' + country_banned: '&4Senin bolgen sunucudan yasaklandi!' + not_owner_error: 'Bu hesabin sahibi degilsin. Lutfen farkli bir isim sec!' + invalid_name_case: 'Oyuna %valid isminde katilmalisin. %invalid ismini kullanarak katilamazsin.' # Email -usage_email_add: '&cKullanim: /email add ' -usage_email_change: '&cKullanim: /email change ' -usage_email_recovery: '&cKullanim: /email recovery ' -new_email_invalid: '&cGecersiz yeni eposta, tekrar deneyin!' -old_email_invalid: '&cGecersiz eski eposta, tekrar deneyin!' -email_invalid: '&cGecersiz eposta, tekrar deneyin!' -email_added: '&2Eposta basariyla kullaniciniza eklendi!' -email_confirm: '&cLutfen tekrar epostanizi giriniz!' -email_changed: '&2Epostaniz basariyla degistirildi!' -email_send: '&2Sifreniz epostaniza gonderildi! Lutfen eposta kutunuzu kontrol edin!' -email_show: '&2Suanki eposta adresin: &f%email' -incomplete_email_settings: 'Hata: Gonderilen epostada bazi ayarlar tamamlanmis degil. Yetkili ile iletisime gec.' -email_already_used: '&4Eposta adresi zaten kullaniliyor.' -email_send_failure: 'Eposta gonderilemedi. Yetkili ile iletisime gec.' -show_no_email: '&2Bu hesapla iliskili bir eposta bulunmuyor.' -add_email: '&3Lutfen hesabinize eposta adresinizi komut ile ekleyin "/email add "' -recovery_email: '&3Sifreni mi unuttun ? Komut kullanarak ogrenebilirsin "/email recovery "' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -email_cooldown_error: '&cKisa bir sure once eposta gonderildi. Yeni bir eposta almak icin %time beklemelisin.' +email: + add_email_request: '&3Lutfen hesabinize eposta adresinizi komut ile ekleyin "/email add "' + usage_email_add: '&cKullanim: /email add ' + usage_email_change: '&cKullanim: /email change ' + new_email_invalid: '&cGecersiz yeni eposta, tekrar deneyin!' + old_email_invalid: '&cGecersiz eski eposta, tekrar deneyin!' + invalid: '&cGecersiz eposta, tekrar deneyin!' + added: '&2Eposta basariyla kullaniciniza eklendi!' + request_confirmation: '&cLutfen tekrar epostanizi giriniz!' + changed: '&2Epostaniz basariyla degistirildi!' + email_show: '&2Suanki eposta adresin: &f%email' + no_email_for_account: '&2Bu hesapla iliskili bir eposta bulunmuyor.' + already_used: '&4Eposta adresi zaten kullaniliyor.' + incomplete_settings: 'Hata: Gonderilen epostada bazi ayarlar tamamlanmis degil. Yetkili ile iletisime gec.' + send_failure: 'Eposta gonderilemedi. Yetkili ile iletisime gec.' + change_password_expired: '' + email_cooldown_error: '&cKisa bir sure once eposta gonderildi. Yeni bir eposta almak icin %time beklemelisin.' + +# Password recovery by email +recovery: + forgot_password_hint: '&3Sifreni mi unuttun ? Komut kullanarak ogrenebilirsin "/email recovery "' + command_usage: '&cKullanim: /email recovery ' + email_sent: '&2Sifreniz epostaniza gonderildi! Lutfen eposta kutunuzu kontrol edin!' + code: + code_sent: 'Sifre sifirlama kodu eposta adresinize gonderildi.' + incorrect: 'Kod dogru degil! Kullanim "/email recovery [eposta]" ile yeni bir kod olustur' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&3Giris yapmak icin guvenlik kodunu komut yazarak girin "/captcha "' -wrong_captcha: '&cYanlis guvenlik kodu, kullanim sekli "/captcha THE_CAPTCHA" sohbete yazin!' -valid_captcha: '&2Guvenlik kodu dogrulandi!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Giris yapmak icin guvenlik kodunu komut yazarak girin "/captcha %captcha_code"' + wrong_captcha: '&cYanlis guvenlik kodu, kullanim sekli "/captcha %captcha_code" sohbete yazin!' + valid_captcha: '&2Guvenlik kodu dogrulandi!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' -# Zaman birimleri -second: 'saniye' -seconds: 'saniye' -minute: 'dakika' -minutes: 'dakika' -hour: 'saat' -hours: 'saat' -day: 'gun' -days: 'gun' +# Time units +time: + second: 'saniye' + seconds: 'saniye' + minute: 'dakika' + minutes: 'dakika' + hour: 'saat' + hours: 'saat' + day: 'gun' + days: 'gun' diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index ec697b2a6..86f147e2a 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -1,117 +1,136 @@ # Registration -reg_msg: '&3Перш ніж почати гру, вам потрібно зареєструвати свій нікнейм!%nl%&3Для цього просто введіть команду "/register <пароль> <повторПароля>"' -usage_reg: '&cСинтаксис: /register <пароль> <повторПароля>' -reg_only: '&4Лише зареєстровані гравці можуть підключатись до сервера!%nl%&4Будь ласка, відвідайте http://example.com для реєстрації.' -kicked_admin_registered: 'Адміністратор вас зареєстрував; Будь ласка, авторизуйтесь знову!' -registered: '&2Реєстрація пройшла успішно!' -reg_disabled: '&cВнутрішньоігрову реєстрацію зараз вимкнено.' -user_regged: '&cТакий нікнейм вже зареєстровано.' +registration: + disabled: '&cВнутрішньоігрову реєстрацію зараз вимкнено.' + name_taken: '&cТакий нікнейм вже зареєстровано.' + register_request: '&3Перш ніж почати гру, вам потрібно зареєструвати свій нікнейм!%nl%&3Для цього просто введіть команду "/register <пароль> <повторПароля>"' + command_usage: '&cСинтаксис: /register <пароль> <повторПароля>' + reg_only: '&4Лише зареєстровані гравці можуть підключатись до сервера!%nl%&4Будь ласка, відвідайте http://example.com для реєстрації.' + success: '&2Реєстрація пройшла успішно!' + kicked_admin_registered: 'Адміністратор вас зареєстрував; Будь ласка, авторизуйтесь знову!' # Password errors on registration -password_error: '&cПаролі не співпадають!' -password_error_nick: '&cНе можна використовувати свій нікнейм у якості пароля! Будь ласка, виберіть щось інакше...' -password_error_unsafe: '&cЦей пароль надто простий! Будь ласка, придумайте інакший...' -password_error_chars: '&4Ваш пароль містить недопустимі символи. Підберіть інакший...%nl%&4(Reg-ex: REG_EX)' -pass_len: '&cВаш пароль надто короткий або надто довгий! Спробуйте інакший...' +password: + match_error: '&cПаролі не співпадають!' + name_in_password: '&cНе можна використовувати свій нікнейм у якості пароля! Будь ласка, виберіть щось інакше...' + unsafe_password: '&cЦей пароль надто простий! Будь ласка, придумайте інакший...' + forbidden_characters: '&4Ваш пароль містить недопустимі символи. Підберіть інакший...%nl%&4(Reg-ex: %valid_chars)' + wrong_length: '&cВаш пароль надто короткий або надто довгий! Спробуйте інакший...' # Login -usage_log: '&cСинтаксис: /login <пароль>' -wrong_pwd: '&cНевірний пароль!' -login: '&2Успішна авторизація!' -login_msg: '&cДля авторизації, введіть команду "/login <пароль>"' -timeout: '&4Час для авторизації сплинув. Будь ласка, спробуйте ще раз!' +login: + command_usage: '&cСинтаксис: /login <пароль>' + wrong_password: '&cНевірний пароль!' + success: '' + login_request: '&cДля авторизації, введіть команду "/login <пароль>"' + timeout_error: '&4Час для авторизації сплинув. Будь ласка, спробуйте ще раз!' # Errors -unknown_user: '&cЦей гравець не є зареєстрованим.' -denied_command: '&cДля використання цієї команди потрібна авторизація.' -denied_chat: '&cДля доступу до чату потрібна авторизація.' -not_logged_in: '&cВи не авторизовані!' -tempban_max_logins: '&cВаш IP тимчасово заблоковано, із‒за багатократного введення хибного пароля.' -max_reg: '&cВичерпано ліміт реєстрацій (%reg_count/%max_acc %reg_names) для вашого підключення!' -no_perm: '&4У вас недостатньо прав, щоб застосувати цю команду!' -error: '&4[AuthMe] Error. Будь ласка, повідомте адміністратора!' -kick_forvip: '&3Вас кікнуто, внаслідок того, що VIP гравець зайшов на сервер коли небуло вільних місць.' +error: + denied_command: '&cДля використання цієї команди потрібна авторизація.' + denied_chat: '&cДля доступу до чату потрібна авторизація.' + unregistered_user: '&cЦей гравець не є зареєстрованим.' + not_logged_in: '&cВи не авторизовані!' + no_permission: '&4У вас недостатньо прав, щоб застосувати цю команду!' + unexpected_error: '' + max_registration: '&cВичерпано ліміт реєстрацій (%reg_count/%max_acc %reg_names) для вашого підключення!' + logged_in: '&cВи вже авторизовані!' + kick_for_vip: '&3Вас кікнуто, внаслідок того, що VIP гравець зайшов на сервер коли небуло вільних місць.' + tempban_max_logins: '&cВаш IP тимчасово заблоковано, із‒за багатократного введення хибного пароля.' # AntiBot -kick_antibot: 'На сервер здійснено DDoS атаку. Будь ласка, зачекайте декілька хвилин доки активність спаде.' -antibot_auto_enabled: '&4[AntiBotService] Ненормативне число з’єднань. Активовано антибот систему!' -antibot_auto_disabled: '&2[AntiBotService] Антибот систему деактивовано після %m хв. активності.' +antibot: + kick_antibot: 'На сервер здійснено DDoS атаку. Будь ласка, зачекайте декілька хвилин доки активність спаде.' + auto_enabled: '&4[AntiBotService] Ненормативне число з’єднань. Активовано антибот систему!' + auto_disabled: '&2[AntiBotService] Антибот систему деактивовано після %m хв. активності.' + +# Unregister +unregister: + success: '&cДані про реєстрацію успішно видалено' + command_usage: '&cСинтаксис: /unregister <пароль>' # Other messages -unregistered: '&cДані про реєстрацію успішно видалено' -accounts_owned_self: 'Кількість ваших твінк‒акаунтів: %count:' -accounts_owned_other: 'Кількість твінк‒акаунтів гравця %name: %count' -two_factor_create: '&2Ваш секретний код — %code %nl%&2Можете зкопіювати його за цим посиланням — %url' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! You have %count tries remaining.' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&cВаш акаунт ще не активовано. Будь ласка, провірте свою електронну пошту!' -usage_unreg: '&cСинтаксис: /unregister <пароль>' -pwd_changed: '&2Пароль успішно змінено!' -logged_in: '&cВи вже авторизовані!' -logout: '&2Ви вийшли зі свого акаунта!' -reload: '&2Конфігурації та базу даних було успішно перезавантажено!' -usage_changepassword: '&cСинтаксис: /changepassword <старийПароль> <новийПароль>' +misc: + account_not_activated: '&cВаш акаунт ще не активовано. Будь ласка, провірте свою електронну пошту!' + password_changed: '&2Пароль успішно змінено!' + logout: '&2Ви вийшли зі свого акаунта!' + reload: '&2Конфігурації та базу даних було успішно перезавантажено!' + usage_change_password: '&cСинтаксис: /changepassword <старийПароль> <новийПароль>' + two_factor_create: '&2Ваш секретний код — %code %nl%&2Можете зкопіювати його за цим посиланням — %url' + accounts_owned_self: 'Кількість ваших твінк‒акаунтів: %count:' + accounts_owned_other: 'Кількість твінк‒акаунтів гравця %name: %count' # Session messages -invalid_session: '&cСесію було розірвано внаслідок зміни IP.' -valid_session: '&2Сесію відновлено.' +session: + valid_session: '&2Сесію відновлено.' + invalid_session: '&cСесію було розірвано внаслідок зміни IP.' # Error messages when joining -name_len: '&4Ваш нікнейм надто короткий або надто довгий!' -regex: '&4Ваш нікнейм містить недопустимі символи! Reg-ex: REG_EX' -country_banned: '&4Your country is banned from this server!' -not_owner_error: 'Цей акаунт вам не належить! Будь ласка, оберіть інакший нікнейм!' -kick_fullserver: '&4Сервер переповнено! Доведеться зачекати доки хтось вийде.' -same_nick: '&4Хтось з таким ніком зараз вже сидить на сервері!' -invalid_name_case: 'Регістр у вашому нікнеймі відрізняється від регістру при реєстрації.%nl%Поточний регістр: &c%invalid&f. Валідний регістр: &a%valid&f.%nl%Будь ласка, перезайдіть з валідним регістром!' -same_ip_online: 'Перевищено ліміт на авторизацію з одного IP.' +on_join_validation: + same_ip_online: 'Перевищено ліміт на авторизацію з одного IP.' + same_nick_online: '&4Хтось з таким ніком зараз вже сидить на сервері!' + name_length: '&4Ваш нікнейм надто короткий або надто довгий!' + characters_in_name: '&4Ваш нікнейм містить недопустимі символи! Reg-ex: %valid_chars' + kick_full_server: '&4Сервер переповнено! Доведеться зачекати доки хтось вийде.' + country_banned: '&4Your country is banned from this server!' + not_owner_error: 'Цей акаунт вам не належить! Будь ласка, оберіть інакший нікнейм!' + invalid_name_case: 'Регістр у вашому нікнеймі відрізняється від регістру при реєстрації.%nl%Поточний регістр: &c%invalid&f. Валідний регістр: &a%valid&f.%nl%Будь ласка, перезайдіть з валідним регістром!' # Email -usage_email_add: '&cСинтаксис: /email add ' -usage_email_change: '&cСинтаксис: /email change <старий e-mail> <новий e-mail>' -usage_email_recovery: '&cСинтаксис: /email recovery ' -new_email_invalid: '&cНекоректний формат.' -old_email_invalid: '&cСтарий e-mail, що прив’язано до вашого акаунта, відрізняється від введеного вами.' -email_invalid: '&cФормат вказаного e-mail’у є некоректним, або його домен внесено до блеклисту.' -email_added: '&2Електронну пошту успішно прив’язано до вашого акаунта.' -email_confirm: '&cАдреси не співпадають.' -email_changed: '&2E-mail успішно змінено.' -email_send: '&2Лист для відновлення доступу надіслано. Будь ласка, провірте свою пошту!' -# TODO email_show: '&2Your current email address is: &f%email' -incomplete_email_settings: '&4[AuthMe] Error: Не всі необхідні налаштування є встановленими, щоб надсилати електронну пошту. Будь ласка, повідомте адміністратора!' -email_already_used: '&4До цієї електронної пошти прив’язано забагато акаунтів!' -# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&3Не забудьте прив’язати електронну пошту до свого акаунта, за допомогою команди "/email add "' -recovery_email: 'Забули пароль? Можете скористатись командою &9/email recovery &f<&9ваш e-mail&f>' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + add_email_request: '&3Не забудьте прив’язати електронну пошту до свого акаунта, за допомогою команди "/email add "' + usage_email_add: '&cСинтаксис: /email add ' + usage_email_change: '&cСинтаксис: /email change <старий e-mail> <новий e-mail>' + new_email_invalid: '&cНекоректний формат.' + old_email_invalid: '&cСтарий e-mail, що прив’язано до вашого акаунта, відрізняється від введеного вами.' + invalid: '&cФормат вказаного e-mail’у є некоректним, або його домен внесено до блеклисту.' + added: '&2Електронну пошту успішно прив’язано до вашого акаунта.' + request_confirmation: '&cАдреси не співпадають.' + changed: '&2E-mail успішно змінено.' + email_show: '' + no_email_for_account: '' + already_used: '&4До цієї електронної пошти прив’язано забагато акаунтів!' + incomplete_settings: '&4[AuthMe] Error: Не всі необхідні налаштування є встановленими, щоб надсилати електронну пошту. Будь ласка, повідомте адміністратора!' + send_failure: '' + change_password_expired: '' + email_cooldown_error: '' + +# Password recovery by email +recovery: + forgot_password_hint: 'Забули пароль? Можете скористатись командою &9/email recovery &f<&9ваш e-mail&f>' + command_usage: '&cСинтаксис: /email recovery ' + email_sent: '&2Лист для відновлення доступу надіслано. Будь ласка, провірте свою пошту!' + code: + code_sent: '' + incorrect: '' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&3Для продовження доведеться ввести капчу — "/captcha "' -wrong_captcha: '&cНевірно введена капча! Спробуйте ще раз — "/captcha THE_CAPTCHA"' -valid_captcha: '&2Капчу прийнято.' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3Для продовження доведеться ввести капчу — "/captcha %captcha_code"' + wrong_captcha: '&cНевірно введена капча! Спробуйте ще раз — "/captcha %captcha_code"' + valid_captcha: '&2Капчу прийнято.' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -# TODO second: 'second' -# TODO seconds: 'seconds' -# TODO minute: 'minute' -# TODO minutes: 'minutes' -# TODO hour: 'hour' -# TODO hours: 'hours' -# TODO day: 'day' -# TODO days: 'days' +time: + second: '' + seconds: '' + minute: '' + minutes: '' + hour: '' + hours: '' + day: '' + days: '' diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index a0382b522..a880c8a77 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&2Xin vui lòng đăng ký tài khoản với lệnh "/register "' -usage_reg: '&cSử dụng: /register ' -reg_only: '&4Chỉ có thành viên mới có thể tham gia máy chủ, vui lòng truy cập trang web http://example.com để đăng ký thành viên!' -kicked_admin_registered: 'Một quản trị viên đã đăng ký cho bạn; vui lòng đăng nhập lại' -registered: '&2Đăng ký thành công!' -reg_disabled: '&cKhông cho phép đăng ký tài khoản trong máy chủ!' -user_regged: '&cBạn có thể đăng ký với tên tài khoản này!' +registration: + disabled: '&cKhông cho phép đăng ký tài khoản trong máy chủ!' + name_taken: '&cBạn có thể đăng ký với tên tài khoản này!' + register_request: '&2Xin vui lòng đăng ký tài khoản với lệnh "/register "' + command_usage: '&cSử dụng: /register ' + reg_only: '&4Chỉ có thành viên mới có thể tham gia máy chủ, vui lòng truy cập trang web http://example.com để đăng ký thành viên!' + success: '&2Đăng ký thành công!' + kicked_admin_registered: 'Một quản trị viên đã đăng ký cho bạn; vui lòng đăng nhập lại' # Password errors on registration -password_error: '&cMật khẩu không hợp, xin vui lòng kiểm tra lại!' -password_error_nick: '&cBạn không thể đặt mật khẩu bằng tên của mình, vui lòng đặt lại...' -password_error_unsafe: '&cMật khẩu của bạn vừa đặt không an toàn, vui lòng đặt lại...' -password_error_chars: '&4Mật khẩu của bạn chứa ký tự không hợp lệ. Các ký tự cho phép: REG_EX' -pass_len: '&cMật khẩu của bạn đặt quá dài hoặc quá ngắn, vui lòng đặt lại!' +password: + match_error: '&cMật khẩu không hợp, xin vui lòng kiểm tra lại!' + name_in_password: '&cBạn không thể đặt mật khẩu bằng tên của mình, vui lòng đặt lại...' + unsafe_password: '&cMật khẩu của bạn vừa đặt không an toàn, vui lòng đặt lại...' + forbidden_characters: '&4Mật khẩu của bạn chứa ký tự không hợp lệ. Các ký tự cho phép: %valid_chars' + wrong_length: '&cMật khẩu của bạn đặt quá dài hoặc quá ngắn, vui lòng đặt lại!' # Login -usage_log: '&cSử dụng: /login ' -wrong_pwd: '&cSai mật khẩu!' -login: '&2Đăng nhập thành công!' -login_msg: '&cXin vui lòng đăng nhập bằng lệnh "/login "' -timeout: '&4Thời gian đăng nhập đã hết, bạn đã bị văng khỏi máy chủ. Xin vui lòng thử lại!' +login: + command_usage: '&cSử dụng: /login ' + wrong_password: '&cSai mật khẩu!' + success: '' + login_request: '&cXin vui lòng đăng nhập bằng lệnh "/login "' + timeout_error: '&4Thời gian đăng nhập đã hết, bạn đã bị văng khỏi máy chủ. Xin vui lòng thử lại!' # Errors -unknown_user: '&cNgười dùng này đã được đăng ký!' -denied_command: '&cĐể được sử dụng lệnh bạn phải hoàn thành xác thực tài khoản đã!' -denied_chat: '&cĐể được chát bạn phải hoàn thành xác thực tài khoản đã!' -not_logged_in: '&cBạn chưa đăng nhập!' -tempban_max_logins: '&cBạn đã bị tạm thời khóa truy cập do đăng nhập sai nhiều lần.' -max_reg: '&cBạn đã vượt quá giới hạn tối đa đăng ký tài khoản (%reg_count/%max_acc %reg_names) cho những lần kết nối tài khoản!' -no_perm: '&4Bạn không có quyền truy cập lệnh này!' -error: '&4Lỗi! Vui lòng liên hệ quản trị viên hoặc admin' -kick_forvip: '&eChỉ có thành viên VIP mới được tham gia khi máy chủ đầy!' +error: + denied_command: '&cĐể được sử dụng lệnh bạn phải hoàn thành xác thực tài khoản đã!' + denied_chat: '&cĐể được chát bạn phải hoàn thành xác thực tài khoản đã!' + unregistered_user: '&cNgười dùng này đã được đăng ký!' + not_logged_in: '&cBạn chưa đăng nhập!' + no_permission: '&4Bạn không có quyền truy cập lệnh này!' + unexpected_error: '' + max_registration: '&cBạn đã vượt quá giới hạn tối đa đăng ký tài khoản (%reg_count/%max_acc %reg_names) cho những lần kết nối tài khoản!' + logged_in: '&cBạn đã đăng nhập!' + kick_for_vip: '&eChỉ có thành viên VIP mới được tham gia khi máy chủ đầy!' + tempban_max_logins: '&cBạn đã bị tạm thời khóa truy cập do đăng nhập sai nhiều lần.' # AntiBot -kick_antibot: 'Chế độ AntiBot đã được kích hoạt! Bạn phải đợi vài phút trước khi tham gia vào máy chủ.' -antibot_auto_enabled: '&4[AntiBotService] AntiBot đã được kích hoạt do số lượng lớn kết nối đến máy chủ!' -antibot_auto_disabled: '&2[AntiBotService] AntiBot đã được tắt sau %m phút!' +antibot: + kick_antibot: 'Chế độ AntiBot đã được kích hoạt! Bạn phải đợi vài phút trước khi tham gia vào máy chủ.' + auto_enabled: '&4[AntiBotService] AntiBot đã được kích hoạt do số lượng lớn kết nối đến máy chủ!' + auto_disabled: '&2[AntiBotService] AntiBot đã được tắt sau %m phút!' + +# Unregister +unregister: + success: '&cHủy đăng ký thành công!' + command_usage: '&cSử dụng: /unregister ' # Other messages -unregistered: '&cHủy đăng ký thành công!' -accounts_owned_self: 'Bạn sở hữu %count tài khoản:' -accounts_owned_other: 'Người chơi %name có %count tài khoản:' -two_factor_create: '&2Mã bí mật của bạn là %code. Bạn có thể quét nó tại đây %url' -recovery_code_sent: 'Một mã khôi phục mật khẩu đã được gửi đến địa chỉ email của bạn.' -# TODO: Missing tags %count -recovery_code_incorrect: 'Mã khôi phục không đúng! Dùng lệnh /email recovery [email] để tạo một mã mới' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&cTài khoản của bạn chưa được kích hoạt, vui lòng kiểm tra email!' -usage_unreg: '&cSử dụng: /unregister ' -pwd_changed: '&2Thay đổi mật khẩu thành công!' -logged_in: '&cBạn đã đăng nhập!' -logout: '&2Bạn đã đăng xuất!' -reload: '&2Cấu hình và cơ sở dử liệu đã được nạp lại!' -usage_changepassword: '&cSử dụng: /changepassword ' +misc: + account_not_activated: '&cTài khoản của bạn chưa được kích hoạt, vui lòng kiểm tra email!' + password_changed: '&2Thay đổi mật khẩu thành công!' + logout: '&2Bạn đã đăng xuất!' + reload: '&2Cấu hình và cơ sở dử liệu đã được nạp lại!' + usage_change_password: '&cSử dụng: /changepassword ' + two_factor_create: '&2Mã bí mật của bạn là %code. Bạn có thể quét nó tại đây %url' + accounts_owned_self: 'Bạn sở hữu %count tài khoản:' + accounts_owned_other: 'Người chơi %name có %count tài khoản:' # Session messages -invalid_session: '&cIP của bạn đã bị thay đổi và phiên đăng nhập của bạn đã hết hạn!' -valid_session: '&2Phiên đăng nhập đã được kết nối trở lại.' +session: + valid_session: '&2Phiên đăng nhập đã được kết nối trở lại.' + invalid_session: '&cIP của bạn đã bị thay đổi và phiên đăng nhập của bạn đã hết hạn!' # Error messages when joining -name_len: '&4Tên đăng nhập của bạn quá ngắn hoặc quá dài!' -regex: '&4Tên nhân vật có chứa ký tự không hợp lệ. Các ký tự được cho phép: REG_EX' -country_banned: '&4Quốc gia của bạn bị cấm tham gia máy chủ này!' -not_owner_error: 'Bạn không phải là chủ sở hữu tài khoản này, hãy chọn tên khác!' -kick_fullserver: '&4Máy chủ quá tải, vui lòng thử lại sau!' -same_nick: '&4Tài khoản đang được sử dụng trên máy chủ!' -invalid_name_case: 'Bạn nên vào máy chủ với tên đăng nhập là %valid, không phải là %invalid.' -same_ip_online: 'Một người chơi với cùng địa chỉ IP đã đang chơi!' +on_join_validation: + same_ip_online: 'Một người chơi với cùng địa chỉ IP đã đang chơi!' + same_nick_online: '&4Tài khoản đang được sử dụng trên máy chủ!' + name_length: '&4Tên đăng nhập của bạn quá ngắn hoặc quá dài!' + characters_in_name: '&4Tên nhân vật có chứa ký tự không hợp lệ. Các ký tự được cho phép: %valid_chars' + kick_full_server: '&4Máy chủ quá tải, vui lòng thử lại sau!' + country_banned: '&4Quốc gia của bạn bị cấm tham gia máy chủ này!' + not_owner_error: 'Bạn không phải là chủ sở hữu tài khoản này, hãy chọn tên khác!' + invalid_name_case: 'Bạn nên vào máy chủ với tên đăng nhập là %valid, không phải là %invalid.' # Email -usage_email_add: '&cSử dụng: /email add ' -usage_email_change: '&cSử dụng: /email change ' -usage_email_recovery: '&cSử dụng: /email recovery ' -new_email_invalid: '&cEmail mới không hợp lệ, vui lòng thử lại!' -old_email_invalid: '&cEmail cũ không hợp lệ, vui lòng thử lại!' -email_invalid: '&cĐại chỉ email không hợp lệ, vui lòng thử lại!' -email_added: '&2Địa chỉ email đã thêm vào tài khoản của bạn thành công!' -email_confirm: '&cVui lòng xác nhận địa chỉ email của bạn!' -email_changed: '&2Địa chỉ email đã thay đổi!' -email_send: '&2Email phục hồi đã được gửi thành công! Vui lòng kiểm tra hộp thư đến trong email của bạn.' -email_show: '&2Địa chỉ email hiện tại của bạn là: &f%email' -incomplete_email_settings: 'Lỗi: các thiết lập để gửi thư không được cài đặt đầy đủ. Vui lòng liên hệ với quản trị viên để thông báo lỗi.' -email_already_used: '&4Địa chỉ email đã được sử dụng' -email_send_failure: 'Không thể gửi thư. Vui lòng liên hệ với ban quản trị.' -show_no_email: '&2Hiện tại bạn chưa liên kết bất kỳ email nào với tài khoản này.' -add_email: '&eVui lòng thêm email của bạn với lệnh "/email add "' -recovery_email: '&aBạn quên mật khẩu? Vui lòng gõ lệnh "/email recovery "' -change_password_expired: '&cBạn không thể thay đổi mật khẩu bằng lệnh này từ nay.' -email_cooldown_error: '&cMột bức thư đã được gửi gần đây. Bạn phải chờ %time trước khi có thể gửi một bức thư mới.' +email: + add_email_request: '&eVui lòng thêm email của bạn với lệnh "/email add "' + usage_email_add: '&cSử dụng: /email add ' + usage_email_change: '&cSử dụng: /email change ' + new_email_invalid: '&cEmail mới không hợp lệ, vui lòng thử lại!' + old_email_invalid: '&cEmail cũ không hợp lệ, vui lòng thử lại!' + invalid: '&cĐại chỉ email không hợp lệ, vui lòng thử lại!' + added: '&2Địa chỉ email đã thêm vào tài khoản của bạn thành công!' + request_confirmation: '&cVui lòng xác nhận địa chỉ email của bạn!' + changed: '&2Địa chỉ email đã thay đổi!' + email_show: '&2Địa chỉ email hiện tại của bạn là: &f%email' + no_email_for_account: '&2Hiện tại bạn chưa liên kết bất kỳ email nào với tài khoản này.' + already_used: '&4Địa chỉ email đã được sử dụng' + incomplete_settings: 'Lỗi: các thiết lập để gửi thư không được cài đặt đầy đủ. Vui lòng liên hệ với quản trị viên để thông báo lỗi.' + send_failure: 'Không thể gửi thư. Vui lòng liên hệ với ban quản trị.' + change_password_expired: '&cBạn không thể thay đổi mật khẩu bằng lệnh này từ nay.' + email_cooldown_error: '&cMột bức thư đã được gửi gần đây. Bạn phải chờ %time trước khi có thể gửi một bức thư mới.' + +# Password recovery by email +recovery: + forgot_password_hint: '&aBạn quên mật khẩu? Vui lòng gõ lệnh "/email recovery "' + command_usage: '&cSử dụng: /email recovery ' + email_sent: '&2Email phục hồi đã được gửi thành công! Vui lòng kiểm tra hộp thư đến trong email của bạn.' + code: + code_sent: 'Một mã khôi phục mật khẩu đã được gửi đến địa chỉ email của bạn.' + incorrect: 'Mã khôi phục không đúng! Dùng lệnh /email recovery [email] để tạo một mã mới' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&eĐể đăng nhập vui lòng hãy nhập mã Captcha, gõ lệnh "/captcha "' -wrong_captcha: '&cSai mã captcha, Vui lòng gõ lệnh "/captcha THE_CAPTCHA"!' -valid_captcha: '&2Mã captcha đã được xác nhận!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&eĐể đăng nhập vui lòng hãy nhập mã Captcha, gõ lệnh "/captcha %captcha_code"' + wrong_captcha: '&cSai mã captcha, Vui lòng gõ lệnh "/captcha %captcha_code"!' + valid_captcha: '&2Mã captcha đã được xác nhận!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: 'giây' -seconds: 'giây' -minute: 'phút' -minutes: 'phút' -hour: 'giờ' -hours: 'giờ' -day: 'ngày' -days: 'ngày' +time: + second: 'giây' + seconds: 'giây' + minute: 'phút' + minutes: 'phút' + hour: 'giờ' + hours: 'giờ' + day: 'ngày' + days: 'ngày' diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index 849e0404d..ac7fdc25d 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -1,119 +1,136 @@ -# 注册 -reg_msg: '&8[&6玩家系统&8] &c请输入“/register <密码> <再输入一次以确定密码>”以注册' -usage_reg: '&8[&6玩家系统&8] &c正确用法:“/register <密码> <再输入一次以确定密码>”' -reg_only: '&8[&6玩家系统&8] &f只允许注册过的玩家进服!请到 https://example.cn 注册' -kicked_admin_registered: '有一位管理员刚刚为您完成了注册,请重新登录' -registered: '&8[&6玩家系统&8] &c已成功注册!' -reg_disabled: '&8[&6玩家系统&8] &c目前服务器暂时禁止注册,请到服务器论坛以得到更多资讯' -user_regged: '&8[&6玩家系统&8] &c此用户已经在此服务器注册过' +# Registration +registration: + disabled: '&8[&6玩家系统&8] &c目前服务器暂时禁止注册,请到服务器论坛以得到更多资讯' + name_taken: '&8[&6玩家系统&8] &c此用户已经在此服务器注册过' + register_request: '&8[&6玩家系统&8] &c请输入“/register <密码> <再输入一次以确定密码>”以注册' + command_usage: '&8[&6玩家系统&8] &c正确用法:“/register <密码> <再输入一次以确定密码>”' + reg_only: '&8[&6玩家系统&8] &f只允许注册过的玩家进服!请到 https://example.cn 注册' + success: '&8[&6玩家系统&8] &c已成功注册!' + kicked_admin_registered: '有一位管理员刚刚为您完成了注册,请重新登录' -# 注册时密码错误 -password_error: '&8[&6玩家系统&8] &f密码不相同' -password_error_nick: '&8[&6玩家系统&8] &f你不能使用你的名字作为密码。 ' -password_error_unsafe: '&8[&6玩家系统&8] &f你不能使用安全性过低的密码。 ' -password_error_chars: '&4您的密码包含了非法字符。可使用的字符: REG_EX' -pass_len: '&8[&6玩家系统&8] &你的密码没有达到要求!' +# Password errors on registration +password: + match_error: '&8[&6玩家系统&8] &f密码不相同' + name_in_password: '&8[&6玩家系统&8] &f你不能使用你的名字作为密码。 ' + unsafe_password: '&8[&6玩家系统&8] &f你不能使用安全性过低的密码。 ' + forbidden_characters: '&4您的密码包含了非法字符。可使用的字符: %valid_chars' + wrong_length: '&8[&6玩家系统&8] &你的密码没有达到要求!' -# 登录 -usage_log: '&8[&6玩家系统&8] &c正确用法:“/login <密码>”' -wrong_pwd: '&8[&6玩家系统&8] &c错误的密码' -login: '&8[&6玩家系统&8] &c已成功登录!' -login_msg: '&8[&6玩家系统&8] &c请输入“/login <密码>”以登录' -timeout: '&8[&6玩家系统&8] &f给你登录的时间已经过了' +# Login +login: + command_usage: '&8[&6玩家系统&8] &c正确用法:“/login <密码>”' + wrong_password: '&8[&6玩家系统&8] &c错误的密码' + success: '' + login_request: '&8[&6玩家系统&8] &c请输入“/login <密码>”以登录' + timeout_error: '&8[&6玩家系统&8] &f给你登录的时间已经过了' -# 出错 -unknown_user: '&8[&6玩家系统&8] &c此用户名还未注册过' -denied_command: '&c您需要先通过验证才能使用该命令!' -denied_chat: '&c您需要先通过验证才能聊天!' -not_logged_in: '&8[&6玩家系统&8] &c你还未登录!' -tempban_max_logins: '&c由于您登录失败次数过多,已被暂时禁止登录。' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&8[&6玩家系统&8] &f你不允许再为你的IP在服务器注册更多用户了!' -no_perm: '&8[&6玩家系统&8] &c没有权限' -error: '&8[&6玩家系统&8] &f发现错误,请联系管理员' -kick_forvip: '&8[&6玩家系统&8] &cA VIP玩家加入了已满的服务器!' +# Errors +error: + denied_command: '&c您需要先通过验证才能使用该命令!' + denied_chat: '&c您需要先通过验证才能聊天!' + unregistered_user: '&8[&6玩家系统&8] &c此用户名还未注册过' + not_logged_in: '&8[&6玩家系统&8] &c你还未登录!' + no_permission: '&8[&6玩家系统&8] &c没有权限' + unexpected_error: '' + max_registration: '&8[&6玩家系统&8] &f你不允许再为你的IP在服务器注册更多用户了!' + logged_in: '&8[&6玩家系统&8] &c你已经登陆过了!' + kick_for_vip: '&8[&6玩家系统&8] &cA VIP玩家加入了已满的服务器!' + tempban_max_logins: '&c由于您登录失败次数过多,已被暂时禁止登录。' -# 防机器人 -kick_antibot: '&8[&6玩家系统&8] &f防机器人程序已启用 !请稍等几分钟后再次进入服务器' -antibot_auto_enabled: '&8[&6玩家系统&8] &f防机器人程序由于大量异常连接而启用' -antibot_auto_disabled: '&8[&6玩家系统&8] &f防机器人程序由于异常连接减少而在 %m 分钟后停止' +# AntiBot +antibot: + kick_antibot: '&8[&6玩家系统&8] &f防机器人程序已启用 !请稍等几分钟后再次进入服务器' + auto_enabled: '&8[&6玩家系统&8] &f防机器人程序由于大量异常连接而启用' + auto_disabled: '&8[&6玩家系统&8] &f防机器人程序由于异常连接减少而在 %m 分钟后停止' -# 其他消息 -unregistered: '&8[&6玩家系统&8] &c成功删除此用户!' -accounts_owned_self: '您拥有 %count 个账户:' -accounts_owned_other: '玩家 %name 拥有 %count 个账户:' -two_factor_create: '&8[&6玩家系统&8] &2你的代码是 %code,你可以使用 %url 来扫描' -recovery_code_sent: '一个用于重置您的密码的验证码已发到您的邮箱' -# TODO: Missing tags %count -recovery_code_incorrect: '验证码不正确! 使用 /email recovery [邮箱] 以生成新的验证码' -recovery_tries_exceeded: '您已经达到输入验证码次数的最大允许次数。请使用 "/email recovery [邮箱]" 来生成一个新的' -recovery_code_correct: '验证码正确!' -recovery_change_password: '请使用 /email setpassword <新密码> 立即设置新的密码' -vb_nonActiv: '&8[&6玩家系统&8] &f你的帐号还未激活,请查看你的邮箱!' -usage_unreg: '&8[&6玩家系统&8] &c正确用法:“/unregister <密码>”' -pwd_changed: '&8[&6玩家系统&8] &c密码已成功修改!' -logged_in: '&8[&6玩家系统&8] &c你已经登陆过了!' -logout: '&8[&6玩家系统&8] &c已成功登出!' -reload: '&8[&6玩家系统&8] &f配置以及数据已经重新加载完毕' -usage_changepassword: '&8[&6玩家系统&8] &f正确用法:“/changepassword 旧密码 新密码”' +# Unregister +unregister: + success: '&8[&6玩家系统&8] &c成功删除此用户!' + command_usage: '&8[&6玩家系统&8] &c正确用法:“/unregister <密码>”' -# 会话消息 -invalid_session: '&8[&6玩家系统&8] &f登录数据异常,请等待登录结束' -valid_session: '&8[&6玩家系统&8] &c欢迎回来,已帮你自动登录到此服务器' +# Other messages +misc: + account_not_activated: '&8[&6玩家系统&8] &f你的帐号还未激活,请查看你的邮箱!' + password_changed: '&8[&6玩家系统&8] &c密码已成功修改!' + logout: '&8[&6玩家系统&8] &c已成功登出!' + reload: '&8[&6玩家系统&8] &f配置以及数据已经重新加载完毕' + usage_change_password: '&8[&6玩家系统&8] &f正确用法:“/changepassword 旧密码 新密码”' + two_factor_create: '&8[&6玩家系统&8] &2你的代码是 %code,你可以使用 %url 来扫描' + accounts_owned_self: '您拥有 %count 个账户:' + accounts_owned_other: '玩家 %name 拥有 %count 个账户:' -# 加入时出错 -name_len: '&8[&6玩家系统&8] &c你的用户名太短或者太长了' -regex: '&8[&6玩家系统&8] &c你的用户名包含非法字母,用户名里允许的字母: REG_EX' -country_banned: '这个服务器禁止该国家登陆' -not_owner_error: '&8[&6玩家系统&8] &4警告! &c你并不是此帐户持有人,请立即登出。 ' -kick_fullserver: '&8[&6玩家系统&8] &c抱歉,服务器已满!' -same_nick: '&8[&6玩家系统&8] &f同样的用户名现在在线且已经登录了!' -invalid_name_case: '&8[&6玩家系统&8] &c你应该使用「%valid」而并非「%invalid」登入游戏。 ' -same_ip_online: '已有一个同IP玩家在游戏中了!' +# Session messages +session: + valid_session: '&8[&6玩家系统&8] &c欢迎回来,已帮你自动登录到此服务器' + invalid_session: '&8[&6玩家系统&8] &f登录数据异常,请等待登录结束' -# 电子邮件 -usage_email_add: '&8[&6玩家系统&8] &f用法: /email add <邮箱> <确认电子邮件> ' -usage_email_change: '&8[&6玩家系统&8] &f用法: /email change <旧邮箱> <新邮箱> ' -usage_email_recovery: '&8[&6玩家系统&8] &f用法: /email recovery <邮箱>' -new_email_invalid: '&8[&6玩家系统&8] &f新邮箱无效!' -old_email_invalid: '&8[&6玩家系统&8] &f旧邮箱无效!' -email_invalid: '&8[&6玩家系统&8] &f无效的邮箱' -email_added: '&8[&6玩家系统&8] &f邮箱已添加 !' -email_confirm: '&8[&6玩家系统&8] &f确认你的邮箱 !' -email_changed: '&8[&6玩家系统&8] &f邮箱已改变 !' -email_send: '&8[&6玩家系统&8] &f恢复邮件已发送 !' -email_show: '&2您当前的电子邮件地址为: &f%email' -incomplete_email_settings: '错误:并非所有发送邮件需要的设置都已被设置,请联系管理员' -email_already_used: '&8[&6玩家系统&8] &4邮箱已被使用' -email_send_failure: '邮件发送失败,请联系管理员' -show_no_email: '&2您当前并没有任何邮箱与该账号绑定' -add_email: '&8[&6玩家系统&8] &c请输入“/email add <你的邮箱> <再输入一次以确认>”以把你的邮箱添加到此帐号' -recovery_email: '&8[&6玩家系统&8] &c忘了你的密码?请输入:“/email recovery <你的邮箱>”' -change_password_expired: '您不能使用此命令更改密码' -email_cooldown_error: '&c邮件已在几分钟前发送,您需要等待 %time 后才能再次请求发送' +# Error messages when joining +on_join_validation: + same_ip_online: '已有一个同IP玩家在游戏中了!' + same_nick_online: '&8[&6玩家系统&8] &f同样的用户名现在在线且已经登录了!' + name_length: '&8[&6玩家系统&8] &c你的用户名太短或者太长了' + characters_in_name: '&8[&6玩家系统&8] &c你的用户名包含非法字母,用户名里允许的字母: %valid_chars' + kick_full_server: '&8[&6玩家系统&8] &c抱歉,服务器已满!' + country_banned: '这个服务器禁止该国家登陆' + not_owner_error: '&8[&6玩家系统&8] &4警告! &c你并不是此帐户持有人,请立即登出。 ' + invalid_name_case: '&8[&6玩家系统&8] &c你应该使用「%valid」而并非「%invalid」登入游戏。 ' -# 验证码 -usage_captcha: '&8[&6玩家系统&8] &c正确用法:/captcha ' -wrong_captcha: '&8[&6玩家系统&8] &c错误的验证码,请输入:“/captcha THE_CAPTCHA”' -valid_captcha: '&8[&6玩家系统&8] &c你的验证码是有效的!' -captcha_for_registration: '注册前您需要先提供验证码,请使用指令:/captcha ' -register_captcha_valid: '&2有效的验证码!您现在可以使用 /register 注册啦!' +# Email +email: + add_email_request: '&8[&6玩家系统&8] &c请输入“/email add <你的邮箱> <再输入一次以确认>”以把你的邮箱添加到此帐号' + usage_email_add: '&8[&6玩家系统&8] &f用法: /email add <邮箱> <确认电子邮件> ' + usage_email_change: '&8[&6玩家系统&8] &f用法: /email change <旧邮箱> <新邮箱> ' + new_email_invalid: '&8[&6玩家系统&8] &f新邮箱无效!' + old_email_invalid: '&8[&6玩家系统&8] &f旧邮箱无效!' + invalid: '&8[&6玩家系统&8] &f无效的邮箱' + added: '&8[&6玩家系统&8] &f邮箱已添加 !' + request_confirmation: '&8[&6玩家系统&8] &f确认你的邮箱 !' + changed: '&8[&6玩家系统&8] &f邮箱已改变 !' + email_show: '&2您当前的电子邮件地址为: &f%email' + no_email_for_account: '&2您当前并没有任何邮箱与该账号绑定' + already_used: '&8[&6玩家系统&8] &4邮箱已被使用' + incomplete_settings: '错误:并非所有发送邮件需要的设置都已被设置,请联系管理员' + send_failure: '邮件发送失败,请联系管理员' + change_password_expired: '您不能使用此命令更改密码' + email_cooldown_error: '&c邮件已在几分钟前发送,您需要等待 %time 后才能再次请求发送' -# 验证码 -verification_code_required: '&3这个命令非常敏感,需要电子邮件验证!请检查您的收件箱,并遵循邮件的指导。' -usage_verification_code: '&c使用方法: /verification <验证码>' -incorrect_verification_code: '&c验证码错误, 请在聊天框输入 "/verification <验证码>",使用您在电子邮件中收到的验证码。' -verification_code_verified: '&2您的身份已经得到验证!您现在可以在当前会话中执行所有命令!' -verification_code_already_verified: '&2您已经可以在当前会话中执行任何敏感命令!' -verification_code_expired: '&3您的验证码已失效!执行另一个敏感命令以获得新的验证码!' -verification_code_email_needed: '&3为了验证您的身份,您需要将一个电子邮件地址与您的帐户绑定!!' +# Password recovery by email +recovery: + forgot_password_hint: '&8[&6玩家系统&8] &c忘了你的密码?请输入:“/email recovery <你的邮箱>”' + command_usage: '&8[&6玩家系统&8] &f用法: /email recovery <邮箱>' + email_sent: '&8[&6玩家系统&8] &f恢复邮件已发送 !' + code: + code_sent: '一个用于重置您的密码的验证码已发到您的邮箱' + incorrect: '验证码不正确! 使用 /email recovery [邮箱] 以生成新的验证码' + tries_exceeded: '您已经达到输入验证码次数的最大允许次数。请使用 "/email recovery [邮箱]" 来生成一个新的' + correct: '验证码正确!' + change_password: '请使用 /email setpassword <新密码> 立即设置新的密码' -# 时间单位 -second: '秒' -seconds: '秒' -minute: '分钟' -minutes: '分钟' -hour: '小时' -hours: '小时' -day: '天' -days: '天' +# Captcha +captcha: + usage_captcha: '&8[&6玩家系统&8] &c正确用法:/captcha %captcha_code' + wrong_captcha: '&8[&6玩家系统&8] &c错误的验证码,请输入:“/captcha %captcha_code”' + valid_captcha: '&8[&6玩家系统&8] &c你的验证码是有效的!' + captcha_for_registration: '注册前您需要先提供验证码,请使用指令:/captcha %captcha_code' + register_captcha_valid: '&2有效的验证码!您现在可以使用 /register 注册啦!' + +# Verification code +verification: + code_required: '&3这个命令非常敏感,需要电子邮件验证!请检查您的收件箱,并遵循邮件的指导。' + command_usage: '&c使用方法: /verification <验证码>' + incorrect_code: '&c验证码错误, 请在聊天框输入 "/verification <验证码>",使用您在电子邮件中收到的验证码。' + success: '&2您的身份已经得到验证!您现在可以在当前会话中执行所有命令!' + already_verified: '&2您已经可以在当前会话中执行任何敏感命令!' + code_expired: '&3您的验证码已失效!执行另一个敏感命令以获得新的验证码!' + email_needed: '&3为了验证您的身份,您需要将一个电子邮件地址与您的帐户绑定!!' + +# Time units +time: + second: '秒' + seconds: '秒' + minute: '分钟' + minutes: '分钟' + hour: '小时' + hours: '小时' + day: '天' + days: '天' diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index e7031dae0..74c5d71ea 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -3,119 +3,138 @@ # -------------------------------------------- # # Registration -reg_msg: '&8[&6用戶系統&8] &c請使用這個指令來註冊:《 /register <密碼> <重覆密碼> 》' -usage_reg: '&8[&6用戶系統&8] &f用法:《 /register <密碼> <重覆密碼> 》' -reg_only: '&8[&6用戶系統&8] &f限已註冊會員,請先到本服網站進行註冊。' -kicked_admin_registered: '&8[&6用戶系統&8] &b管理員已替你完成註冊,請重新登入。' -registered: '&8[&6用戶系統&8] &b你成功註冊了。' -reg_disabled: '&8[&6用戶系統&8] &c本伺服器已停止新玩家註冊。' -user_regged: '&8[&6用戶系統&8] &c此用戶名已經註冊過了。' +registration: + disabled: '&8[&6用戶系統&8] &c本伺服器已停止新玩家註冊。' + name_taken: '&8[&6用戶系統&8] &c此用戶名已經註冊過了。' + register_request: '&8[&6用戶系統&8] &c請使用這個指令來註冊:《 /register <密碼> <重覆密碼> 》' + command_usage: '&8[&6用戶系統&8] &f用法:《 /register <密碼> <重覆密碼> 》' + reg_only: '&8[&6用戶系統&8] &f限已註冊會員,請先到本服網站進行註冊。' + success: '&8[&6用戶系統&8] &b你成功註冊了。' + kicked_admin_registered: '&8[&6用戶系統&8] &b管理員已替你完成註冊,請重新登入。' # Password errors on registration -password_error: '&8[&6用戶系統&8] &f密碼不符合。' -password_error_nick: '&8[&6用戶系統&8] &c這個密碼太不安全了!' -password_error_unsafe: '&8[&6用戶系統&8] &c這個密碼太不安全了!' -password_error_chars: '&8[&6用戶系統&8] &4密碼字符只能含有這些喔:REG_EX' -pass_len: '&8[&6用戶系統&8] &f嗯,你的密碼並不符合規定長度。' +password: + match_error: '&8[&6用戶系統&8] &f密碼不符合。' + name_in_password: '&8[&6用戶系統&8] &c這個密碼太不安全了!' + unsafe_password: '&8[&6用戶系統&8] &c這個密碼太不安全了!' + forbidden_characters: '&8[&6用戶系統&8] &4密碼字符只能含有這些喔:%valid_chars' + wrong_length: '&8[&6用戶系統&8] &f嗯,你的密碼並不符合規定長度。' # Login -usage_log: '&8[&6用戶系統&8] &f用法:《 /login <密碼> 》' -wrong_pwd: '&8[&6用戶系統&8] &c你輸入了錯誤的密碼。' -login: '&8[&6用戶系統&8] &a你成功登入了。' -login_msg: '&8[&6用戶系統&8] &c請使用這個指令來登入:《 /login <密碼> 》' -timeout: '&8[&6用戶系統&8] &f登入逾時。' +login: + command_usage: '&8[&6用戶系統&8] &f用法:《 /login <密碼> 》' + wrong_password: '&8[&6用戶系統&8] &c你輸入了錯誤的密碼。' + success: '' + login_request: '&8[&6用戶系統&8] &c請使用這個指令來登入:《 /login <密碼> 》' + timeout_error: '&8[&6用戶系統&8] &f登入逾時。' # Errors -unknown_user: '&8[&6用戶系統&8] &c此用戶名沒有已登記資料。' -denied_command: '&8[&6用戶系統&8] &c請先登入以便使用此指令。' -denied_chat: '&8[&6用戶系統&8] &c請先登入以便與其他玩家聊天。' -not_logged_in: '&8[&6用戶系統&8] &c你還沒有登入 !' -tempban_max_logins: '&8[&6用戶系統&8] &c因為多次登入失敗,你已被暫時封禁。' -max_reg: '&8[&6用戶系統&8] &f你的IP地址已達到註冊數上限。 &7(info: %reg_count/%max_acc %reg_names)' -no_perm: '&8[&6用戶系統&8] &b嗯~你想幹甚麼?' -error: '&8[&6用戶系統&8] &f發生錯誤,請與管理員聯絡。' -kick_forvip: '&c喔!因為有VIP玩家登入了伺服器。' +error: + denied_command: '&8[&6用戶系統&8] &c請先登入以便使用此指令。' + denied_chat: '&8[&6用戶系統&8] &c請先登入以便與其他玩家聊天。' + unregistered_user: '&8[&6用戶系統&8] &c此用戶名沒有已登記資料。' + not_logged_in: '&8[&6用戶系統&8] &c你還沒有登入 !' + no_permission: '&8[&6用戶系統&8] &b嗯~你想幹甚麼?' + unexpected_error: '' + max_registration: '&8[&6用戶系統&8] &f你的IP地址已達到註冊數上限。 &7(info: %reg_count/%max_acc %reg_names)' + logged_in: '&8[&6用戶系統&8] &c你已經登入過了。' + kick_for_vip: '&c喔!因為有VIP玩家登入了伺服器。' + tempban_max_logins: '&8[&6用戶系統&8] &c因為多次登入失敗,你已被暫時封禁。' # AntiBot -kick_antibot: '&8[&6用戶系統&8] &c伺服器錯誤!請稍候再嘗試登入吧。 &7(err: kick_due2_bot)' -antibot_auto_enabled: '&8[&6用戶系統&8] &3防止機械人程序已因應現時大量不尋常連線而啟用。' -antibot_auto_disabled: '&8[&6用戶系統&8] &3不正常連接數已減少,防止機械人程序將於 %m 分鐘後停止。' +antibot: + kick_antibot: '&8[&6用戶系統&8] &c伺服器錯誤!請稍候再嘗試登入吧。 &7(err: kick_due2_bot)' + auto_enabled: '&8[&6用戶系統&8] &3防止機械人程序已因應現時大量不尋常連線而啟用。' + auto_disabled: '&8[&6用戶系統&8] &3不正常連接數已減少,防止機械人程序將於 %m 分鐘後停止。' + +# Unregister +unregister: + success: '&8[&6用戶系統&8] &c你已成功刪除會員註冊記錄。' + command_usage: '&8[&6用戶系統&8] &f用法:《 /unregister <密碼> 》' # Other messages -unregistered: '&8[&6用戶系統&8] &c你已成功刪除會員註冊記錄。' -accounts_owned_self: '你擁有 %count 個帳戶:' -accounts_owned_other: '玩家《%name》擁有 %count 個帳戶:' -two_factor_create: '&8[&6用戶系統 - 兩步驗證碼&8] &b你的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' -recovery_code_sent: '&8[&6用戶系統&8] &b帳戶驗證碼已發送到你的郵箱,請查收。' -recovery_code_incorrect: '&8[&6用戶系統&8] &c帳戶驗證碼無效。 &7(你尚餘 %count 次嘗試機會)' -recovery_tries_exceeded: '&8[&6用戶系統&8] &c此驗證碼已因多次嘗試後失效,請使用《 /email recovery <電郵地址> 》重新辦理。' -recovery_code_correct: '&8[&6用戶系統&8] &a帳戶驗證碼輸入正確。' -recovery_change_password: '&8[&6用戶系統&8] 請立即使用《 /email setpassword <新密碼> 》指令,以重設你的帳戶密碼。' -vb_nonActiv: '&8[&6用戶系統&8] &f你的帳戶還沒有經過電郵驗證 !' -usage_unreg: '&8[&6用戶系統&8] &f用法:《 /unregister <密碼> 》' -pwd_changed: '&8[&6用戶系統&8] &c你成功更換了你的密碼 !' -logged_in: '&8[&6用戶系統&8] &c你已經登入過了。' -logout: '&8[&6用戶系統&8] &b你成功登出了。' -reload: '&8[&6用戶系統&8] &b登入系統設定及資料庫重新載入完畢。' -usage_changepassword: '&8[&6用戶系統&8] &f用法:《 /changepassword <舊密碼> <新密碼> 》' +misc: + account_not_activated: '&8[&6用戶系統&8] &f你的帳戶還沒有經過電郵驗證 !' + password_changed: '&8[&6用戶系統&8] &c你成功更換了你的密碼 !' + logout: '&8[&6用戶系統&8] &b你成功登出了。' + reload: '&8[&6用戶系統&8] &b登入系統設定及資料庫重新載入完畢。' + usage_change_password: '&8[&6用戶系統&8] &f用法:《 /changepassword <舊密碼> <新密碼> 》' + two_factor_create: '&8[&6用戶系統 - 兩步驗證碼&8] &b你的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' + accounts_owned_self: '你擁有 %count 個帳戶:' + accounts_owned_other: '玩家《%name》擁有 %count 個帳戶:' # Session messages -invalid_session: '&8[&6用戶系統&8] &f登入階段資料已損壞,請等待登入階段結束。' -valid_session: '&8[&6用戶系統&8] &b嗨 ! 歡迎回來喔~' +session: + valid_session: '&8[&6用戶系統&8] &b嗨 ! 歡迎回來喔~' + invalid_session: '&8[&6用戶系統&8] &f登入階段資料已損壞,請等待登入階段結束。' # Error messages when joining -name_len: '&8[&6用戶系統&8] &c你的用戶名不符合規定長度。' -regex: '&8[&6用戶系統&8] &c用戶名稱錯誤! 登入系統只接受以下字符:REG_EX' -country_banned: '&8[&6用戶系統&8] &4本伺服器已停止對你的國家提供遊戲服務。' -not_owner_error: '&8[&6用戶系統&8] &4警告!&c你並不是此帳戶持有人,請立即登出。' -kick_fullserver: '&c抱歉! 因為伺服器滿人了,所以你目前未能登入伺服器。' -same_nick: '&8[&6用戶系統&8] &f同名玩家已在遊玩。' -invalid_name_case: '&8[&6用戶系統&8] &4警告!&c你應該使用「%valid」而並非「%invalid」登入遊戲。' -same_ip_online: '&8[&6用戶系統&8] 你正使用的 IP 位址已被其他玩家佔用。' +on_join_validation: + same_ip_online: '&8[&6用戶系統&8] 你正使用的 IP 位址已被其他玩家佔用。' + same_nick_online: '&8[&6用戶系統&8] &f同名玩家已在遊玩。' + name_length: '&8[&6用戶系統&8] &c你的用戶名不符合規定長度。' + characters_in_name: '&8[&6用戶系統&8] &c用戶名稱錯誤! 登入系統只接受以下字符:%valid_chars' + kick_full_server: '&c抱歉! 因為伺服器滿人了,所以你目前未能登入伺服器。' + country_banned: '&8[&6用戶系統&8] &4本伺服器已停止對你的國家提供遊戲服務。' + not_owner_error: '&8[&6用戶系統&8] &4警告!&c你並不是此帳戶持有人,請立即登出。' + invalid_name_case: '&8[&6用戶系統&8] &4警告!&c你應該使用「%valid」而並非「%invalid」登入遊戲。' # Email -usage_email_add: '&8[&6用戶系統&8] &f用法:《 /email add <電郵> <重覆電郵> 》' -usage_email_change: '&8[&6用戶系統&8] &f用法:《 /email change <舊電郵> <新電郵> 》' -usage_email_recovery: '&8[&6用戶系統&8] &f用法:《 /email recovery <電郵> 》' -new_email_invalid: '&8[&6用戶系統&8] &c你所填寫的新電郵地址並不正確。' -old_email_invalid: '&8[&6用戶系統&8] &c你所填寫的舊電郵地址並不正確。' -email_invalid: '&8[&6用戶系統&8] &c你所填寫的電郵地址並不正確。' -email_added: '&8[&6用戶系統&8] &a已新增你的電郵地址。' -email_confirm: '&8[&6用戶系統&8] &5請重覆輸入你的電郵地址。' -email_changed: '&8[&6用戶系統&8] &a你的電郵地址已更改。' -email_send: '&8[&6用戶系統&8] &a忘記密碼信件已寄出,請查收。' -email_show: '&8[&6用戶系統&8] &2你所使用的電郵地址為:&f%email' -incomplete_email_settings: '&8[&6用戶系統&8] &c電郵系統錯誤,請聯絡伺服器管理員。 &7(err: mailconfig_incomplete)' -email_already_used: '&8[&6用戶系統&8] &4這個電郵地址已被使用。' -email_send_failure: '&8[&6用戶系統&8] &c電郵系統錯誤,請聯絡伺服器管理員。 &7(err: smtperr)' -show_no_email: '&8[&6用戶系統&8] &2你並未有綁定電郵地址到此帳戶。' -add_email: '&8[&6用戶系統&8] &b請為你的帳戶立即添加電郵地址:《 /email add <電郵地址> <重覆電郵地址> 》' -recovery_email: '&8[&6用戶系統&8] &b忘記密碼?請使用 /email recovery <電郵地址> 來更新密碼。' -change_password_expired: '&8[&6用戶系統&8] 此指令已過期,請重新辦理。' -email_cooldown_error: '&8[&6用戶系統&8] &c你已經辦理過重寄郵件,請等待 %time 後再嘗試吧。' +email: + add_email_request: '&8[&6用戶系統&8] &b請為你的帳戶立即添加電郵地址:《 /email add <電郵地址> <重覆電郵地址> 》' + usage_email_add: '&8[&6用戶系統&8] &f用法:《 /email add <電郵> <重覆電郵> 》' + usage_email_change: '&8[&6用戶系統&8] &f用法:《 /email change <舊電郵> <新電郵> 》' + new_email_invalid: '&8[&6用戶系統&8] &c你所填寫的新電郵地址並不正確。' + old_email_invalid: '&8[&6用戶系統&8] &c你所填寫的舊電郵地址並不正確。' + invalid: '&8[&6用戶系統&8] &c你所填寫的電郵地址並不正確。' + added: '&8[&6用戶系統&8] &a已新增你的電郵地址。' + request_confirmation: '&8[&6用戶系統&8] &5請重覆輸入你的電郵地址。' + changed: '&8[&6用戶系統&8] &a你的電郵地址已更改。' + email_show: '&8[&6用戶系統&8] &2你所使用的電郵地址為:&f%email' + no_email_for_account: '&8[&6用戶系統&8] &2你並未有綁定電郵地址到此帳戶。' + already_used: '&8[&6用戶系統&8] &4這個電郵地址已被使用。' + incomplete_settings: '&8[&6用戶系統&8] &c電郵系統錯誤,請聯絡伺服器管理員。 &7(err: mailconfig_incomplete)' + send_failure: '&8[&6用戶系統&8] &c電郵系統錯誤,請聯絡伺服器管理員。 &7(err: smtperr)' + change_password_expired: '&8[&6用戶系統&8] 此指令已過期,請重新辦理。' + email_cooldown_error: '&8[&6用戶系統&8] &c你已經辦理過重寄郵件,請等待 %time 後再嘗試吧。' + +# Password recovery by email +recovery: + forgot_password_hint: '&8[&6用戶系統&8] &b忘記密碼?請使用 /email recovery <電郵地址> 來更新密碼。' + command_usage: '&8[&6用戶系統&8] &f用法:《 /email recovery <電郵> 》' + email_sent: '&8[&6用戶系統&8] &a忘記密碼信件已寄出,請查收。' + code: + code_sent: '&8[&6用戶系統&8] &b帳戶驗證碼已發送到你的郵箱,請查收。' + incorrect: '&8[&6用戶系統&8] &c帳戶驗證碼無效。 &7(你尚餘 %count 次嘗試機會)' + tries_exceeded: '&8[&6用戶系統&8] &c此驗證碼已因多次嘗試後失效,請使用《 /email recovery <電郵地址> 》重新辦理。' + correct: '&8[&6用戶系統&8] &a帳戶驗證碼輸入正確。' + change_password: '&8[&6用戶系統&8] 請立即使用《 /email setpassword <新密碼> 》指令,以重設你的帳戶密碼。' # Captcha -usage_captcha: '&8[&6用戶系統&8] &f用法:《 /captcha 》' -wrong_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效,請使用 《 /captcha THE_CAPTCHA 》 再次輸入。' -valid_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效 !' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&8[&6用戶系統&8] &f用法:《 /captcha %captcha_code 》' + wrong_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效,請使用 《 /captcha %captcha_code 》 再次輸入。' + valid_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效 !' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -second: '秒' -seconds: '秒' -minute: '分' -minutes: '分' -hour: '小時' -hours: '小時' -day: '日' -days: '日' +time: + second: '秒' + seconds: '秒' + minute: '分' + minutes: '分' + hour: '小時' + hours: '小時' + day: '日' + days: '日' diff --git a/src/main/resources/messages/messages_zhmc.yml b/src/main/resources/messages/messages_zhmc.yml index 5b21ba260..458bce2b1 100644 --- a/src/main/resources/messages/messages_zhmc.yml +++ b/src/main/resources/messages/messages_zhmc.yml @@ -1,118 +1,136 @@ # Registration -reg_msg: '&3[請先注冊] 請按T , 然後輸入 "/register [你的密碼] [重覆確認你的密碼]" 來注冊。' -usage_reg: '&c使用方法: 輸入"/register [你的密碼] [重覆確認你的密碼]"' -reg_only: '&4只有註冊用戶才能加入服務器! 請訪問http://example.com註冊!' -kicked_admin_registered: '管理員剛剛註冊了您; 請重新登錄。' -registered: '&2已成功註冊!' -reg_disabled: '&c遊戲內註冊已停用!' -user_regged: '&c您已經註冊此用戶名!' +registration: + disabled: '&c遊戲內註冊已停用!' + name_taken: '&c您已經註冊此用戶名!' + register_request: '&3[請先注冊] 請按T , 然後輸入 "/register [你的密碼] [重覆確認你的密碼]" 來注冊。' + command_usage: '&c使用方法: 輸入"/register [你的密碼] [重覆確認你的密碼]"' + reg_only: '&4只有註冊用戶才能加入服務器! 請訪問http://example.com註冊!' + success: '&2已成功註冊!' + kicked_admin_registered: '管理員剛剛註冊了您; 請重新登錄。' # Password errors on registration -password_error: '&密碼錯誤!' -password_error_nick: '&c你不能使用你的用戶名作密碼,請 choose another one...' -password_error_unsafe: '&c所選的你的密碼並不安全的,請選擇另一個...' -password_error_chars: '&4您的密碼含有非法字符。允許的字符:REG_EX' -pass_len: '&c你的密碼太短或太長!請嘗試另一個!' +password: + match_error: '&密碼錯誤!' + name_in_password: '&c你不能使用你的用戶名作密碼,請 choose another one...' + unsafe_password: '&c所選的你的密碼並不安全的,請選擇另一個...' + forbidden_characters: '&4您的密碼含有非法字符。允許的字符:%valid_chars' + wrong_length: '&c你的密碼太短或太長!請嘗試另一個!' # Login -usage_log: '&b使用方法: 輸入"/login [你的密碼]" 來登入' -wrong_pwd: '&c密碼錯誤!' -login: '&2你已成功登入!' -login_msg: '&c [請先登入] 請按T , 然後輸入 "/login [你的密碼]" 。' -timeout: '&4超過登錄超時,您已從伺服器中踢出,請重試!' +login: + command_usage: '&b使用方法: 輸入"/login [你的密碼]" 來登入' + wrong_password: '&c密碼錯誤!' + success: '' + login_request: '&c [請先登入] 請按T , 然後輸入 "/login [你的密碼]" 。' + timeout_error: '&4超過登錄超時,您已從伺服器中踢出,請重試!' # Errors -unknown_user: '&c此用戶尚未注冊!' -denied_command: '&c你必須得到權限來使用此指令!' -denied_chat: '&c你必須得到權限來使用聊天功能!' -not_logged_in: '&c你尚未登入!' -tempban_max_logins: '&c由於登錄失敗次數過多,您已被暫時禁止。' -max_reg: '&c您已超過註冊的最大數量(%reg_count/%max_acc %reg_names)!' -no_perm: '&4您沒有執行此操作的權限!' -error: '&4發生錯誤!請聯繫伺服器管理員!' -kick_forvip: '&3一名VIP玩家在服務器已滿時已加入伺服器!' +error: + denied_command: '&c你必須得到權限來使用此指令!' + denied_chat: '&c你必須得到權限來使用聊天功能!' + unregistered_user: '&c此用戶尚未注冊!' + not_logged_in: '&c你尚未登入!' + no_permission: '&4您沒有執行此操作的權限!' + unexpected_error: '' + max_registration: '&c您已超過註冊的最大數量(%reg_count/%max_acc %reg_names)!' + logged_in: '&c您已經登錄!' + kick_for_vip: '&3一名VIP玩家在服務器已滿時已加入伺服器!' + tempban_max_logins: '&c由於登錄失敗次數過多,您已被暫時禁止。' # AntiBot -kick_antibot: '伺服器正在啟用AntiBot保護模式! 您必須等待幾分鐘才能加入服務器。' -antibot_auto_enabled: '&4[AntiBotService] 伺服器由於連接數量龐大而啟用AntiBot!' -antibot_auto_disabled: '&2[AntiBotService] AntiBot將在%m分鐘後禁用!' +antibot: + kick_antibot: '伺服器正在啟用AntiBot保護模式! 您必須等待幾分鐘才能加入服務器。' + auto_enabled: '&4[AntiBotService] 伺服器由於連接數量龐大而啟用AntiBot!' + auto_disabled: '&2[AntiBotService] AntiBot將在%m分鐘後禁用!' + +# Unregister +unregister: + success: '&c帳戶已刪除!' + command_usage: '&c使用方法: "/unregister <你的密碼>"' # Other messages -unregistered: '&c帳戶已刪除!' -accounts_owned_self: '您擁有 %count 個帳戶:' -accounts_owned_other: '玩家 %name 擁有 %count 個帳戶:' -two_factor_create: '&2您的密碼是 %code。您可以從這裡掃描 %url' -recovery_code_sent: '已將重設密碼的恢復代碼發送到您的電子郵件。' -# TODO: Missing tags %count -recovery_code_incorrect: '恢復代碼錯誤!使用指令: "/email recovery [電郵地址]" 生成新的一個恢復代碼。' -# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' -# TODO recovery_code_correct: 'Recovery code entered correctly!' -# TODO recovery_change_password: 'Please use the command /email setpassword to change your password immediately.' -vb_nonActiv: '&c你的帳戶未激活,請確認電郵!' -usage_unreg: '&c使用方法: "/unregister <你的密碼>"' -pwd_changed: '&2密碼已更變!' -logged_in: '&c您已經登錄!' -logout: '&2已成功註銷!' -reload: '&2伺服器已正確地被重新加載配置和數據庫!' -usage_changepassword: '&c使用方法: "/changepassword [舊密碼] [新密碼]"' +misc: + account_not_activated: '&c你的帳戶未激活,請確認電郵!' + password_changed: '&2密碼已更變!' + logout: '&2已成功註銷!' + reload: '&2伺服器已正確地被重新加載配置和數據庫!' + usage_change_password: '&c使用方法: "/changepassword [舊密碼] [新密碼]"' + two_factor_create: '&2您的密碼是 %code。您可以從這裡掃描 %url' + accounts_owned_self: '您擁有 %count 個帳戶:' + accounts_owned_other: '玩家 %name 擁有 %count 個帳戶:' # Session messages -invalid_session: '&c您的IP已更改,並且您的會話數據已過期!' -valid_session: '&2由於會話重新連接而登錄.' +session: + valid_session: '&2由於會話重新連接而登錄.' + invalid_session: '&c您的IP已更改,並且您的會話數據已過期!' # Error messages when joining -name_len: '&4你的暱稱太短或太長!' -regex: '&4您的暱稱含有非法字符。允許的字符:REG_EX' -country_banned: '&4您的國家/地區已被禁止使用此伺服器!' -not_owner_error: '您不是此帳戶的所有者。 請選擇其他名稱!' -kick_fullserver: '&4服務器已滿,請稍後再試!' -same_nick: '&4伺服器上已經在使用相同的用戶名!' -invalid_name_case: '您應該使用用戶名 %valid 而不是 %invalid 來加入。' -same_ip_online: '一位同一IP的玩家已在遊戲中!' +on_join_validation: + same_ip_online: '一位同一IP的玩家已在遊戲中!' + same_nick_online: '&4伺服器上已經在使用相同的用戶名!' + name_length: '&4你的暱稱太短或太長!' + characters_in_name: '&4您的暱稱含有非法字符。允許的字符:%valid_chars' + kick_full_server: '&4服務器已滿,請稍後再試!' + country_banned: '&4您的國家/地區已被禁止使用此伺服器!' + not_owner_error: '您不是此帳戶的所有者。 請選擇其他名稱!' + invalid_name_case: '您應該使用用戶名 %valid 而不是 %invalid 來加入。' # Email -usage_email_add: '&c使用方法: "/email add [電郵地址] [confirmEmail]"' -usage_email_change: '&c使用方法: "/email change [舊電郵地址] [新電郵地址]"' -usage_email_recovery: '&c使用方法: "/email recovery [電郵地址]"' -new_email_invalid: '&c新電子郵件地址無效,請重試!' -old_email_invalid: '&c舊電子郵件地址無效,請重試!' -email_invalid: '&c電子郵件地址無效,請重試!' -email_added: '&2電子郵件地址已成功添加到您的帳戶!' -email_confirm: '&c請確認你的電郵地址!' -email_changed: '&2已正確地更改電子郵件地址!' -email_send: '&2帳戶恢復電子郵件已成功發送! 請檢查您的電子郵件收件箱!' -# TODO email_show: '&2Your current email address is: &f%email' -incomplete_email_settings: '缺少必要的配置來為發送電子郵件。請聯繫管理員。' -email_already_used: '&4此電子郵件地址已被使用' -# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -add_email: '&3請使用命令: /email add [你的電郵地址] [重覆確認你的電郵地址] 將您的電子郵件添加到您的帳戶"' -recovery_email: '&3忘記密碼了嗎? 請使用命令: "/email recovery [你的電郵地址]"' -# TODO change_password_expired: 'You cannot change your password using this command anymore.' -# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' +email: + add_email_request: '&3請使用命令: /email add [你的電郵地址] [重覆確認你的電郵地址] 將您的電子郵件添加到您的帳戶"' + usage_email_add: '&c使用方法: "/email add [電郵地址] [confirmEmail]"' + usage_email_change: '&c使用方法: "/email change [舊電郵地址] [新電郵地址]"' + new_email_invalid: '&c新電子郵件地址無效,請重試!' + old_email_invalid: '&c舊電子郵件地址無效,請重試!' + invalid: '&c電子郵件地址無效,請重試!' + added: '&2電子郵件地址已成功添加到您的帳戶!' + request_confirmation: '&c請確認你的電郵地址!' + changed: '&2已正確地更改電子郵件地址!' + email_show: '' + no_email_for_account: '' + already_used: '&4此電子郵件地址已被使用' + incomplete_settings: '缺少必要的配置來為發送電子郵件。請聯繫管理員。' + send_failure: '' + change_password_expired: '' + email_cooldown_error: '' + +# Password recovery by email +recovery: + forgot_password_hint: '&3忘記密碼了嗎? 請使用命令: "/email recovery [你的電郵地址]"' + command_usage: '&c使用方法: "/email recovery [電郵地址]"' + email_sent: '&2帳戶恢復電子郵件已成功發送! 請檢查您的電子郵件收件箱!' + code: + code_sent: '已將重設密碼的恢復代碼發送到您的電子郵件。' + incorrect: '恢復代碼錯誤!使用指令: "/email recovery [電郵地址]" 生成新的一個恢復代碼。' + tries_exceeded: '' + correct: '' + change_password: '' # Captcha -usage_captcha: '&3T要登錄您必須使用captcha驗證碼,請使用命令: "/captcha "' -wrong_captcha: '&c驗證碼錯誤!請按T在聊天中輸入 "/captcha THE_CAPTCHA"' -valid_captcha: '&2驗證碼正確!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&3T要登錄您必須使用captcha驗證碼,請使用命令: "/captcha %captcha_code"' + wrong_captcha: '&c驗證碼錯誤!請按T在聊天中輸入 "/captcha %captcha_code"' + valid_captcha: '&2驗證碼正確!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' -# TODO usage_verification_code: '&cUsage: /verification ' -# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' -# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!' -# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!' -# TODO verification_code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' -# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!' +verification: + code_required: '' + command_usage: '' + incorrect_code: '' + success: '' + already_verified: '' + code_expired: '' + email_needed: '' # Time units -# TODO second: 'second' -# TODO seconds: 'seconds' -# TODO minute: 'minute' -# TODO minutes: 'minutes' -# TODO hour: 'hour' -# TODO hours: 'hours' -# TODO day: 'day' -# TODO days: 'days' +time: + second: '' + seconds: '' + minute: '' + minutes: '' + hour: '' + hours: '' + day: '' + days: '' diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index 639a01720..81cfca473 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -2,120 +2,138 @@ # -------------------------------------------- # # Registration -reg_msg: '&b【AuthMe】&6請使用 "&c/register <密碼> <確認密碼>" 來註冊。' -usage_reg: '&b【AuthMe】&6用法: &c"/register <密碼> <確認密碼>"' -reg_only: '&b【AuthMe】&6請到下列網站 :「 https://example.tw 」 進行註冊' -kicked_admin_registered: '&b【AuthMe】&6管理員已協助您註冊,請重新登入' -registered: '&b【AuthMe】&6您已成功註冊' -reg_disabled: '&b【AuthMe】&6已關閉註冊功能' -user_regged: '&b【AuthMe】&6這個帳號已經被註冊過了!' +registration: + disabled: '&b【AuthMe】&6已關閉註冊功能' + name_taken: '&b【AuthMe】&6這個帳號已經被註冊過了!' + register_request: '&b【AuthMe】&6請使用 "&c/register <密碼> <確認密碼>" 來註冊。' + command_usage: '&b【AuthMe】&6用法: &c"/register <密碼> <確認密碼>"' + reg_only: '&b【AuthMe】&6請到下列網站 :「 https://example.tw 」 進行註冊' + success: '&b【AuthMe】&6您已成功註冊' + kicked_admin_registered: '&b【AuthMe】&6管理員已協助您註冊,請重新登入' # Password errors on registration -password_error: '&b【AuthMe】&6兩次輸入的密碼不一致!' -password_error_nick: '&b【AuthMe】&6您不可以用您的 ID (遊戲名稱) 來當作密碼 !' -password_error_unsafe: '&b【AuthMe】&6您不可以使用這個不安全的密碼' -password_error_chars: '&b【AuthMe】&c密碼包含非法字符. 可使用: REG_EX' -pass_len: '&b【AuthMe】&6您的密碼 超過最大字數 / 小於最小字數' +password: + match_error: '&b【AuthMe】&6兩次輸入的密碼不一致!' + name_in_password: '&b【AuthMe】&6您不可以用您的 ID (遊戲名稱) 來當作密碼 !' + unsafe_password: '&b【AuthMe】&6您不可以使用這個不安全的密碼' + forbidden_characters: '&b【AuthMe】&c密碼包含非法字符. 可使用: %valid_chars' + wrong_length: '&b【AuthMe】&6您的密碼 超過最大字數 / 小於最小字數' # Login -usage_log: '&b【AuthMe】&6用法: &c"/login <密碼>"' -wrong_pwd: '&b【AuthMe】&6密碼錯誤!' -login: '&b【AuthMe】&6密碼正確,您已成功登入!' -login_msg: '&b【AuthMe】&6請使用 &c"/login <密碼>" &6來登入。' -timeout: '&b【AuthMe】&6超過登入時間,請稍後再試一次' +login: + command_usage: '&b【AuthMe】&6用法: &c"/login <密碼>"' + wrong_password: '&b【AuthMe】&6密碼錯誤!' + success: '' + login_request: '&b【AuthMe】&6請使用 &c"/login <密碼>" &6來登入。' + timeout_error: '&b【AuthMe】&6超過登入時間,請稍後再試一次' # Errors -unknown_user: '&b【AuthMe】&6這個帳號還沒有註冊過' -denied_command: '&b【AuthMe】&c使用指令之前必須通過驗證!' -denied_chat: '&b【AuthMe】&c說話之前必須通過驗證!' -not_logged_in: '&b【AuthMe】&6您還沒有登入!' -tempban_max_logins: '&b【AuthMe】&c您已被暫時封鎖IP位置,因為您登入失敗太多次.' -# TODO: Missing tags %reg_count, %max_acc, %reg_names -max_reg: '&b【AuthMe】&6您的 IP 位置所註冊的帳號數量已經達到最大。' -no_perm: '&b【AuthMe】&6您沒有使用該指令的權限。' -error: '&b【AuthMe】&6發生錯誤,請聯繫管理員' -kick_forvip: '&b【AuthMe】&6您已經被請出。&c原因 : 有 VIP 玩家登入伺服器' +error: + denied_command: '&b【AuthMe】&c使用指令之前必須通過驗證!' + denied_chat: '&b【AuthMe】&c說話之前必須通過驗證!' + unregistered_user: '&b【AuthMe】&6這個帳號還沒有註冊過' + not_logged_in: '&b【AuthMe】&6您還沒有登入!' + no_permission: '&b【AuthMe】&6您沒有使用該指令的權限。' + unexpected_error: '' + max_registration: '&b【AuthMe】&6您的 IP 位置所註冊的帳號數量已經達到最大。' + logged_in: '&b【AuthMe】&6您已經登入了!' + kick_for_vip: '&b【AuthMe】&6您已經被請出。&c原因 : 有 VIP 玩家登入伺服器' + tempban_max_logins: '&b【AuthMe】&c您已被暫時封鎖IP位置,因為您登入失敗太多次.' # AntiBot -kick_antibot: '&b【AuthMe】&cAntiBotMod 正在啟用中,請稍後再嘗試登入吧!' -antibot_auto_enabled: '&b【AuthMe】&6AntiBotMod 已自動啟用!' -antibot_auto_disabled: '&b【AuthMe】&6AntiBotMod 將會於 &c%m &6分鐘後自動關閉' +antibot: + kick_antibot: '&b【AuthMe】&cAntiBotMod 正在啟用中,請稍後再嘗試登入吧!' + auto_enabled: '&b【AuthMe】&6AntiBotMod 已自動啟用!' + auto_disabled: '&b【AuthMe】&6AntiBotMod 將會於 &c%m &6分鐘後自動關閉' + +# Unregister +unregister: + success: '&b【AuthMe】&6您已經成功註銷。' + command_usage: '&b【AuthMe】&6用法: &c"/unregister <密碼>"' # Other messages -unregistered: '&b【AuthMe】&6您已經成功註銷。' -accounts_owned_self: '&b【AuthMe】&6您擁有 %count 個帳號:' -accounts_owned_other: '&b【AuthMe】&6玩家 %name 擁有 %count 個帳號:' -two_factor_create: '&b【AuthMe - 兩步驗證碼】&b您的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' -recovery_code_sent: '&b【AuthMe】&6忘記密碼的恢復密碼電子郵件已傳送至您的信箱中.' -recovery_code_incorrect: '&b【AuthMe】&6恢復密碼錯誤! 您剩餘 %count 次嘗試機會.' -recovery_tries_exceeded: '&b【AuthMe】&6恢復密碼過多次數錯誤. 使用 "/email recovery [email]" 取得新的恢復密碼.' -recovery_code_correct: '&b【AuthMe】&6恢復密碼正確!' -recovery_change_password: '&b【AuthMe】&6請使用 "/email setpassword <新密碼>" 變更您的密碼.' -vb_nonActiv: '&b【AuthMe】&6您的帳號還沒有經過驗證! 檢查看看您的電子信箱 (Email) 吧!' -usage_unreg: '&b【AuthMe】&6用法: &c"/unregister <密碼>"' -pwd_changed: '&b【AuthMe】&6密碼變更成功!' -logged_in: '&b【AuthMe】&6您已經登入了!' -logout: '&b【AuthMe】&6您已成功登出' -reload: '&b【AuthMe】&6已重新讀取設定檔及資料庫' -usage_changepassword: '&b【AuthMe】&6用法: &c"/changepassword <舊密碼> <新密碼>"' +misc: + account_not_activated: '&b【AuthMe】&6您的帳號還沒有經過驗證! 檢查看看您的電子信箱 (Email) 吧!' + password_changed: '&b【AuthMe】&6密碼變更成功!' + logout: '&b【AuthMe】&6您已成功登出' + reload: '&b【AuthMe】&6已重新讀取設定檔及資料庫' + usage_change_password: '&b【AuthMe】&6用法: &c"/changepassword <舊密碼> <新密碼>"' + two_factor_create: '&b【AuthMe - 兩步驗證碼】&b您的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' + accounts_owned_self: '&b【AuthMe】&6您擁有 %count 個帳號:' + accounts_owned_other: '&b【AuthMe】&6玩家 %name 擁有 %count 個帳號:' # Session messages -invalid_session: '&b【AuthMe】&6Session驗證不相符!' -valid_session: '&b【AuthMe】&6您已經成功登入!' +session: + valid_session: '&b【AuthMe】&6您已經成功登入!' + invalid_session: '&b【AuthMe】&6Session驗證不相符!' # Error messages when joining -name_len: '&b【AuthMe】&6您的暱稱 太長 / 太短 了!' -regex: '&b【AuthMe】&6暱稱裡能使用的字符為: REG_EX' -country_banned: '&b【AuthMe】&6您所在的地區無法進入此伺服器' -not_owner_error: '&b【AuthMe】&4警告!&c您並不是此帳戶持有人,請立即登出。' -kick_fullserver: '&b【AuthMe】&6伺服器已經滿了,請等等再試一次' -same_nick: '&b【AuthMe】&6有同樣帳號的玩家在線上!' -invalid_name_case: '&b【AuthMe】&4警告!&c您應該使用「%valid」而並非「%invalid」登入遊戲。' -same_ip_online: '&b【AuthMe】&6同樣IP玩家在線上!' +on_join_validation: + same_ip_online: '&b【AuthMe】&6同樣IP玩家在線上!' + same_nick_online: '&b【AuthMe】&6有同樣帳號的玩家在線上!' + name_length: '&b【AuthMe】&6您的暱稱 太長 / 太短 了!' + characters_in_name: '&b【AuthMe】&6暱稱裡能使用的字符為: %valid_chars' + kick_full_server: '&b【AuthMe】&6伺服器已經滿了,請等等再試一次' + country_banned: '&b【AuthMe】&6您所在的地區無法進入此伺服器' + not_owner_error: '&b【AuthMe】&4警告!&c您並不是此帳戶持有人,請立即登出。' + invalid_name_case: '&b【AuthMe】&4警告!&c您應該使用「%valid」而並非「%invalid」登入遊戲。' # Email -usage_email_add: '&b【AuthMe】&6用法: &c"/email add <您的Email> <重複Email>"' -usage_email_change: '&b【AuthMe】&6用法: &c"/email change <舊的Email> <新的Email>"' -usage_email_recovery: '&b【AuthMe】&6用法: &c"/email recovery <您的Email>"' -new_email_invalid: '&b【AuthMe】&6新的Email無效!' -old_email_invalid: '&b【AuthMe】&6舊的Email無效!' -email_invalid: '&b【AuthMe】&6無效的Email!' -email_added: '&b【AuthMe】&6已添加Email!' -email_confirm: '&b【AuthMe】&6請驗證您的Email!' -email_changed: '&b【AuthMe】&6Email已變更!' -email_send: '&b【AuthMe】&6已經送出重設密碼要求至您的Email , 請查收。' -email_show: '&b【AuthMe】&2目前的電子郵件: &f%email' -incomplete_email_settings: '&b【AuthMe】&4因為電子郵件設定無完整導致無法傳送,請聯絡管理員.' -email_already_used: '&b【AuthMe】&4這個電郵地址已被使用。' -email_send_failure: '&b【AuthMe】&4無法傳送電子郵件,請聯絡管理員.' -show_no_email: '&b【AuthMe】&2您目前沒有設置電子郵件.' -add_email: '&b【AuthMe】&6請使用 &c"/email add <您的Email> <再次輸入您的Email>" &6來添加 Email' -recovery_email: '&b【AuthMe】&6忘記密碼了嗎? 使用 &c"/email recovery <您的Email>"' -change_password_expired: '&b【AuthMe】&6您現在不能使用這個指令變更密碼了.' -email_cooldown_error: '&b【AuthMe】&c電子郵件已經寄出了. 您只能在 %time 後才能傳送.' +email: + add_email_request: '&b【AuthMe】&6請使用 &c"/email add <您的Email> <再次輸入您的Email>" &6來添加 Email' + usage_email_add: '&b【AuthMe】&6用法: &c"/email add <您的Email> <重複Email>"' + usage_email_change: '&b【AuthMe】&6用法: &c"/email change <舊的Email> <新的Email>"' + new_email_invalid: '&b【AuthMe】&6新的Email無效!' + old_email_invalid: '&b【AuthMe】&6舊的Email無效!' + invalid: '&b【AuthMe】&6無效的Email!' + added: '&b【AuthMe】&6已添加Email!' + request_confirmation: '&b【AuthMe】&6請驗證您的Email!' + changed: '&b【AuthMe】&6Email已變更!' + email_show: '&b【AuthMe】&2目前的電子郵件: &f%email' + no_email_for_account: '&b【AuthMe】&2您目前沒有設置電子郵件.' + already_used: '&b【AuthMe】&4這個電郵地址已被使用。' + incomplete_settings: '&b【AuthMe】&4因為電子郵件設定無完整導致無法傳送,請聯絡管理員.' + send_failure: '&b【AuthMe】&4無法傳送電子郵件,請聯絡管理員.' + change_password_expired: '&b【AuthMe】&6您現在不能使用這個指令變更密碼了.' + email_cooldown_error: '&b【AuthMe】&c電子郵件已經寄出了. 您只能在 %time 後才能傳送.' + +# Password recovery by email +recovery: + forgot_password_hint: '&b【AuthMe】&6忘記密碼了嗎? 使用 &c"/email recovery <您的Email>"' + command_usage: '&b【AuthMe】&6用法: &c"/email recovery <您的Email>"' + email_sent: '&b【AuthMe】&6已經送出重設密碼要求至您的Email , 請查收。' + code: + code_sent: '&b【AuthMe】&6忘記密碼的恢復密碼電子郵件已傳送至您的信箱中.' + incorrect: '&b【AuthMe】&6恢復密碼錯誤! 您剩餘 %count 次嘗試機會.' + tries_exceeded: '&b【AuthMe】&6恢復密碼過多次數錯誤. 使用 "/email recovery [email]" 取得新的恢復密碼.' + correct: '&b【AuthMe】&6恢復密碼正確!' + change_password: '&b【AuthMe】&6請使用 "/email setpassword <新密碼>" 變更您的密碼.' # Captcha -usage_captcha: '&b【AuthMe】&6請用 &c"/captcha " &6來輸入您的驗證碼.' -wrong_captcha: '&b【AuthMe】&6錯誤的驗證碼,請使用 "/captcha THE_CAPTCHA" 再試一次.' -valid_captcha: '&b【AuthMe】&6驗證碼無效!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha: + usage_captcha: '&b【AuthMe】&6請用 &c"/captcha %captcha_code" &6來輸入您的驗證碼.' + wrong_captcha: '&b【AuthMe】&6錯誤的驗證碼,請使用 "/captcha %captcha_code" 再試一次.' + valid_captcha: '&b【AuthMe】&6驗證碼無效!' + captcha_for_registration: '' + register_captcha_valid: '' # Verification code -verification_code_required: '&b【AuthMe】&3敏感指令,需要電子郵件驗證後才能執行,請檢查電子郵件.' -usage_verification_code: '&b【AuthMe】&c用法: /verification <驗證碼>' -incorrect_verification_code: '&b【AuthMe】&c驗證碼錯誤,請在聊天室使用 "/verification <驗證碼>" 電子郵件收到的驗證碼' -verification_code_verified: '&b【AuthMe】&2身分已驗證,您現在可以使用所有指令!' -verification_code_already_verified: '&b【AuthMe】&2您已經可以使用所有指令!' -verification_code_expired: '&b【AuthMe】&3驗證碼已過期,請使用其他敏感指令來取得新的驗證碼!' -verification_code_email_needed: '&b【AuthMe】&3若需要身分驗證,請先添加電子郵件!!' +verification: + code_required: '&b【AuthMe】&3敏感指令,需要電子郵件驗證後才能執行,請檢查電子郵件.' + command_usage: '&b【AuthMe】&c用法: /verification <驗證碼>' + incorrect_code: '&b【AuthMe】&c驗證碼錯誤,請在聊天室使用 "/verification <驗證碼>" 電子郵件收到的驗證碼' + success: '&b【AuthMe】&2身分已驗證,您現在可以使用所有指令!' + already_verified: '&b【AuthMe】&2您已經可以使用所有指令!' + code_expired: '&b【AuthMe】&3驗證碼已過期,請使用其他敏感指令來取得新的驗證碼!' + email_needed: '&b【AuthMe】&3若需要身分驗證,請先添加電子郵件!!' # Time units -second: '秒' -seconds: '秒' -minute: '分' -minutes: '分' -hour: '時' -hours: '時' -day: '天' -days: '天' +time: + second: '秒' + seconds: '秒' + minute: '分' + minutes: '分' + hour: '時' + hours: '時' + day: '天' + days: '天' diff --git a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java index 8f3b10483..9fbd27bdb 100644 --- a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java +++ b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java @@ -5,7 +5,6 @@ import fr.xephi.authme.command.help.HelpSection; import fr.xephi.authme.util.StringUtils; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -14,8 +13,8 @@ import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; -import static tools.utils.FileIoUtils.listFilesOrThrow; import static org.junit.Assert.fail; +import static tools.utils.FileIoUtils.listFilesOrThrow; /** * Tests that all YML text files can be loaded. @@ -34,7 +33,6 @@ public class YamlTextFileCheckerTest { } @Test - @Ignore // TODO #1467: Migrate all files to new keys public void testAllMessagesYmlFiles() { checkFiles( Pattern.compile("messages_\\w+\\.yml"), From f44353ed4cd8e8d696fb8c3495aff89075b0408f Mon Sep 17 00:00:00 2001 From: ljacqu Date: Thu, 1 Feb 2018 20:35:30 +0100 Subject: [PATCH 023/155] #1467 Fix messages verification tool task + remove empty messages in YML files --- .../message/updater/MessageUpdater.java | 20 +-- src/main/resources/messages/messages_bg.yml | 32 ++-- src/main/resources/messages/messages_br.yml | 24 +-- src/main/resources/messages/messages_cz.yml | 24 +-- src/main/resources/messages/messages_de.yml | 24 +-- src/main/resources/messages/messages_eo.yml | 24 +-- src/main/resources/messages/messages_es.yml | 6 +- src/main/resources/messages/messages_et.yml | 24 +-- src/main/resources/messages/messages_eu.yml | 102 ++++++------ src/main/resources/messages/messages_fi.yml | 96 ++++++------ src/main/resources/messages/messages_fr.yml | 6 +- src/main/resources/messages/messages_gl.yml | 90 +++++------ src/main/resources/messages/messages_hu.yml | 10 +- src/main/resources/messages/messages_id.yml | 92 +++++------ src/main/resources/messages/messages_it.yml | 6 +- src/main/resources/messages/messages_ko.yml | 10 +- src/main/resources/messages/messages_lt.yml | 116 +++++++------- src/main/resources/messages/messages_nl.yml | 24 +-- src/main/resources/messages/messages_pl.yml | 6 +- src/main/resources/messages/messages_pt.yml | 24 +-- src/main/resources/messages/messages_ro.yml | 24 +-- src/main/resources/messages/messages_ru.yml | 10 +- src/main/resources/messages/messages_sk.yml | 24 +-- src/main/resources/messages/messages_tr.yml | 32 ++-- src/main/resources/messages/messages_uk.yml | 58 +++---- src/main/resources/messages/messages_vn.yml | 30 ++-- src/main/resources/messages/messages_zhcn.yml | 6 +- src/main/resources/messages/messages_zhhk.yml | 24 +-- src/main/resources/messages/messages_zhmc.yml | 54 +++---- src/main/resources/messages/messages_zhtw.yml | 10 +- .../message/updater/MessageUpdaterTest.java | 38 +++++ .../tools/messages/MessageFileComments.java | 13 -- .../tools/messages/MessageFileElement.java | 20 --- .../messages/MessageFileElementMerger.java | 132 ---------------- .../messages/MessageFileElementReader.java | 77 --------- .../java/tools/messages/MessageFileEntry.java | 85 ---------- .../tools/messages/MessageFileVerifier.java | 14 +- .../tools/messages/MessagesFileWriter.java | 148 ++++++++++++++++++ .../tools/messages/VerifyMessagesTask.java | 25 ++- 39 files changed, 726 insertions(+), 858 deletions(-) delete mode 100644 src/test/java/tools/messages/MessageFileComments.java delete mode 100644 src/test/java/tools/messages/MessageFileElement.java delete mode 100644 src/test/java/tools/messages/MessageFileElementMerger.java delete mode 100644 src/test/java/tools/messages/MessageFileElementReader.java delete mode 100644 src/test/java/tools/messages/MessageFileEntry.java create mode 100644 src/test/java/tools/messages/MessagesFileWriter.java diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index a833d4c19..38ed7f07a 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -24,7 +24,10 @@ import java.util.Set; */ public class MessageUpdater { - private static final ConfigurationData CONFIGURATION_DATA = buildConfigurationData(); + /** + * Configuration data object for all message keys incl. comments associated to sections. + */ + public static final ConfigurationData CONFIGURATION_DATA = buildConfigurationData(); /** * Applies any necessary migrations to the user's messages file and saves it if it has been modified. @@ -49,14 +52,14 @@ public class MessageUpdater { private boolean migrateAndSave(File userFile, JarMessageSource jarMessageSource) { // YamlConfiguration escapes all special characters when saving, making the file hard to use, so use ConfigMe YamlFileResource userResource = new MigraterYamlFileResource(userFile); - SettingsManager settingsManager = new SettingsManager(userResource, null, CONFIGURATION_DATA); // Step 1: Migrate any old keys in the file to the new paths boolean movedOldKeys = migrateOldKeys(userResource); // Step 2: Take any missing messages from the message files shipped in the AuthMe JAR - boolean addedMissingKeys = addMissingKeys(jarMessageSource, userResource, settingsManager); + boolean addedMissingKeys = addMissingKeys(jarMessageSource, userResource); if (movedOldKeys || addedMissingKeys) { + SettingsManager settingsManager = new SettingsManager(userResource, null, CONFIGURATION_DATA); settingsManager.save(); ConsoleLogger.debug("Successfully saved {0}", userFile); return true; @@ -72,12 +75,11 @@ public class MessageUpdater { return hasChange; } - private boolean addMissingKeys(JarMessageSource jarMessageSource, YamlFileResource userResource, - SettingsManager settingsManager) { + private boolean addMissingKeys(JarMessageSource jarMessageSource, YamlFileResource userResource) { int addedKeys = 0; for (Property property : CONFIGURATION_DATA.getProperties()) { - if (!property.isPresent(userResource)) { - settingsManager.setProperty((Property) property, jarMessageSource.getMessageFromJar(property)); + if (userResource.getString(property.getPath()) == null) { + userResource.setValue(property.getPath(), jarMessageSource.getMessageFromJar(property)); ++addedKeys; } } @@ -131,11 +133,11 @@ public class MessageUpdater { /** * Extension of {@link YamlFileResource} to fine-tune the export style. */ - private static final class MigraterYamlFileResource extends YamlFileResource { + public static final class MigraterYamlFileResource extends YamlFileResource { private Yaml singleQuoteYaml; - MigraterYamlFileResource(File file) { + public MigraterYamlFileResource(File file) { super(file, new MessageMigraterPropertyReader(file), new LeafPropertiesGenerator()); } diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index 64341683c..3dcdd9359 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cКоманда: /login парола' wrong_password: '&cГрешна парола!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cМоля влезте с: /login парола' timeout_error: '&4Времето за вход изтече, беше кикнат от сървъра. Моля опитайте отново!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cПотребителското име не е регистрирано!' not_logged_in: '&cНе си влязъл!' no_permission: '&4Нямаш нужните права за това действие!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cТи си достигнал максималният брой регистрации (%reg_count/%max_acc %reg_names)!' logged_in: '&cВече си вписан!' kick_for_vip: '&3VIP потребител влезе докато сървъра беше пълен, ти беше изгонен!' @@ -91,7 +91,7 @@ email: already_used: '&4Имейл адреса вече се използва, опитайте с друг.' incomplete_settings: 'Грешка: Не всички настройки са написани за изпращане на имейл адрес. Моля свържете се с администратора!' send_failure: 'Съобщението не беше изпратено. Моля свържете се с администратора.' - change_password_expired: '' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' email_cooldown_error: '&cВече е бил изпратен имейл адрес. Трябва а изчакаш %time преди да пратиш нов.' # Password recovery by email @@ -102,27 +102,27 @@ recovery: code: code_sent: 'Възстановяващият код беше изпратен на твоят email адрес.' incorrect: 'Възстановяващият код е неправилен! Използвайте: /email recovery имейл, за да генерирате нов' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&3Моля въведе цифрите/буквите от капчата: /captcha %captcha_code' wrong_captcha: '&cКода е грешен, използвайте: "/captcha %captcha_code" в чата!' valid_captcha: '&2Кода е валиден!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'час' hours: 'часа' day: 'ден' - days: 'дена' + days: 'дена' \ No newline at end of file diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 3318f8a35..6758dd1e4 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -24,7 +24,7 @@ password: login: command_usage: '&cUse: /login ' wrong_password: '&cSenha incorreta!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cPor favor, faça o login com o comando "/login "' timeout_error: '&4Tempo limite de sessão excedido, você foi expulso do servidor, por favor, tente novamente!' @@ -35,7 +35,7 @@ error: unregistered_user: '&cEste usuário não está registrado!' not_logged_in: '&cVocê não está logado!' no_permission: '&4Você não tem permissão para executar esta ação!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cVocê excedeu o número máximo de inscrições (%reg_count/%max_acc %reg_names) do seu IP!' logged_in: '&cVocê já está logado!' kick_for_vip: '&3Um jogador VIP juntou-se ao servidor enquanto ele estava cheio!' @@ -115,18 +115,18 @@ captcha: usage_captcha: '&3Para iniciar sessão você tem que resolver um código captcha, utilize o comando "/captcha %captcha_code"' wrong_captcha: '&cCaptcha errado, por favor, escreva "/captcha %captcha_code" no chat!' valid_captcha: '&2Código Captcha resolvido corretamente!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -137,4 +137,4 @@ time: hour: 'hora' hours: 'horas' day: 'dia' - days: 'dias' + days: 'dias' \ No newline at end of file diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index 6d98a3cda..f5916bf29 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cPoužij: "/login TvojeHeslo".' wrong_password: '&cŠpatné heslo.' - success: '' + # TODO success: '&2Successful login!' login_request: '&cProsím přihlaš se pomocí "/login TvojeHeslo".' timeout_error: '&cČas pro přihlášení vypršel!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cUživatelské jméno není zaregistrováno.' not_logged_in: '&cNejsi přihlášený!' no_permission: '&cNa tento příkaz nemáš dostatečné pravomoce.' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cPřekročil(a) jsi limit pro počet účtů (%reg_count/%max_acc %reg_names) z jedné IP adresy.' logged_in: '&cJsi již přihlášen!' kick_for_vip: '&cOmlouváme se, ale VIP hráč se připojil na plný server!' @@ -111,18 +111,18 @@ captcha: usage_captcha: '&cPoužij: /captcha %captcha_code' wrong_captcha: '&cŠpatné opsana Captcha, pouzij prosim: /captcha %captcha_code' valid_captcha: '&cZadaná captcha je v pořádku!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'hodiny' hours: 'hodin' day: 'dny' - days: 'dnu' + days: 'dnu' \ No newline at end of file diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 38d18f2f2..0a524f5c6 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cBenutze: /login ' wrong_password: '&cFalsches Passwort' - success: '' + # TODO success: '&2Successful login!' login_request: '&cBitte logge dich ein mit "/login "' timeout_error: '&4Zeitüberschreitung beim Login' @@ -31,7 +31,7 @@ error: unregistered_user: '&cBenutzername nicht registriert!' not_logged_in: '&cNicht eingeloggt!' no_permission: '&4Du hast keine Rechte, um diese Aktion auszuführen!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cDu hast die maximale Anzahl an Accounts erreicht (%reg_count/%max_acc %reg_names).' logged_in: '&cBereits eingeloggt!' kick_for_vip: '&3Ein VIP-Spieler hat den vollen Server betreten!' @@ -111,18 +111,18 @@ captcha: usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha %captcha_code' wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha %captcha_code' valid_captcha: '&2Das Captcha ist korrekt!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'Stunde' hours: 'Stunden' day: 'Tag' - days: 'Tage' + days: 'Tage' \ No newline at end of file diff --git a/src/main/resources/messages/messages_eo.yml b/src/main/resources/messages/messages_eo.yml index 0d71447d3..41a5739ac 100644 --- a/src/main/resources/messages/messages_eo.yml +++ b/src/main/resources/messages/messages_eo.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUzado: /login ' wrong_password: '&cErara pasvorto!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cBonvolu ensaluti per la komando: /login ' timeout_error: '&4Salutnomo tempolimo superis, vi estis piedbatita el la servilo, bonvolu provi denove!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cTiu uzanto ne estas registrita!' not_logged_in: '&cVi ne estas ensalutita!' no_permission: '&4Vi ne havas la permeson por fari ĉi tiun funkcion!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: 'Vi superis la maksimuman nombron de enregistroj (%reg_count/%max_acc %reg_names) pro via ligo!' logged_in: '&cVi jam estas ensalutinta!' kick_for_vip: '&3VIP ludanto aliĝis al la servilo kiam ĝi pleniĝis!' @@ -111,18 +111,18 @@ captcha: usage_captcha: '&3Ensaluti vi devas solvi captcha kodo, bonvolu uzi la komando: /captcha %captcha_code' wrong_captcha: '&cMalĝusta captcha, bonvolu tajpi "/captcha %captcha_code" en la babilejo!' valid_captcha: '&2Captcha kodo solvita ĝuste!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'horo' hours: 'horoj' day: 'tago' - days: 'tagoj' + days: 'tagoj' \ No newline at end of file diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index 98c417f1f..7326ea977 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -22,7 +22,7 @@ password: login: command_usage: '&cUso: /login contraseña' wrong_password: '&cContraseña incorrecta' - success: '' + # TODO success: '&2Successful login!' login_request: '&cInicia sesión con "/login contraseña"' timeout_error: '&fTiempo de espera para inicio de sesión excedido' @@ -33,7 +33,7 @@ error: unregistered_user: '&cUsuario no registrado' not_logged_in: '&c¡No has iniciado sesión!' no_permission: '&cNo tienes permiso' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&fHas excedido la cantidad máxima de registros para tu cuenta' logged_in: '&c¡Ya has iniciado sesión!' kick_for_vip: '&c¡Un jugador VIP ha ingresado al servidor lleno!' @@ -135,4 +135,4 @@ time: hour: 'hora' hours: 'horas' day: 'día' - days: 'días' + days: 'días' \ No newline at end of file diff --git a/src/main/resources/messages/messages_et.yml b/src/main/resources/messages/messages_et.yml index f0eb19163..2de633a7f 100644 --- a/src/main/resources/messages/messages_et.yml +++ b/src/main/resources/messages/messages_et.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cKasutus: /login ' wrong_password: '&cVale salasõna!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cPalun logi sisse kasutades käsklust: /login ' timeout_error: '&4Sisselogimise taimer täis, palun logi sisse ja proovi uuesti!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cSee kasutaja ei ole registreeritud.' not_logged_in: '&cSa ei ole sisse loginud!' no_permission: '&4Sul ei ole selle käskluse kasutamiseks õigust.' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cSu IP peal (%reg_count/%max_acc %reg_names) on liiga palju kontosid!' logged_in: '&cJuba sisselogitud!' kick_for_vip: '&3VIP kasutaja liitus serveriga, kui see oli täis. Sind visati välja, et talle ruumi teha.' @@ -111,18 +111,18 @@ captcha: usage_captcha: '&3Sisselogimiseks pead lahendama captcha. Tee seda kasutades käsklust: /captcha %captcha_code' wrong_captcha: '&cVale captcha, kirjuta "/captcha %captcha_code" chatti!' valid_captcha: '&2Captcha lahendatud!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'tund' hours: 'tundi' day: 'päev' - days: 'päeva' + days: 'päeva' \ No newline at end of file diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index e729aafcd..9d4ce4b7a 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -6,42 +6,42 @@ registration: command_usage: '&cErabili: /register pasahitza pasahitza' reg_only: '&fErregistratuako erabiltzaileak bakarrik! Mesedez bisitatu http://example.com erregistratzeko' success: '&cOndo erregistratu zara!' - kicked_admin_registered: '' + # TODO kicked_admin_registered: 'An admin just registered you; please log in again' # Password errors on registration password: match_error: '&fPasahitzak ez datoz bat' - name_in_password: '' - unsafe_password: '' - forbidden_characters: '' + # TODO name_in_password: '&cYou can''t use your name as password, please choose another one...' + # TODO unsafe_password: '&cThe chosen password isn''t safe, please choose another one...' + # TODO forbidden_characters: '&4Your password contains illegal characters. Allowed chars: %valid_chars' wrong_length: '&fZure pasahitza motzegia edo luzeegia da' # Login login: command_usage: '&cErabili: /login pasahitza' wrong_password: '&cPasahitz okerra' - success: '' + # TODO success: '&2Successful login!' login_request: '&cMesedez erabili "/login pasahitza" saioa hasteko' timeout_error: '&fDenbora gehiegi egon zara saioa hasi gabe' # Errors error: - denied_command: '' - denied_chat: '' + # TODO denied_command: '&cIn order to use this command you must be authenticated!' + # TODO denied_chat: '&cIn order to chat you must be authenticated!' unregistered_user: '&cErabiltzailea ez dago erregistratuta' not_logged_in: '&cSaioa hasi gabe!' no_permission: '&cBaimenik ez' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&fKontuko 2 erabiltzaile bakarrik izan ditzakezu' logged_in: '&cDagoeneko saioa hasita!' kick_for_vip: '&cVIP erabiltzaile bat sartu da zerbitzaria beteta zegoenean!' - tempban_max_logins: '' + # TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' # AntiBot antibot: - kick_antibot: '' - auto_enabled: '' - auto_disabled: '' + # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' + # TODO auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' + # TODO auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' # Unregister unregister: @@ -55,25 +55,25 @@ misc: logout: '&cAtera zara' reload: '&fConfiguration and database has been reloaded' usage_change_password: '&fErabili: /changepassword pasahitzZaharra pasahitzBerria' - two_factor_create: '' - accounts_owned_self: '' - accounts_owned_other: '' + # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' + # TODO accounts_owned_self: 'You own %count accounts:' + # TODO accounts_owned_other: 'The player %name has %count accounts:' # Session messages session: valid_session: '&cSession login' - invalid_session: '' + # TODO invalid_session: '&cYour IP has been changed and your session data has expired!' # Error messages when joining on_join_validation: - same_ip_online: '' + # TODO same_ip_online: 'A player with the same IP is already in game!' same_nick_online: '&fZure izen berdina duen erabiltzaile bat zerbitzarian jolasten dago' name_length: '&cZure erabiltzaile izena motzegia edo luzeegia da' characters_in_name: '&cZure erabiltzaileak karaktere debekatuak ditu. Karaktere onartuak: %valid_chars' kick_full_server: '&cZerbitzaria beteta dago, Barkatu!' country_banned: '[AuthMe] Zure herrialdea blokeatuta dago zerbitzari honetan' - not_owner_error: '' - invalid_name_case: '' + # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' + # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # Email email: @@ -86,13 +86,13 @@ email: added: '[AuthMe] Emaila gehitu duzu !' request_confirmation: '[AuthMe] Konfirmatu zure emaila !' changed: '[AuthMe] Emaila aldatua!' - email_show: '' - no_email_for_account: '' - already_used: '' - incomplete_settings: '' - send_failure: '' - change_password_expired: '' - email_cooldown_error: '' + # TODO email_show: '&2Your current email address is: &f%email' + # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' + # TODO already_used: '&4The email address is already being used' + # TODO incomplete_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' + # TODO send_failure: 'The email could not be sent. Please contact an administrator.' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' + # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' # Password recovery by email recovery: @@ -100,37 +100,37 @@ recovery: command_usage: '&fErabili: /email recovery ' email_sent: '[AuthMe] Berreskuratze emaila bidalita !' code: - code_sent: '' - incorrect: '' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO code_sent: 'A recovery code to reset your password has been sent to your email.' + # TODO incorrect: 'The recovery code is not correct! You have %count tries remaining.' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: - usage_captcha: '' - wrong_captcha: '' - valid_captcha: '' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO usage_captcha: '&3To log in you have to solve a captcha code, please use the command: /captcha %captcha_code' + # TODO wrong_captcha: '&cWrong captcha, please type "/captcha %captcha_code" into the chat!' + # TODO valid_captcha: '&2Captcha code solved correctly!' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: - second: '' - seconds: '' - minute: '' - minutes: '' - hour: '' - hours: '' - day: '' - days: '' + # TODO second: 'second' + # TODO seconds: 'seconds' + # TODO minute: 'minute' + # TODO minutes: 'minutes' + # TODO hour: 'hour' + # TODO hours: 'hours' + # TODO day: 'day' + # TODO days: 'days' \ No newline at end of file diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index f2f958f7c..01b9906c8 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -6,42 +6,42 @@ registration: command_usage: '&cKäyttötapa: /register ' reg_only: '&fMene sivustolle: http://example.com rekisteröityäksesi!' success: '&cRekisteröidyit onnistuneesti!' - kicked_admin_registered: '' + # TODO kicked_admin_registered: 'An admin just registered you; please log in again' # Password errors on registration password: match_error: '&fSalasanat ei täsmää' - name_in_password: '' - unsafe_password: '' - forbidden_characters: '' + # TODO name_in_password: '&cYou can''t use your name as password, please choose another one...' + # TODO unsafe_password: '&cThe chosen password isn''t safe, please choose another one...' + # TODO forbidden_characters: '&4Your password contains illegal characters. Allowed chars: %valid_chars' wrong_length: '&fSalasanasi on liian pitkä tai lyhyt.' # Login login: command_usage: '&cKäyttötapa: /login salasana' wrong_password: '&cVäärä salasana' - success: '' + # TODO success: '&2Successful login!' login_request: '&cKirjaudu palvelimmelle komennolla "/login salasana"' timeout_error: '&fKirjautumisaika meni umpeen.' # Errors error: - denied_command: '' - denied_chat: '' + # TODO denied_command: '&cIn order to use this command you must be authenticated!' + # TODO denied_chat: '&cIn order to chat you must be authenticated!' unregistered_user: '&cSalasanat eivät täsmää' not_logged_in: '&cEt ole kirjautunut sisään!' no_permission: '&cEi oikeuksia' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&fSinulla ei ole oikeuksia tehdä enempää pelaajatilejä!' logged_in: '&cOlet jo kirjautunut!' kick_for_vip: '&cVIP pelaaja liittyi täyteen palvelimeen!' - tempban_max_logins: '' + # TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' # AntiBot antibot: - kick_antibot: '' - auto_enabled: '' - auto_disabled: '' + # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' + # TODO auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' + # TODO auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' # Unregister unregister: @@ -55,9 +55,9 @@ misc: logout: '&cKirjauduit ulos palvelimelta.' reload: '&fAsetukset uudelleenladattu' usage_change_password: '&fKäyttötapa: /changepassword vanhaSalasana uusiSalasana' - two_factor_create: '' - accounts_owned_self: '' - accounts_owned_other: '' + # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' + # TODO accounts_owned_self: 'You own %count accounts:' + # TODO accounts_owned_other: 'The player %name has %count accounts:' # Session messages session: @@ -66,14 +66,14 @@ session: # Error messages when joining on_join_validation: - same_ip_online: '' + # TODO same_ip_online: 'A player with the same IP is already in game!' same_nick_online: '&COlet jo palvelimella! &COdota käyttäjän aikakatkaisua tai ota yhteyttä palveluntarjoojaan.' name_length: '&cPelaajanimesi on liian lyhyt tai pitkä' characters_in_name: '&cPelaajanimesi sisältää luvattomia merkkejä. Hyväksytyt merkit: %valid_chars' kick_full_server: '&cPalvelin on täynnä, Yritä pian uudelleen!' - country_banned: '' - not_owner_error: '' - invalid_name_case: '' + # TODO country_banned: '&4Your country is banned from this server!' + # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' + # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # Email email: @@ -86,13 +86,13 @@ email: added: '[AuthMe] Sähköposti lisätty!' request_confirmation: '[AuthMe] Vahvistuta sähköposti!' changed: '[AuthMe] Sähköposti vaihdettu!' - email_show: '' - no_email_for_account: '' - already_used: '' - incomplete_settings: '' - send_failure: '' - change_password_expired: '' - email_cooldown_error: '' + # TODO email_show: '&2Your current email address is: &f%email' + # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' + # TODO already_used: '&4The email address is already being used' + # TODO incomplete_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' + # TODO send_failure: 'The email could not be sent. Please contact an administrator.' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' + # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' # Password recovery by email recovery: @@ -100,37 +100,37 @@ recovery: command_usage: '&fKäyttötapa: /email recovery ' email_sent: '[AuthMe] Palautus sähköposti lähetetty!' code: - code_sent: '' - incorrect: '' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO code_sent: 'A recovery code to reset your password has been sent to your email.' + # TODO incorrect: 'The recovery code is not correct! You have %count tries remaining.' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&cKäyttötapa: /captcha %captcha_code' wrong_captcha: '&cVäärä varmistus, käytä : /captcha %captcha_code' valid_captcha: '&cSinun varmistus onnistui.!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: - second: '' - seconds: '' - minute: '' - minutes: '' - hour: '' - hours: '' - day: '' - days: '' + # TODO second: 'second' + # TODO seconds: 'seconds' + # TODO minute: 'minute' + # TODO minutes: 'minutes' + # TODO hour: 'hour' + # TODO hours: 'hours' + # TODO day: 'day' + # TODO days: 'days' \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index fdfe622b7..c34911249 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -25,7 +25,7 @@ password: login: command_usage: '&cUsage: /login ' wrong_password: '&cMauvais mot de passe !' - success: '' + # TODO success: '&2Successful login!' login_request: '&cPour vous connecter, utilisez "/login "' timeout_error: 'Vous avez été expulsé car vous êtes trop lent pour vous enregistrer/connecter !' @@ -36,7 +36,7 @@ error: unregistered_user: '&cUtilisateur non-inscrit.' not_logged_in: '&cUtilisateur non connecté !' no_permission: '&cVous n''êtes pas autorisé à utiliser cette commande.' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: 'Vous avez atteint la limite d''inscription !%nl%&cVous avez %reg_count sur %max_acc : %reg_names' logged_in: '&aVous êtes déjà connecté.' kick_for_vip: 'Un joueur VIP a rejoint le serveur à votre place (serveur plein).' @@ -138,4 +138,4 @@ time: hour: 'heure' hours: 'heures' day: 'jour' - days: 'jours' + days: 'jours' \ No newline at end of file diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index 1ae95174b..c8ebe4ab3 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -6,40 +6,40 @@ registration: command_usage: '&cUso: /register contrasinal confirmarContrasinal' reg_only: '&fSó xogadores rexistrados! Por favor, visita http://example.com para rexistrarte' success: '&cRexistrado con éxito!' - kicked_admin_registered: '' + # TODO kicked_admin_registered: 'An admin just registered you; please log in again' # Password errors on registration password: match_error: '&fO contrasinal non coincide' - name_in_password: '' - unsafe_password: '' - forbidden_characters: '' + # TODO name_in_password: '&cYou can''t use your name as password, please choose another one...' + # TODO unsafe_password: '&cThe chosen password isn''t safe, please choose another one...' + # TODO forbidden_characters: '&4Your password contains illegal characters. Allowed chars: %valid_chars' wrong_length: '&fO teu contrasinal non alcanza a lonxitude mínima ou excede a lonxitude máxima' # Login login: command_usage: '&cUso: /login ' wrong_password: '&cContrasinal equivocado' - success: '' + # TODO success: '&2Successful login!' login_request: '&cPor favor, identifícate con "/login "' timeout_error: '&fRematou o tempo da autentificación' # Errors error: - denied_command: '' - denied_chat: '' + # TODO denied_command: '&cIn order to use this command you must be authenticated!' + # TODO denied_chat: '&cIn order to chat you must be authenticated!' unregistered_user: '&cEse nome de usuario non está rexistrado' not_logged_in: '&cNon te identificaches!' no_permission: '&cNon tes o permiso' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&fExcediches o máximo de rexistros para a túa Conta' logged_in: '&cXa estás identificado!' kick_for_vip: '&cUn xogador VIP uniuse ao servidor cheo!' - tempban_max_logins: '' + # TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' # AntiBot antibot: - kick_antibot: '' + # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' auto_enabled: '[AuthMe] AntiBotMod conectouse automáticamente debido a conexións masivas!' auto_disabled: '[AuthMe] AntiBotMod desactivouse automáticamente despois de %m minutos, esperemos que a invasión se detivera' @@ -55,9 +55,9 @@ misc: logout: '&cSesión pechada con éxito' reload: '&fRecargáronse a configuración e a base de datos' usage_change_password: '&fUso: /changepassword ' - two_factor_create: '' - accounts_owned_self: '' - accounts_owned_other: '' + # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' + # TODO accounts_owned_self: 'You own %count accounts:' + # TODO accounts_owned_other: 'The player %name has %count accounts:' # Session messages session: @@ -66,14 +66,14 @@ session: # Error messages when joining on_join_validation: - same_ip_online: '' + # TODO same_ip_online: 'A player with the same IP is already in game!' same_nick_online: '&fXa está xogando alguén co mesmo nome' name_length: '&cO teu nome é demasiado curto ou demasiado longo' characters_in_name: '&cO teu nome contén caracteres ilegais. Caracteres permitidos: %valid_chars' kick_full_server: '&cO servidor está actualmente cheo, sentímolo!' country_banned: 'O teu país está bloqueado neste servidor' - not_owner_error: '' - invalid_name_case: '' + # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' + # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # Email email: @@ -86,13 +86,13 @@ email: added: '[AuthMe] Correo engadido!' request_confirmation: '[AuthMe] Confirma o teu correo!' changed: '[AuthMe] Cambiouse o correo!' - email_show: '' - no_email_for_account: '' - already_used: '' - incomplete_settings: '' - send_failure: '' - change_password_expired: '' - email_cooldown_error: '' + # TODO email_show: '&2Your current email address is: &f%email' + # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' + # TODO already_used: '&4The email address is already being used' + # TODO incomplete_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' + # TODO send_failure: 'The email could not be sent. Please contact an administrator.' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' + # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' # Password recovery by email recovery: @@ -100,37 +100,37 @@ recovery: command_usage: '&fUso: /email recovery ' email_sent: '[AuthMe] Enviouse o correo de confirmación!' code: - code_sent: '' - incorrect: '' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO code_sent: 'A recovery code to reset your password has been sent to your email.' + # TODO incorrect: 'The recovery code is not correct! You have %count tries remaining.' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&cNecesitas escribir un captcha, por favor escribe: /captcha %captcha_code' wrong_captcha: '&cCaptcha equivocado, por favor usa: /captcha %captcha_code' valid_captcha: '&cO teu captcha é válido !' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: - second: '' - seconds: '' - minute: '' - minutes: '' - hour: '' - hours: '' - day: '' - days: '' + # TODO second: 'second' + # TODO seconds: 'seconds' + # TODO minute: 'minute' + # TODO minutes: 'minutes' + # TODO hour: 'hour' + # TODO hours: 'hours' + # TODO day: 'day' + # TODO days: 'days' \ No newline at end of file diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index a5a953415..773a9d7ed 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cBejelentkezés: "&7/login &c".' wrong_password: '&4A jelszó helytelen!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cKérlek, jelentkezz be: "&7/login &c"!' timeout_error: 'Bejelentkezési időtúllépés!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cEz a felhasználó nincs regisztrálva!' not_logged_in: '&cNem vagy bejelentkezve!' no_permission: '&cNincs jogosultságod a használatára!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cElérted a maximálisan beregisztrálható karakterek számát. (%reg_count/%max_acc %reg_names)' logged_in: '&cMár be vagy jelentkezve!' kick_for_vip: '&3VIP játékos csatlakozott a szerverhez!' @@ -111,8 +111,8 @@ captcha: usage_captcha: '&3A bejelentkezéshez CAPTCHA szükséges, kérlek, használd a következő parancsot: "&7/captcha %captcha_code&3".' wrong_captcha: '&cHibás CAPTCHA, kérlek, írd be a következő parancsot: "&7/captcha %captcha_code&c"!' valid_captcha: '&2A CAPTCHA sikeresen feloldva!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: @@ -133,4 +133,4 @@ time: hour: 'óra' hours: 'óra' day: 'nap' - days: 'nap' + days: 'nap' \ No newline at end of file diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 4d6c641e2..8afc1c665 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -3,50 +3,50 @@ registration: disabled: '&cRegister dalam game tidak diaktifkan!' name_taken: '&cKamu telah mendaftarkan username ini!' register_request: '&3Silahkan mendaftar ke server menggunakan command "/register "' - command_usage: '' + # TODO command_usage: '&cUsage: /register ' reg_only: '&4Hanya pengguna terdaftar yg bisa bergabung! Silahkan kunjungi http://example.com untuk mendaftar!' success: '&2Register berhasil!' - kicked_admin_registered: '' + # TODO kicked_admin_registered: 'An admin just registered you; please log in again' # Password errors on registration password: match_error: '&cPassword tidak cocok, silahkan periksa dan ulangi kembali!' name_in_password: '&cKamu tidak bisa menggunakan namamu sebagai password, silahkan coba yg lain...' unsafe_password: '&cPassword yg kamu pilih tidak aman, silahkan coba yg lain...' - forbidden_characters: '' + # TODO forbidden_characters: '&4Your password contains illegal characters. Allowed chars: %valid_chars' wrong_length: '&cPassword kamu terlalu panjang/pendek! Silahkan pilih yg lain!' # Login login: command_usage: '&cUsage: /login ' wrong_password: '&cPassword salah!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cSilahkan login menggunakan command "/login "' timeout_error: '&4Jangka waktu login telah habis, kamu di keluarkan dari server. Silahkan coba lagi!' # Errors error: - denied_command: '' - denied_chat: '' + # TODO denied_command: '&cIn order to use this command you must be authenticated!' + # TODO denied_chat: '&cIn order to chat you must be authenticated!' unregistered_user: '&cUser ini belum terdaftar!' not_logged_in: '&cKamu belum login!' no_permission: '&4Kamu tidak mempunyai izin melakukan ini!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&Kamu telah mencapai batas maksimum pendaftaran di server ini!' logged_in: '&cKamu telah login!' kick_for_vip: '&3Player VIP mencoba masuk pada saat server sedang penuh!' - tempban_max_logins: '' + # TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' # AntiBot antibot: - kick_antibot: '' + # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' auto_enabled: '&4[AntiBotService] AntiBot diaktifkan dikarenakan banyak koneksi yg diterima!' auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!' # Unregister unregister: success: '&cUnregister berhasil!' - command_usage: '' + # TODO command_usage: '&cUsage: /unregister ' # Other messages misc: @@ -55,9 +55,9 @@ misc: logout: '&2Berhasil logout!' reload: '&2Konfigurasi dan database telah dimuat ulang!' usage_change_password: '&cUsage: /changepassword ' - two_factor_create: '' - accounts_owned_self: '' - accounts_owned_other: '' + # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' + # TODO accounts_owned_self: 'You own %count accounts:' + # TODO accounts_owned_other: 'The player %name has %count accounts:' # Session messages session: @@ -66,14 +66,14 @@ session: # Error messages when joining on_join_validation: - same_ip_online: '' + # TODO same_ip_online: 'A player with the same IP is already in game!' same_nick_online: '&4Username yg sama telah bermain di server ini!' name_length: '&4Username kamu terlalu panjang atau terlalu pendek!' characters_in_name: '&4Username kamu mengandung karakter illegal. Karakter yg diijinkan: %valid_chars' kick_full_server: '&4Server sedang penuh, silahkan coba lagi nanti!' - country_banned: '' - not_owner_error: '' - invalid_name_case: '' + # TODO country_banned: '&4Your country is banned from this server!' + # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' + # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # Email email: @@ -86,13 +86,13 @@ email: added: '&2Berhasil menambahkan alamat email ke akunmu!' request_confirmation: '&cSilahkan konfirmasi alamat email kamu!' changed: '&2Alamat email telah diubah dengan benar!' - email_show: '' - no_email_for_account: '' - already_used: '' - incomplete_settings: '' - send_failure: '' - change_password_expired: '' - email_cooldown_error: '' + # TODO email_show: '&2Your current email address is: &f%email' + # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' + # TODO already_used: '&4The email address is already being used' + # TODO incomplete_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' + # TODO send_failure: 'The email could not be sent. Please contact an administrator.' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' + # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' # Password recovery by email recovery: @@ -100,37 +100,37 @@ recovery: command_usage: '&cUsage: /email recovery ' email_sent: '&2Email pemulihan akun telah dikirim! Silahkan periksa kotak masuk emailmu!' code: - code_sent: '' - incorrect: '' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO code_sent: 'A recovery code to reset your password has been sent to your email.' + # TODO incorrect: 'The recovery code is not correct! You have %count tries remaining.' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&3Kamu harus menyelesaikan kode captcha untuk login, silahkan gunakan command "/captcha %captcha_code"' wrong_captcha: '&cCaptcha salah, gunakan command "/captcha %captcha_code" pada chat!' valid_captcha: '&2Kode captcha terselesaikan!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: - second: '' - seconds: '' - minute: '' - minutes: '' - hour: '' - hours: '' - day: '' - days: '' + # TODO second: 'second' + # TODO seconds: 'seconds' + # TODO minute: 'minute' + # TODO minutes: 'minutes' + # TODO hour: 'hour' + # TODO hours: 'hours' + # TODO day: 'day' + # TODO days: 'days' \ No newline at end of file diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index fd59e5fa3..3aa9fdb6a 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -22,7 +22,7 @@ password: login: command_usage: '&cUtilizzo: /login ' wrong_password: '&cPassword non corretta!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cPer favore, esegui l''autenticazione con il comando: /login ' timeout_error: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!' @@ -33,7 +33,7 @@ error: unregistered_user: '&cL''utente non ha ancora eseguito la registrazione.' not_logged_in: '&cNon hai ancora eseguito l''autenticazione!' no_permission: '&4Non hai il permesso di eseguire questa operazione.' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cHai raggiunto il numero massimo di registrazioni (%reg_count/%max_acc %reg_names) per questo indirizzo IP!' logged_in: '&cHai già eseguito l''autenticazione, non è necessario eseguirla nuovamente!' kick_for_vip: '&3Un utente VIP è entrato mentre il server era pieno e ha preso il tuo posto!' @@ -135,4 +135,4 @@ time: hour: 'ora' hours: 'ore' day: 'giorno' - days: 'giorni' + days: 'giorni' \ No newline at end of file diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index cdc4f02d6..488f336f3 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -23,7 +23,7 @@ password: login: command_usage: '&c사용법: /login <비밀번호>' wrong_password: '&c비밀번호가 잘못되었습니다!' - success: '' + # TODO success: '&2Successful login!' login_request: '&c다음 명령어로 로그인 해주세요: /login <비밀번호>' timeout_error: '&4로그인 시간이 초과 되어 서버에서 추방당했습니다. 다시 시도하세요!' @@ -34,7 +34,7 @@ error: unregistered_user: '&c이 유저는 등록되지 않았습니다!' not_logged_in: '&c로그인이 되어있지 않습니다!' no_permission: '&4이 작업을 수행할 수 있는 권한이 없습니다!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&c당신은 가입할 수 있는 계정 한도를 초과했습니다 (%reg_count/%max_acc %reg_names)!' logged_in: '&c이미 로그인되어 있습니다!' kick_for_vip: '&3서버가 꽉 차있을땐 VIP 플레이어만 접속이 가능합니다!' @@ -114,8 +114,8 @@ captcha: usage_captcha: '&3로그인 하려면 CAPTCHA 코드를 입력해야 합니다. 이 명령어를 사용하세요: /captcha %captcha_code' wrong_captcha: '&c잘못된 CAPTCHA 코드 입니다. "/captcha %captcha_code" 형태로 입력해주세요!' valid_captcha: '&2CAPTCHA 코드가 확인되었습니다!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: @@ -136,4 +136,4 @@ time: hour: '시간' hours: '시간' day: '일' - days: '일' + days: '일' \ No newline at end of file diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index 67fba54eb..58d59fa9d 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -6,42 +6,42 @@ registration: command_usage: '&eNaudojimas: /register slaptazodis pakartotiSlaptazodi' reg_only: '&cTik prisiregistravusiem zaidejams: apsilankykite: http://example.com tam kad uzsiregistruoti.' success: '&aSekmingai prisiregistravote.' - kicked_admin_registered: '' + # TODO kicked_admin_registered: 'An admin just registered you; please log in again' # Password errors on registration password: match_error: '&cSlaptazodziai nesutampa' - name_in_password: '' - unsafe_password: '' - forbidden_characters: '' + # TODO name_in_password: '&cYou can''t use your name as password, please choose another one...' + # TODO unsafe_password: '&cThe chosen password isn''t safe, please choose another one...' + # TODO forbidden_characters: '&4Your password contains illegal characters. Allowed chars: %valid_chars' wrong_length: '&cJusu slaptazodis buvo per ilgas arba per trumpas.' # Login login: command_usage: '&eKomandos panaudojimas: /login slaptazodis' wrong_password: '&cNeteisingas slaptazosdis' - success: '' + # TODO success: '&2Successful login!' login_request: '&ePrasome prisijungti: /login slaptazodis' timeout_error: '&cNespejote prisijungti' # Errors error: - denied_command: '' - denied_chat: '' + # TODO denied_command: '&cIn order to use this command you must be authenticated!' + # TODO denied_chat: '&cIn order to chat you must be authenticated!' unregistered_user: '&cVartotojas neprisiregistraves' not_logged_in: '&cTu neprisijunges!' no_permission: '&cNera leidimo' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cJus pasiekete maksimalu registraciju skaiciu.' logged_in: '&cTu aju prisijunges!' kick_for_vip: '&cA VIP prisijunge i pilna serveri!' - tempban_max_logins: '' + # TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' # AntiBot antibot: - kick_antibot: '' - auto_enabled: '' - auto_disabled: '' + # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' + # TODO auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' + # TODO auto_disabled: '&2[AntiBotService] AntiBot disabled after %m minutes!' # Unregister unregister: @@ -55,9 +55,9 @@ misc: logout: '&aSekmingai atsijungete' reload: '&aNustatymai ir duomenu baze buvo perkrauta.' usage_change_password: '&ePanaudojimas: /changepassword senasSlaptazodis naujasSlaptazodis' - two_factor_create: '' - accounts_owned_self: '' - accounts_owned_other: '' + # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' + # TODO accounts_owned_self: 'You own %count accounts:' + # TODO accounts_owned_other: 'The player %name has %count accounts:' # Session messages session: @@ -66,71 +66,71 @@ session: # Error messages when joining on_join_validation: - same_ip_online: '' + # TODO same_ip_online: 'A player with the same IP is already in game!' same_nick_online: '&cKazkas situo vardu jau zaidzia.' name_length: '&cJusu varsdas yra per ilgas arba per trumpas.' characters_in_name: '&cJusu varde yra neledziamu simboliu, leidziami: %valid_chars' kick_full_server: '&cServeris yra pilnas, Atsiprasome.' - country_banned: '' - not_owner_error: '' - invalid_name_case: '' + # TODO country_banned: '&4Your country is banned from this server!' + # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' + # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # Email email: add_email_request: '&ePrasau pridekite savo el.pasta : /email add Email confirmEmail' - usage_email_add: '' - usage_email_change: '' - new_email_invalid: '' - old_email_invalid: '' - invalid: '' - added: '' - request_confirmation: '' - changed: '' - email_show: '' - no_email_for_account: '' - already_used: '' - incomplete_settings: '' - send_failure: '' - change_password_expired: '' - email_cooldown_error: '' + # TODO usage_email_add: '&cUsage: /email add ' + # TODO usage_email_change: '&cUsage: /email change ' + # TODO new_email_invalid: '&cInvalid new email, try again!' + # TODO old_email_invalid: '&cInvalid old email, try again!' + # TODO invalid: '&cInvalid email address, try again!' + # TODO added: '&2Email address successfully added to your account!' + # TODO request_confirmation: '&cPlease confirm your email address!' + # TODO changed: '&2Email address changed correctly!' + # TODO email_show: '&2Your current email address is: &f%email' + # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' + # TODO already_used: '&4The email address is already being used' + # TODO incomplete_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' + # TODO send_failure: 'The email could not be sent. Please contact an administrator.' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' + # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' # Password recovery by email recovery: forgot_password_hint: '&cPamirsote slaptazodi? Rasykite: /email recovery el.pastas' - command_usage: '' - email_sent: '' + # TODO command_usage: '&cUsage: /email recovery ' + # TODO email_sent: '&2Recovery email sent successfully! Please check your email inbox!' code: - code_sent: '' - incorrect: '' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO code_sent: 'A recovery code to reset your password has been sent to your email.' + # TODO incorrect: 'The recovery code is not correct! You have %count tries remaining.' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&cPanaudojimas: /captcha %captcha_code' wrong_captcha: '&cNeteisinga Captcha, naudokite : /captcha %captcha_code' valid_captcha: '&cJusu captcha Teisinga!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: - second: '' - seconds: '' - minute: '' - minutes: '' - hour: '' - hours: '' - day: '' - days: '' + # TODO second: 'second' + # TODO seconds: 'seconds' + # TODO minute: 'minute' + # TODO minutes: 'minutes' + # TODO hour: 'hour' + # TODO hours: 'hours' + # TODO day: 'day' + # TODO days: 'days' \ No newline at end of file diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index b0e0f30b5..0f8b8ba57 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cGebruik: /login ' wrong_password: '&cFout wachtwoord' - success: '' + # TODO success: '&2Successful login!' login_request: '&cLog in met: /login ' timeout_error: 'Login time-out: het duurde te lang tot je inlogde.' @@ -31,7 +31,7 @@ error: unregistered_user: '&cDeze gebruikersnaam is niet geregistreerd!' not_logged_in: '&cNiet ingelogd!' no_permission: '&cJe hebt geen rechten om deze actie uit te voeren!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: 'Je hebt het maximum aantal registraties overschreden (%reg_count/%max_acc: %reg_names).' logged_in: '&cJe bent al ingelogd!' kick_for_vip: '&cEen VIP-gebruiker heeft ingelogd toen de server vol was!' @@ -111,18 +111,18 @@ captcha: usage_captcha: '&3Om in te loggen moet je een captcha-code oplossen, gebruik het commando: /captcha %captcha_code' wrong_captcha: '&cVerkeerde captcha-code, typ alsjeblieft "/captcha %captcha_code" in de chat!' valid_captcha: '&2De captcha-code is geldig!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'uur' hours: 'uren' day: 'dag' - days: 'dagen' + days: 'dagen' \ No newline at end of file diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 755c9cca7..44c0676a3 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUżycie: /login hasło' wrong_password: '&cNiepoprawne hasło.' - success: '' + # TODO success: '&2Successful login!' login_request: '&2Proszę się zalogować przy użyciu &6/login ' timeout_error: '&cUpłynął limit czasu zalogowania' @@ -31,7 +31,7 @@ error: unregistered_user: '&fGracz nie jest zarejestrowany.' not_logged_in: '&4Nie jesteś zalogowany!' no_permission: '&4Nie posiadasz wymaganych uprawnień.' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cPrzekroczyłeś limit zarejestrowanych kont na serwerze &8(&e%reg_count/%max_acc %reg_names&8) &cdla twojego połączenia.' logged_in: '&fJesteś już zalogowany!' kick_for_vip: '&cGracz VIP dołączył do gry!' @@ -133,4 +133,4 @@ time: hour: 'godziny' hours: 'godzin' day: 'dzień' - days: 'dni' + days: 'dni' \ No newline at end of file diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index df5604152..33badd111 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUse: /login ' wrong_password: '&cPassword errada!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cIdentifique-se com "/login "' timeout_error: '&fExcedeu o tempo para autenticação' @@ -31,7 +31,7 @@ error: unregistered_user: '&cUsername não registado' not_logged_in: '&cNão autenticado!' no_permission: '&cSem Permissões' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cAtingiu o numero máximo de %reg_count contas registas, maximo de contas %max_acc' logged_in: '&cJá se encontra autenticado!' kick_for_vip: '&cUm jogador VIP entrou no servidor cheio!' @@ -111,18 +111,18 @@ captcha: usage_captcha: '&cPrecisa digitar um captcha, escreva: /captcha %captcha_code' wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha %captcha_code' valid_captcha: '&cO seu captcha é válido!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'hora' hours: 'horas' day: 'dia' - days: 'dias' + days: 'dias' \ No newline at end of file diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml index 11d77a1d9..11ef01a50 100644 --- a/src/main/resources/messages/messages_ro.yml +++ b/src/main/resources/messages/messages_ro.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cFoloseste comanda "/login " pentru a te autentifica.' wrong_password: '&cParola gresita!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cTe rugam sa te autentifici folosind comanda "/login "' timeout_error: '&4A expirat timpul de autentificare si ai fost dat afara de server, te rugam incearca din nou!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cAcest jucator nu este inregistrat!' not_logged_in: '&cNu te-ai autentificat!' no_permission: '&4Nu ai permisiunea!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cTe-ai inregistrat cu prea multe conturi pe acelasi ip! (%reg_count/%max_acc %reg_names)' logged_in: '&cEsti deja autentificat!' kick_for_vip: '&3Un V.I.P a intrat pe server cand era plin!' @@ -111,18 +111,18 @@ captcha: usage_captcha: '&3Pentru a te autentifica, te rugam sa folosesti comanda "/captcha %captcha_code"' wrong_captcha: '&cCodul de verificare este gresit, te rugam foloseste comanda "/captcha %captcha_code"!' valid_captcha: '&2Codul de verificare a fost scris corect!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'ora' hours: 'ore' day: 'zi' - days: 'zile' + days: 'zile' \ No newline at end of file diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 6378e2036..594292b98 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cИспользование: /login <пароль>' wrong_password: '&cНеправильный пароль!' - success: '' + # TODO success: '&2Successful login!' login_request: '&3Авторизация: /login <Пароль>' timeout_error: '&4Время авторизации истекло.' @@ -31,7 +31,7 @@ error: unregistered_user: '&cИгрок с таким именем не зарегистрирован.' not_logged_in: '&cВы ещё не вошли!' no_permission: '&4Недостаточно прав.' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cПревышено максимальное количество регистраций на сервере! (%reg_count/%max_acc %reg_names)' logged_in: '&cВы уже авторизированы!' kick_for_vip: '&3VIP-игрок зашёл на переполненный сервер.' @@ -111,8 +111,8 @@ captcha: usage_captcha: '&3Необходимо ввести текст с каптчи. Используйте «/captcha %captcha_code»' wrong_captcha: '&cНеверно! Используйте «/captcha %captcha_code».' valid_captcha: '&2Вы успешно решили каптчу!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: @@ -133,4 +133,4 @@ time: hour: 'ч.' hours: 'ч.' day: 'дн.' - days: 'дн.' + days: 'дн.' \ No newline at end of file diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index 652fa2bc4..204489f4d 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -27,7 +27,7 @@ password: login: command_usage: '&cPoužitie: /login ' wrong_password: '&cZadal si zlé heslo.' - success: '' + # TODO success: '&2Successful login!' login_request: '&cPrihlás sa príkazom "/login ".' timeout_error: '&fVypršal čas na prihlásenie, pripoj sa a skús to znovu.' @@ -38,7 +38,7 @@ error: unregistered_user: '&cZadané meno nie je zaregistrované!' not_logged_in: '&cNie si ešte prihlásený!' no_permission: '&cNemáš dostatočné práva na vykonanie tejto činnosti.' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&fPrekročil si maximum registrovaných účtov(%reg_count/%max_acc|%reg_names).' logged_in: '&cAktuálne si už prihlásený!' kick_for_vip: '&3Uvoľnil si miesto pre VIP hráča!' @@ -118,18 +118,18 @@ captcha: usage_captcha: '&3Pre prihlásenie musíš vyriešiť captcha kód, prosím použi príkaz: /captcha %captcha_code' wrong_captcha: '&cNesprávny kód captcha, prosím napíš "/captcha %captcha_code" do chatu!' valid_captcha: '&2Správne si vyriešil captcha kód!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -140,4 +140,4 @@ time: hour: 'hod.' hours: 'hod.' day: 'd.' - days: 'd.' + days: 'd.' \ No newline at end of file diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index c92ae3028..13dc08dc8 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cKullanim: /login ' wrong_password: '&cYanlis sifre!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cLutfen giris komutunu kullanin "/login "' timeout_error: '&4Giris izni icin verilen zaman suresini astigin icin sunucudan atildin, tekrar deneyin!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cBu oyuncu kayitli degil!' not_logged_in: '&cGiris yapmadin!' no_permission: '&4Bunu yapmak icin iznin yok!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cSen maksimum kayit sinirini astin (%reg_count/%max_acc %reg_names)!' logged_in: '&cZaten giris yaptin!' kick_for_vip: '&3Bir VIP oyuna giris yaptigi icin atildin!' @@ -91,7 +91,7 @@ email: already_used: '&4Eposta adresi zaten kullaniliyor.' incomplete_settings: 'Hata: Gonderilen epostada bazi ayarlar tamamlanmis degil. Yetkili ile iletisime gec.' send_failure: 'Eposta gonderilemedi. Yetkili ile iletisime gec.' - change_password_expired: '' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' email_cooldown_error: '&cKisa bir sure once eposta gonderildi. Yeni bir eposta almak icin %time beklemelisin.' # Password recovery by email @@ -102,27 +102,27 @@ recovery: code: code_sent: 'Sifre sifirlama kodu eposta adresinize gonderildi.' incorrect: 'Kod dogru degil! Kullanim "/email recovery [eposta]" ile yeni bir kod olustur' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&3Giris yapmak icin guvenlik kodunu komut yazarak girin "/captcha %captcha_code"' wrong_captcha: '&cYanlis guvenlik kodu, kullanim sekli "/captcha %captcha_code" sohbete yazin!' valid_captcha: '&2Guvenlik kodu dogrulandi!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'saat' hours: 'saat' day: 'gun' - days: 'gun' + days: 'gun' \ No newline at end of file diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index 86f147e2a..20f2a27dd 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cСинтаксис: /login <пароль>' wrong_password: '&cНевірний пароль!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cДля авторизації, введіть команду "/login <пароль>"' timeout_error: '&4Час для авторизації сплинув. Будь ласка, спробуйте ще раз!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cЦей гравець не є зареєстрованим.' not_logged_in: '&cВи не авторизовані!' no_permission: '&4У вас недостатньо прав, щоб застосувати цю команду!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cВичерпано ліміт реєстрацій (%reg_count/%max_acc %reg_names) для вашого підключення!' logged_in: '&cВи вже авторизовані!' kick_for_vip: '&3Вас кікнуто, внаслідок того, що VIP гравець зайшов на сервер коли небуло вільних місць.' @@ -86,13 +86,13 @@ email: added: '&2Електронну пошту успішно прив’язано до вашого акаунта.' request_confirmation: '&cАдреси не співпадають.' changed: '&2E-mail успішно змінено.' - email_show: '' - no_email_for_account: '' + # TODO email_show: '&2Your current email address is: &f%email' + # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' already_used: '&4До цієї електронної пошти прив’язано забагато акаунтів!' incomplete_settings: '&4[AuthMe] Error: Не всі необхідні налаштування є встановленими, щоб надсилати електронну пошту. Будь ласка, повідомте адміністратора!' - send_failure: '' - change_password_expired: '' - email_cooldown_error: '' + # TODO send_failure: 'The email could not be sent. Please contact an administrator.' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' + # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' # Password recovery by email recovery: @@ -100,37 +100,37 @@ recovery: command_usage: '&cСинтаксис: /email recovery ' email_sent: '&2Лист для відновлення доступу надіслано. Будь ласка, провірте свою пошту!' code: - code_sent: '' - incorrect: '' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO code_sent: 'A recovery code to reset your password has been sent to your email.' + # TODO incorrect: 'The recovery code is not correct! You have %count tries remaining.' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&3Для продовження доведеться ввести капчу — "/captcha %captcha_code"' wrong_captcha: '&cНевірно введена капча! Спробуйте ще раз — "/captcha %captcha_code"' valid_captcha: '&2Капчу прийнято.' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: - second: '' - seconds: '' - minute: '' - minutes: '' - hour: '' - hours: '' - day: '' - days: '' + # TODO second: 'second' + # TODO seconds: 'seconds' + # TODO minute: 'minute' + # TODO minutes: 'minutes' + # TODO hour: 'hour' + # TODO hours: 'hours' + # TODO day: 'day' + # TODO days: 'days' \ No newline at end of file diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index a880c8a77..a62770045 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cSử dụng: /login ' wrong_password: '&cSai mật khẩu!' - success: '' + # TODO success: '&2Successful login!' login_request: '&cXin vui lòng đăng nhập bằng lệnh "/login "' timeout_error: '&4Thời gian đăng nhập đã hết, bạn đã bị văng khỏi máy chủ. Xin vui lòng thử lại!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cNgười dùng này đã được đăng ký!' not_logged_in: '&cBạn chưa đăng nhập!' no_permission: '&4Bạn không có quyền truy cập lệnh này!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&cBạn đã vượt quá giới hạn tối đa đăng ký tài khoản (%reg_count/%max_acc %reg_names) cho những lần kết nối tài khoản!' logged_in: '&cBạn đã đăng nhập!' kick_for_vip: '&eChỉ có thành viên VIP mới được tham gia khi máy chủ đầy!' @@ -102,27 +102,27 @@ recovery: code: code_sent: 'Một mã khôi phục mật khẩu đã được gửi đến địa chỉ email của bạn.' incorrect: 'Mã khôi phục không đúng! Dùng lệnh /email recovery [email] để tạo một mã mới' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&eĐể đăng nhập vui lòng hãy nhập mã Captcha, gõ lệnh "/captcha %captcha_code"' wrong_captcha: '&cSai mã captcha, Vui lòng gõ lệnh "/captcha %captcha_code"!' valid_captcha: '&2Mã captcha đã được xác nhận!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -133,4 +133,4 @@ time: hour: 'giờ' hours: 'giờ' day: 'ngày' - days: 'ngày' + days: 'ngày' \ No newline at end of file diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index ac7fdc25d..19d436581 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&8[&6玩家系统&8] &c正确用法:“/login <密码>”' wrong_password: '&8[&6玩家系统&8] &c错误的密码' - success: '' + # TODO success: '&2Successful login!' login_request: '&8[&6玩家系统&8] &c请输入“/login <密码>”以登录' timeout_error: '&8[&6玩家系统&8] &f给你登录的时间已经过了' @@ -31,7 +31,7 @@ error: unregistered_user: '&8[&6玩家系统&8] &c此用户名还未注册过' not_logged_in: '&8[&6玩家系统&8] &c你还未登录!' no_permission: '&8[&6玩家系统&8] &c没有权限' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&8[&6玩家系统&8] &f你不允许再为你的IP在服务器注册更多用户了!' logged_in: '&8[&6玩家系统&8] &c你已经登陆过了!' kick_for_vip: '&8[&6玩家系统&8] &cA VIP玩家加入了已满的服务器!' @@ -133,4 +133,4 @@ time: hour: '小时' hours: '小时' day: '天' - days: '天' + days: '天' \ No newline at end of file diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index 74c5d71ea..3622458c1 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -24,7 +24,7 @@ password: login: command_usage: '&8[&6用戶系統&8] &f用法:《 /login <密碼> 》' wrong_password: '&8[&6用戶系統&8] &c你輸入了錯誤的密碼。' - success: '' + # TODO success: '&2Successful login!' login_request: '&8[&6用戶系統&8] &c請使用這個指令來登入:《 /login <密碼> 》' timeout_error: '&8[&6用戶系統&8] &f登入逾時。' @@ -35,7 +35,7 @@ error: unregistered_user: '&8[&6用戶系統&8] &c此用戶名沒有已登記資料。' not_logged_in: '&8[&6用戶系統&8] &c你還沒有登入 !' no_permission: '&8[&6用戶系統&8] &b嗯~你想幹甚麼?' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&8[&6用戶系統&8] &f你的IP地址已達到註冊數上限。 &7(info: %reg_count/%max_acc %reg_names)' logged_in: '&8[&6用戶系統&8] &c你已經登入過了。' kick_for_vip: '&c喔!因為有VIP玩家登入了伺服器。' @@ -115,18 +115,18 @@ captcha: usage_captcha: '&8[&6用戶系統&8] &f用法:《 /captcha %captcha_code 》' wrong_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效,請使用 《 /captcha %captcha_code 》 再次輸入。' valid_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效 !' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: @@ -137,4 +137,4 @@ time: hour: '小時' hours: '小時' day: '日' - days: '日' + days: '日' \ No newline at end of file diff --git a/src/main/resources/messages/messages_zhmc.yml b/src/main/resources/messages/messages_zhmc.yml index 458bce2b1..b8508b99a 100644 --- a/src/main/resources/messages/messages_zhmc.yml +++ b/src/main/resources/messages/messages_zhmc.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&b使用方法: 輸入"/login [你的密碼]" 來登入' wrong_password: '&c密碼錯誤!' - success: '' + # TODO success: '&2Successful login!' login_request: '&c [請先登入] 請按T , 然後輸入 "/login [你的密碼]" 。' timeout_error: '&4超過登錄超時,您已從伺服器中踢出,請重試!' @@ -31,7 +31,7 @@ error: unregistered_user: '&c此用戶尚未注冊!' not_logged_in: '&c你尚未登入!' no_permission: '&4您沒有執行此操作的權限!' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&c您已超過註冊的最大數量(%reg_count/%max_acc %reg_names)!' logged_in: '&c您已經登錄!' kick_for_vip: '&3一名VIP玩家在服務器已滿時已加入伺服器!' @@ -86,13 +86,13 @@ email: added: '&2電子郵件地址已成功添加到您的帳戶!' request_confirmation: '&c請確認你的電郵地址!' changed: '&2已正確地更改電子郵件地址!' - email_show: '' - no_email_for_account: '' + # TODO email_show: '&2Your current email address is: &f%email' + # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' already_used: '&4此電子郵件地址已被使用' incomplete_settings: '缺少必要的配置來為發送電子郵件。請聯繫管理員。' - send_failure: '' - change_password_expired: '' - email_cooldown_error: '' + # TODO send_failure: 'The email could not be sent. Please contact an administrator.' + # TODO change_password_expired: 'You cannot change your password using this command anymore.' + # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' # Password recovery by email recovery: @@ -102,35 +102,35 @@ recovery: code: code_sent: '已將重設密碼的恢復代碼發送到您的電子郵件。' incorrect: '恢復代碼錯誤!使用指令: "/email recovery [電郵地址]" 生成新的一個恢復代碼。' - tries_exceeded: '' - correct: '' - change_password: '' + # TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.' + # TODO correct: 'Recovery code entered correctly!' + # TODO change_password: 'Please use the command /email setpassword to change your password immediately.' # Captcha captcha: usage_captcha: '&3T要登錄您必須使用captcha驗證碼,請使用命令: "/captcha %captcha_code"' wrong_captcha: '&c驗證碼錯誤!請按T在聊天中輸入 "/captcha %captcha_code"' valid_captcha: '&2驗證碼正確!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: - code_required: '' - command_usage: '' - incorrect_code: '' - success: '' - already_verified: '' - code_expired: '' - email_needed: '' + # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' + # TODO command_usage: '&cUsage: /verification ' + # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' + # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' + # TODO already_verified: '&2You can already execute every sensitive command within the current session!' + # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' + # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' # Time units time: - second: '' - seconds: '' - minute: '' - minutes: '' - hour: '' - hours: '' - day: '' - days: '' + # TODO second: 'second' + # TODO seconds: 'seconds' + # TODO minute: 'minute' + # TODO minutes: 'minutes' + # TODO hour: 'hour' + # TODO hours: 'hours' + # TODO day: 'day' + # TODO days: 'days' \ No newline at end of file diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index 81cfca473..556021dda 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -23,7 +23,7 @@ password: login: command_usage: '&b【AuthMe】&6用法: &c"/login <密碼>"' wrong_password: '&b【AuthMe】&6密碼錯誤!' - success: '' + # TODO success: '&2Successful login!' login_request: '&b【AuthMe】&6請使用 &c"/login <密碼>" &6來登入。' timeout_error: '&b【AuthMe】&6超過登入時間,請稍後再試一次' @@ -34,7 +34,7 @@ error: unregistered_user: '&b【AuthMe】&6這個帳號還沒有註冊過' not_logged_in: '&b【AuthMe】&6您還沒有登入!' no_permission: '&b【AuthMe】&6您沒有使用該指令的權限。' - unexpected_error: '' + # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' max_registration: '&b【AuthMe】&6您的 IP 位置所註冊的帳號數量已經達到最大。' logged_in: '&b【AuthMe】&6您已經登入了!' kick_for_vip: '&b【AuthMe】&6您已經被請出。&c原因 : 有 VIP 玩家登入伺服器' @@ -114,8 +114,8 @@ captcha: usage_captcha: '&b【AuthMe】&6請用 &c"/captcha %captcha_code" &6來輸入您的驗證碼.' wrong_captcha: '&b【AuthMe】&6錯誤的驗證碼,請使用 "/captcha %captcha_code" 再試一次.' valid_captcha: '&b【AuthMe】&6驗證碼無效!' - captcha_for_registration: '' - register_captcha_valid: '' + # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' + # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' # Verification code verification: @@ -136,4 +136,4 @@ time: hour: '時' hours: '時' day: '天' - days: '天' + days: '天' \ No newline at end of file diff --git a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java index 1d1abef5c..c75be723e 100644 --- a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java +++ b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java @@ -1,6 +1,9 @@ package fr.xephi.authme.message.updater; +import ch.jalu.configme.configurationdata.ConfigurationData; +import ch.jalu.configme.properties.Property; import com.google.common.io.Files; +import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.message.MessageKey; import org.bukkit.configuration.file.FileConfiguration; @@ -12,6 +15,10 @@ import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -92,4 +99,35 @@ public class MessageUpdaterTest { assertThat(configuration.getString(MessageKey.SECONDS.getKey()), equalTo("seconds in plural")); } + + @Test + public void shouldHaveAllKeysInConfigurationData() { + // given + Set messageKeysFromEnum = Arrays.stream(MessageKey.values()) + .map(MessageKey::getKey) + .collect(Collectors.toSet()); + + // when + Set messageKeysFromConfigData = MessageUpdater.CONFIGURATION_DATA.getProperties().stream() + .map(Property::getPath) + .collect(Collectors.toSet()); + + // then + assertThat(messageKeysFromConfigData, equalTo(messageKeysFromEnum)); + } + + @Test + public void shouldHaveCommentForAllRootPathsInConfigurationData() { + // given + Set rootPaths = Arrays.stream(MessageKey.values()) + .map(key -> key.getKey().split("\\.")[0]) + .collect(Collectors.toSet()); + + // when + Map comments = ReflectionTestUtils.getFieldValue( + ConfigurationData.class, MessageUpdater.CONFIGURATION_DATA, "sectionComments"); + + // then + assertThat(comments.keySet(), equalTo(rootPaths)); + } } diff --git a/src/test/java/tools/messages/MessageFileComments.java b/src/test/java/tools/messages/MessageFileComments.java deleted file mode 100644 index eaa5d5eed..000000000 --- a/src/test/java/tools/messages/MessageFileComments.java +++ /dev/null @@ -1,13 +0,0 @@ -package tools.messages; - -import java.util.List; - -/** - * Represents a section of one or more consecutive comment lines in a file. - */ -public class MessageFileComments extends MessageFileElement { - - public MessageFileComments(List lines) { - super(lines); - } -} diff --git a/src/test/java/tools/messages/MessageFileElement.java b/src/test/java/tools/messages/MessageFileElement.java deleted file mode 100644 index 775d7f80e..000000000 --- a/src/test/java/tools/messages/MessageFileElement.java +++ /dev/null @@ -1,20 +0,0 @@ -package tools.messages; - -import java.util.Collections; -import java.util.List; - -/** - * An element (a logical unit) in a messages file. - */ -public abstract class MessageFileElement { - - private final List lines; - - protected MessageFileElement(List lines) { - this.lines = Collections.unmodifiableList(lines); - } - - public List getLines() { - return lines; - } -} diff --git a/src/test/java/tools/messages/MessageFileElementMerger.java b/src/test/java/tools/messages/MessageFileElementMerger.java deleted file mode 100644 index ca5720d3d..000000000 --- a/src/test/java/tools/messages/MessageFileElementMerger.java +++ /dev/null @@ -1,132 +0,0 @@ -package tools.messages; - -import fr.xephi.authme.message.MessageKey; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Creates the same order of message file elements as the given default message elements, - * using the local file's own elements as much as possible and filling in elements from the - * default messages file where necessary. - *

- * The current implementation (of this merger and of the {@link MessageFileElementReader reader}) - * has the following limitations: - *

    - *
  • It assumes that new comments are only ever added to the bottom of the default file.
  • - *
  • If a file only has a partial number of the comments present in the default messages file, - * the file's comments will be moved to the top. This most likely adds the comment above the - * wrong group of messages.
  • - *
  • Assumes that the text for a message only takes one line.
  • - *
  • Ignores the last comment section of a file if it is not followed by any message entry.
  • - *
- */ -public class MessageFileElementMerger { - - /** Ordered list of comments in the messages file. */ - private final List comments; - /** List of message entries by corresponding MessageKey. */ - private final Map entries; - /** - * Ordered list of file elements of the default file. The entries of the (non-default) messages - * file are based on this. - */ - private final List defaultFileElements; - /** Missing tags in message entries. */ - private final Map> missingTags; - /** Counter for encountered comment elements. */ - private int commentsCounter = 0; - - private MessageFileElementMerger(List defaultFileElements, - List comments, - Map entries, - Map> missingTags) { - this.defaultFileElements = defaultFileElements; - this.comments = comments; - this.entries = entries; - this.missingTags = missingTags; - } - - /** - * Returns a list of file elements that follow the order and type of the provided default file elements. - * In other words, using the list of default file elements as template and fallback, it returns the provided - * file elements in the same order and fills in default file elements if an equivalent in {@code fileElements} - * is not present. - * - * @param fileElements file elements to sort and merge - * @param defaultFileElements file elements of the default file to base the operation on - * @param missingTags list of missing tags per message key - * @return ordered and complete list of file elements - */ - public static List mergeElements(List fileElements, - List defaultFileElements, - Map> missingTags) { - List comments = filteredStream(fileElements, MessageFileComments.class) - .collect(Collectors.toList()); - Map entries = filteredStream(fileElements, MessageFileEntry.class) - .collect(Collectors.toMap(MessageFileEntry::getMessageKey, Function.identity(), (e1, e2) -> e1)); - - MessageFileElementMerger merger = new MessageFileElementMerger( - defaultFileElements, comments, entries, missingTags); - return merger.mergeElements(); - } - - private List mergeElements() { - List mergedElements = new ArrayList<>(defaultFileElements.size()); - for (MessageFileElement element : defaultFileElements) { - if (element instanceof MessageFileComments) { - mergedElements.add(getCommentsEntry((MessageFileComments) element)); - } else if (element instanceof MessageFileEntry) { - mergedElements.add(getEntryForDefaultMessageEntry((MessageFileEntry) element)); - } else { - throw new IllegalStateException("Found element of unknown subtype '" + element.getClass() + "'"); - } - } - return mergedElements; - } - - private MessageFileComments getCommentsEntry(MessageFileComments defaultComments) { - if (comments.size() > commentsCounter) { - MessageFileComments localComments = comments.get(commentsCounter); - ++commentsCounter; - return localComments; - } - return defaultComments; - } - - private MessageFileElement getEntryForDefaultMessageEntry(MessageFileEntry entry) { - MessageKey messageKey = entry.getMessageKey(); - if (messageKey == null) { - throw new IllegalStateException("Default message file should not have unknown entries, but " - + " entry with lines '" + entry.getLines() + "' has message key = null"); - } - - MessageFileEntry localEntry = entries.get(messageKey); - if (localEntry == null) { - return entry.convertToMissingEntryComment(); - } - - Collection absentTags = missingTags.get(messageKey); - return absentTags == null - ? localEntry - : localEntry.convertToEntryWithMissingTagsComment(absentTags); - } - - /** - * Creates a stream of the entries in {@code collection} with only the elements which are of type {@code clazz}. - * - * @param collection the collection to stream over - * @param clazz the class to restrict the elements to - * @param

the collection type (parent) - * @param the type to restrict to (child) - * @return stream over all elements of the given type - */ - private static Stream filteredStream(Collection

collection, Class clazz) { - return collection.stream().filter(clazz::isInstance).map(clazz::cast); - } -} diff --git a/src/test/java/tools/messages/MessageFileElementReader.java b/src/test/java/tools/messages/MessageFileElementReader.java deleted file mode 100644 index 54d08b50e..000000000 --- a/src/test/java/tools/messages/MessageFileElementReader.java +++ /dev/null @@ -1,77 +0,0 @@ -package tools.messages; - -import tools.utils.FileIoUtils; - -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Reads a messages file and returns the lines as corresponding {@link MessageFileElement} objects. - * - * @see MessageFileElementMerger - */ -public class MessageFileElementReader { - - private final List elements = new ArrayList<>(); - - private MessageFileElementReader() { - } - - /** - * Returns the message files as separate {@link MessageFileElement elements}. - * - * @param file the file to read - * @return the file's elements - */ - public static List readFileIntoElements(File file) { - checkArgument(file.exists(), "Template file '" + file + "' must exist"); - - MessageFileElementReader reader = new MessageFileElementReader(); - reader.loadElements(file.toPath()); - return reader.elements; - } - - private void loadElements(Path path) { - List currentCommentSection = new ArrayList<>(10); - for (String line : FileIoUtils.readLinesFromFile(path)) { - if (isCommentLine(line)) { - currentCommentSection.add(line); - } else { - if (!currentCommentSection.isEmpty()) { - processTempCommentsList(currentCommentSection); - } - if (MessageFileEntry.isMessageEntry(line)) { - elements.add(new MessageFileEntry(line)); - } else if (!isTodoComment(line)) { - throw new IllegalStateException("Could not match line '" + line + "' to any type"); - } - } - } - } - - /** - * Creates a message file comments element for one or more read comment lines. Does not add - * a comments element if the read lines are only empty lines. - * - * @param comments the read comment lines - */ - private void processTempCommentsList(List comments) { - if (comments.stream().anyMatch(c -> !c.trim().isEmpty())) { - elements.add(new MessageFileComments(new ArrayList<>(comments))); - } - comments.clear(); - } - - private static boolean isCommentLine(String line) { - return !isTodoComment(line) - && (line.trim().isEmpty() || line.trim().startsWith("#")); - } - - private static boolean isTodoComment(String line) { - return line.startsWith("# TODO"); - } -} diff --git a/src/test/java/tools/messages/MessageFileEntry.java b/src/test/java/tools/messages/MessageFileEntry.java deleted file mode 100644 index 788e0b6d3..000000000 --- a/src/test/java/tools/messages/MessageFileEntry.java +++ /dev/null @@ -1,85 +0,0 @@ -package tools.messages; - -import fr.xephi.authme.message.MessageKey; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static java.util.Collections.singletonList; - -/** - * Entry in a message file for a message key. - */ -public class MessageFileEntry extends MessageFileElement { - - private static final Pattern MESSAGE_ENTRY_REGEX = Pattern.compile("([a-zA-Z_-]+): .*"); - private final MessageKey messageKey; - - public MessageFileEntry(String line) { - this(singletonList(line), extractMessageKey(line)); - } - - private MessageFileEntry(List lines, MessageKey messageKey) { - super(lines); - this.messageKey = messageKey; - } - - public static boolean isMessageEntry(String line) { - return MESSAGE_ENTRY_REGEX.matcher(line).matches(); - } - - public MessageKey getMessageKey() { - return messageKey; - } - - /** - * Based on this entry, creates a comments element indicating that this message is missing. - * - * @return comments element based on this message element - */ - public MessageFileComments convertToMissingEntryComment() { - List comments = getLines().stream().map(l -> "# TODO " + l).collect(Collectors.toList()); - return new MessageFileComments(comments); - } - - /** - * Creates an adapted message file entry object with a comment for missing tags. - * - * @param missingTags the tags missing in the message - * @return message file entry with verification comment - */ - public MessageFileEntry convertToEntryWithMissingTagsComment(Collection missingTags) { - List lines = new ArrayList<>(getLines().size() + 1); - lines.add("# TODO: Missing tags " + String.join(", ", missingTags)); - lines.addAll(getLines()); - return new MessageFileEntry(lines, messageKey); - } - - /** - * Returns the {@link MessageKey} this entry is for. Returns {@code null} if the message key could not be matched. - * - * @param line the line to process - * @return the associated message key, or {@code null} if no match was found - */ - private static MessageKey extractMessageKey(String line) { - Matcher matcher = MESSAGE_ENTRY_REGEX.matcher(line); - if (matcher.find()) { - String key = matcher.group(1); - return fromKey(key); - } - throw new IllegalStateException("Could not extract message key from line '" + line + "'"); - } - - private static MessageKey fromKey(String key) { - for (MessageKey messageKey : MessageKey.values()) { - if (messageKey.getKey().equals(key)) { - return messageKey; - } - } - return null; - } -} diff --git a/src/test/java/tools/messages/MessageFileVerifier.java b/src/test/java/tools/messages/MessageFileVerifier.java index 058799054..dd33e487a 100644 --- a/src/test/java/tools/messages/MessageFileVerifier.java +++ b/src/test/java/tools/messages/MessageFileVerifier.java @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import fr.xephi.authme.message.MessageKey; +import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -62,6 +63,13 @@ public class MessageFileVerifier { return missingTags; } + /** + * @return true if the verifier has found an issue with the analyzed file, false otherwise + */ + public boolean hasErrors() { + return !missingKeys.isEmpty() || !missingTags.isEmpty() || !unknownKeys.isEmpty(); + } + private void verifyKeys() { FileConfiguration configuration = YamlConfiguration.loadConfiguration(messagesFile); @@ -77,12 +85,16 @@ public class MessageFileVerifier { // Check FileConfiguration for all of its keys to find unknown keys for (String key : configuration.getValues(true).keySet()) { - if (!messageKeyExists(key)) { + if (isNotInnerNode(key, configuration) && !messageKeyExists(key)) { unknownKeys.add(key); } } } + private static boolean isNotInnerNode(String key, FileConfiguration configuration) { + return !(configuration.get(key) instanceof MemorySection); + } + private void checkTagsInMessage(MessageKey messageKey, String message) { for (String tag : messageKey.getTags()) { if (!message.contains(tag)) { diff --git a/src/test/java/tools/messages/MessagesFileWriter.java b/src/test/java/tools/messages/MessagesFileWriter.java new file mode 100644 index 000000000..e47d10a05 --- /dev/null +++ b/src/test/java/tools/messages/MessagesFileWriter.java @@ -0,0 +1,148 @@ +package tools.messages; + +import ch.jalu.configme.SettingsManager; +import ch.jalu.configme.configurationdata.ConfigurationData; +import ch.jalu.configme.properties.Property; +import ch.jalu.configme.resource.PropertyResource; +import ch.jalu.configme.resource.YamlFileResource; +import fr.xephi.authme.message.updater.MessageUpdater; +import fr.xephi.authme.message.updater.MessageUpdater.MigraterYamlFileResource; +import org.bukkit.configuration.file.FileConfiguration; +import tools.utils.FileIoUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Writes to a messages file, adding comments with the default file's message where + * entries are missing. + *

+ * This writer writes to the file twice: once with ConfigMe to ensure a proper order + * of the properties and comments, and a second time to add any custom comments that + * were at the top of the file and to separate comments by new lines (which ConfigMe + * currently doesn't support). + */ +public class MessagesFileWriter { + + /** Marker used inside a text to signal that it should be a comment later on. */ + private static final String COMMENT_MARKER = "::COMMENT::"; + + private static final Pattern SPACES_BEFORE_TEXT_PATTERN = Pattern.compile("(\\s+)\\w.*"); + + /** The messages file to update. */ + private final File file; + /** Messages from the default file. */ + private final FileConfiguration defaultFile; + + private MessagesFileWriter(File file, FileConfiguration defaultFile) { + this.file = file; + this.defaultFile = defaultFile; + } + + public static void writeToFileWithCommentsFromDefault(File file, FileConfiguration configuration) { + new MessagesFileWriter(file, configuration).performWrite(); + } + + private void performWrite() { + // Store initial comments so we can add them back later + List initialComments = getInitialUserComments(); + + // Create property resource with new defaults, save with ConfigMe for proper sections & comments + PropertyResource resource = createPropertyResourceWithCommentEntries(); + new SettingsManager(resource, null, MessageUpdater.CONFIGURATION_DATA).save(); + + // Go through the newly saved file and replace texts with comment marker to actual YAML comments + // and add initial comments back to the file + rewriteToFileWithComments(initialComments); + } + + /** + * @return any custom comments at the top of the file, for later usage + */ + private List getInitialUserComments() { + final List initialComments = new ArrayList<>(); + final String firstCommentByConfigMe = getFirstCommentByConfigMe(); + + for (String line : FileIoUtils.readLinesFromFile(file.toPath())) { + if (line.isEmpty() || line.startsWith("#") && !line.equals(firstCommentByConfigMe)) { + initialComments.add(line); + } else { + break; + } + } + // Small fix: so we can keep running this writer and get the same result, we need to make sure that any ending + // empty lines are removed + for (int i = initialComments.size() - 1; i >= 0; --i) { + if (initialComments.get(i).isEmpty()) { + initialComments.remove(i); + } else { + break; + } + } + return initialComments; + } + + /** + * @return the first comment generated by ConfigMe (comment of the first root path) + */ + private static String getFirstCommentByConfigMe() { + ConfigurationData configurationData = MessageUpdater.CONFIGURATION_DATA; + String firstRootPath = configurationData.getProperties().get(0).getPath().split("\\.")[0]; + return "# " + configurationData.getCommentsForSection(firstRootPath)[0]; + } + + /** + * @return generated {@link PropertyResource} with missing entries taken from the default file and marked + * with the {@link #COMMENT_MARKER} + */ + private PropertyResource createPropertyResourceWithCommentEntries() { + YamlFileResource resource = new MigraterYamlFileResource(file); + for (Property property : MessageUpdater.CONFIGURATION_DATA.getProperties()) { + String text = resource.getString(property.getPath()); + if (text == null) { + resource.setValue(property.getPath(), COMMENT_MARKER + defaultFile.getString(property.getPath())); + } + } + return resource; + } + + /** + * Writes to the file again, adding the provided initial comments at the top of the file and converting + * any entries marked with {@link #COMMENT_MARKER} to YAML comments. + * + * @param initialComments the comments at the top of the file to add back + */ + private void rewriteToFileWithComments(List initialComments) { + List newLines = new ArrayList<>(initialComments); + + for (String line : FileIoUtils.readLinesFromFile(file.toPath())) { + if (line.contains(COMMENT_MARKER)) { + String lineAsYamlComment = convertLineWithCommentMarkerToYamlComment(line); + newLines.add(lineAsYamlComment); + } else if (line.startsWith("#") && !newLines.isEmpty()) { + // ConfigMe doesn't support empty line between comments, so here we check if we have a comment that + // isn't at the very top and sneak in an empty line if so. + newLines.add(""); + newLines.add(line); + } else if (!line.isEmpty()) { + // ConfigMe adds an empty line at the beginning, so check here that we don't include any empty lines... + newLines.add(line); + } + } + + FileIoUtils.writeToFile(file.toPath(), String.join("\n", newLines)); + } + + private static String convertLineWithCommentMarkerToYamlComment(String line) { + Matcher matcher = SPACES_BEFORE_TEXT_PATTERN.matcher(line); + if (matcher.matches()) { + String spacesBefore = matcher.group(1); + return spacesBefore + "# TODO " + line.replace(COMMENT_MARKER, "").trim(); + } else { + throw new IllegalStateException("Space-counting pattern unexpectedly did not match on line '" + line + "'"); + } + } +} diff --git a/src/test/java/tools/messages/VerifyMessagesTask.java b/src/test/java/tools/messages/VerifyMessagesTask.java index b7e1efe1b..d665ce89a 100644 --- a/src/test/java/tools/messages/VerifyMessagesTask.java +++ b/src/test/java/tools/messages/VerifyMessagesTask.java @@ -1,9 +1,10 @@ package tools.messages; import com.google.common.collect.Multimap; +import de.bananaco.bpermissions.imp.YamlConfiguration; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.util.StringUtils; -import tools.utils.FileIoUtils; +import org.bukkit.configuration.file.FileConfiguration; import tools.utils.ToolTask; import tools.utils.ToolsConstants; @@ -15,7 +16,6 @@ import java.util.Map; import java.util.Scanner; import java.util.Set; import java.util.regex.Pattern; -import java.util.stream.Collectors; import static tools.utils.FileIoUtils.listFilesOrThrow; @@ -54,9 +54,9 @@ public final class VerifyMessagesTask implements ToolTask { messageFiles = Collections.singletonList(customFile); } - List defaultFileElements = null; + FileConfiguration defaultFileConfiguration = null; if (addMissingKeys) { - defaultFileElements = MessageFileElementReader.readFileIntoElements(new File(DEFAULT_MESSAGES_FILE)); + defaultFileConfiguration = YamlConfiguration.loadConfiguration(new File(DEFAULT_MESSAGES_FILE)); } // Verify the given files @@ -65,7 +65,7 @@ public final class VerifyMessagesTask implements ToolTask { MessageFileVerifier verifier = new MessageFileVerifier(file); if (addMissingKeys) { outputVerificationResults(verifier); - updateMessagesFile(file, verifier, defaultFileElements); + updateMessagesFile(file, verifier, defaultFileConfiguration); } else { outputVerificationResults(verifier); } @@ -104,18 +104,13 @@ public final class VerifyMessagesTask implements ToolTask { * * @param file the file to update * @param verifier the verifier whose results should be used - * @param defaultFileElements default file elements to base the new file structure on + * @param defaultConfiguration default file configuration to retrieve missing texts from */ private static void updateMessagesFile(File file, MessageFileVerifier verifier, - List defaultFileElements) { - List messageFileElements = MessageFileElementReader.readFileIntoElements(file); - String newMessageFileContents = MessageFileElementMerger - .mergeElements(messageFileElements, defaultFileElements, verifier.getMissingTags().asMap()) - .stream() - .map(MessageFileElement::getLines) - .flatMap(List::stream) - .collect(Collectors.joining("\n")); - FileIoUtils.writeToFile(file.toPath(), newMessageFileContents + "\n"); + FileConfiguration defaultConfiguration) { + if (verifier.hasErrors()) { + MessagesFileWriter.writeToFileWithCommentsFromDefault(file, defaultConfiguration); + } } From 1d6d9eb764eed3ef008f7d60f98c7a546e981ae6 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 2 Feb 2018 18:53:08 +0100 Subject: [PATCH 024/155] #1467 Rearrange old keys migration so no entries get lost - 'error' was an old entry but now we have multiple entries under 'error' (which is now a section), so we need to ensure that we migrate the old 'error' entry before the migration sets anything under that path --- .../message/updater/MessageUpdater.java | 6 ++- .../updater/OldMessageKeysMigrater.java | 25 ++++++----- src/main/resources/messages/messages_bg.yml | 6 +-- src/main/resources/messages/messages_br.yml | 6 +-- src/main/resources/messages/messages_cz.yml | 6 +-- src/main/resources/messages/messages_de.yml | 6 +-- src/main/resources/messages/messages_eo.yml | 6 +-- src/main/resources/messages/messages_es.yml | 6 +-- src/main/resources/messages/messages_et.yml | 6 +-- src/main/resources/messages/messages_eu.yml | 6 +-- src/main/resources/messages/messages_fi.yml | 6 +-- src/main/resources/messages/messages_fr.yml | 6 +-- src/main/resources/messages/messages_gl.yml | 6 +-- src/main/resources/messages/messages_hu.yml | 6 +-- src/main/resources/messages/messages_id.yml | 6 +-- src/main/resources/messages/messages_it.yml | 6 +-- src/main/resources/messages/messages_ko.yml | 6 +-- src/main/resources/messages/messages_lt.yml | 6 +-- src/main/resources/messages/messages_nl.yml | 6 +-- src/main/resources/messages/messages_pl.yml | 6 +-- src/main/resources/messages/messages_pt.yml | 6 +-- src/main/resources/messages/messages_ro.yml | 6 +-- src/main/resources/messages/messages_ru.yml | 6 +-- src/main/resources/messages/messages_sk.yml | 6 +-- src/main/resources/messages/messages_tr.yml | 6 +-- src/main/resources/messages/messages_uk.yml | 6 +-- src/main/resources/messages/messages_vn.yml | 6 +-- src/main/resources/messages/messages_zhcn.yml | 6 +-- src/main/resources/messages/messages_zhhk.yml | 6 +-- src/main/resources/messages/messages_zhmc.yml | 6 +-- src/main/resources/messages/messages_zhtw.yml | 6 +-- .../message/updater/MessageUpdaterTest.java | 4 +- .../updater/OldMessageKeysMigraterTest.java | 41 +++++++++++++++++++ .../tools/messages/MessagesFileWriter.java | 9 ++-- 34 files changed, 154 insertions(+), 105 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index 38ed7f07a..b4634a712 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -27,7 +27,11 @@ public class MessageUpdater { /** * Configuration data object for all message keys incl. comments associated to sections. */ - public static final ConfigurationData CONFIGURATION_DATA = buildConfigurationData(); + private static final ConfigurationData CONFIGURATION_DATA = buildConfigurationData(); + + public static ConfigurationData getConfigurationData() { + return CONFIGURATION_DATA; + } /** * Applies any necessary migrations to the user's messages file and saves it if it has been modified. diff --git a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java index 4f5e5ad72..c4e0c3143 100644 --- a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java +++ b/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java @@ -1,6 +1,7 @@ package fr.xephi.authme.message.updater; import ch.jalu.configme.resource.PropertyResource; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import fr.xephi.authme.message.MessageKey; @@ -15,16 +16,11 @@ import static com.google.common.collect.ImmutableMap.of; */ final class OldMessageKeysMigrater { - private static final Map> PLACEHOLDER_REPLACEMENTS = - ImmutableMap.>builder() - .put(MessageKey.PASSWORD_CHARACTERS_ERROR, of("REG_EX", "%valid_chars")) - .put(MessageKey.INVALID_NAME_CHARACTERS, of("REG_EX", "%valid_chars")) - .put(MessageKey.USAGE_CAPTCHA, of("", "%captcha_code")) - .put(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, of("", "%captcha_code")) - .put(MessageKey.CAPTCHA_WRONG_ERROR, of("THE_CAPTCHA", "%captcha_code")) - .build(); - private static final Map KEYS_TO_OLD_PATH = ImmutableMap.builder() + @VisibleForTesting + static final Map KEYS_TO_OLD_PATH = ImmutableMap.builder() + .put(MessageKey.LOGIN_SUCCESS, "login") + .put(MessageKey.ERROR, "error") .put(MessageKey.DENIED_COMMAND, "denied_command") .put(MessageKey.SAME_IP_ONLINE, "same_ip_online") .put(MessageKey.DENIED_CHAT, "denied_chat") @@ -36,11 +32,9 @@ final class OldMessageKeysMigrater { .put(MessageKey.UNREGISTERED_SUCCESS, "unregistered") .put(MessageKey.REGISTRATION_DISABLED, "reg_disabled") .put(MessageKey.SESSION_RECONNECTION, "valid_session") - .put(MessageKey.LOGIN_SUCCESS, "login") .put(MessageKey.ACCOUNT_NOT_ACTIVATED, "vb_nonActiv") .put(MessageKey.NAME_ALREADY_REGISTERED, "user_regged") .put(MessageKey.NO_PERMISSION, "no_perm") - .put(MessageKey.ERROR, "error") .put(MessageKey.LOGIN_MESSAGE, "login_msg") .put(MessageKey.REGISTER_MESSAGE, "reg_msg") .put(MessageKey.MAX_REGISTER_EXCEEDED, "max_reg") @@ -121,6 +115,15 @@ final class OldMessageKeysMigrater { .put(MessageKey.DAYS, "days") .build(); + private static final Map> PLACEHOLDER_REPLACEMENTS = + ImmutableMap.>builder() + .put(MessageKey.PASSWORD_CHARACTERS_ERROR, of("REG_EX", "%valid_chars")) + .put(MessageKey.INVALID_NAME_CHARACTERS, of("REG_EX", "%valid_chars")) + .put(MessageKey.USAGE_CAPTCHA, of("", "%captcha_code")) + .put(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, of("", "%captcha_code")) + .put(MessageKey.CAPTCHA_WRONG_ERROR, of("THE_CAPTCHA", "%captcha_code")) + .build(); + private OldMessageKeysMigrater() { } diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index 3dcdd9359..ee21f3f57 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cКоманда: /login парола' wrong_password: '&cГрешна парола!' - # TODO success: '&2Successful login!' + success: '&2Успешен вход!' login_request: '&cМоля влезте с: /login парола' timeout_error: '&4Времето за вход изтече, беше кикнат от сървъра. Моля опитайте отново!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cПотребителското име не е регистрирано!' not_logged_in: '&cНе си влязъл!' no_permission: '&4Нямаш нужните права за това действие!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Получи се неочаквана грешка, моля свържете се с администратора!' max_registration: '&cТи си достигнал максималният брой регистрации (%reg_count/%max_acc %reg_names)!' logged_in: '&cВече си вписан!' kick_for_vip: '&3VIP потребител влезе докато сървъра беше пълен, ти беше изгонен!' @@ -133,4 +133,4 @@ time: hour: 'час' hours: 'часа' day: 'ден' - days: 'дена' \ No newline at end of file + days: 'дена' diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 6758dd1e4..05d8aa6f8 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -24,7 +24,7 @@ password: login: command_usage: '&cUse: /login ' wrong_password: '&cSenha incorreta!' - # TODO success: '&2Successful login!' + success: '&2Login realizado com sucesso!' login_request: '&cPor favor, faça o login com o comando "/login "' timeout_error: '&4Tempo limite de sessão excedido, você foi expulso do servidor, por favor, tente novamente!' @@ -35,7 +35,7 @@ error: unregistered_user: '&cEste usuário não está registrado!' not_logged_in: '&cVocê não está logado!' no_permission: '&4Você não tem permissão para executar esta ação!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Ocorreu um erro inesperado, por favor contacte um administrador!' max_registration: '&cVocê excedeu o número máximo de inscrições (%reg_count/%max_acc %reg_names) do seu IP!' logged_in: '&cVocê já está logado!' kick_for_vip: '&3Um jogador VIP juntou-se ao servidor enquanto ele estava cheio!' @@ -137,4 +137,4 @@ time: hour: 'hora' hours: 'horas' day: 'dia' - days: 'dias' \ No newline at end of file + days: 'dias' diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index f5916bf29..0b0e9e02a 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cPoužij: "/login TvojeHeslo".' wrong_password: '&cŠpatné heslo.' - # TODO success: '&2Successful login!' + success: '&cÚspěšně přihlášen!' login_request: '&cProsím přihlaš se pomocí "/login TvojeHeslo".' timeout_error: '&cČas pro přihlášení vypršel!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cUživatelské jméno není zaregistrováno.' not_logged_in: '&cNejsi přihlášený!' no_permission: '&cNa tento příkaz nemáš dostatečné pravomoce.' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&cVyskytla se chyba - kontaktujte prosím administrátora ...' max_registration: '&cPřekročil(a) jsi limit pro počet účtů (%reg_count/%max_acc %reg_names) z jedné IP adresy.' logged_in: '&cJsi již přihlášen!' kick_for_vip: '&cOmlouváme se, ale VIP hráč se připojil na plný server!' @@ -133,4 +133,4 @@ time: hour: 'hodiny' hours: 'hodin' day: 'dny' - days: 'dnu' \ No newline at end of file + days: 'dnu' diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 0a524f5c6..18693d476 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cBenutze: /login ' wrong_password: '&cFalsches Passwort' - # TODO success: '&2Successful login!' + success: '&2Successful login!' login_request: '&cBitte logge dich ein mit "/login "' timeout_error: '&4Zeitüberschreitung beim Login' @@ -31,7 +31,7 @@ error: unregistered_user: '&cBenutzername nicht registriert!' not_logged_in: '&cNicht eingeloggt!' no_permission: '&4Du hast keine Rechte, um diese Aktion auszuführen!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Ein Fehler ist aufgetreten. Bitte kontaktiere einen Administrator.' max_registration: '&cDu hast die maximale Anzahl an Accounts erreicht (%reg_count/%max_acc %reg_names).' logged_in: '&cBereits eingeloggt!' kick_for_vip: '&3Ein VIP-Spieler hat den vollen Server betreten!' @@ -133,4 +133,4 @@ time: hour: 'Stunde' hours: 'Stunden' day: 'Tag' - days: 'Tage' \ No newline at end of file + days: 'Tage' diff --git a/src/main/resources/messages/messages_eo.yml b/src/main/resources/messages/messages_eo.yml index 41a5739ac..f438b19da 100644 --- a/src/main/resources/messages/messages_eo.yml +++ b/src/main/resources/messages/messages_eo.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUzado: /login ' wrong_password: '&cErara pasvorto!' - # TODO success: '&2Successful login!' + success: '&2Sukcesa ensaluto!' login_request: '&cBonvolu ensaluti per la komando: /login ' timeout_error: '&4Salutnomo tempolimo superis, vi estis piedbatita el la servilo, bonvolu provi denove!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cTiu uzanto ne estas registrita!' not_logged_in: '&cVi ne estas ensalutita!' no_permission: '&4Vi ne havas la permeson por fari ĉi tiun funkcion!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Neatendita eraro, bonvolu kontakti administranto!' max_registration: 'Vi superis la maksimuman nombron de enregistroj (%reg_count/%max_acc %reg_names) pro via ligo!' logged_in: '&cVi jam estas ensalutinta!' kick_for_vip: '&3VIP ludanto aliĝis al la servilo kiam ĝi pleniĝis!' @@ -133,4 +133,4 @@ time: hour: 'horo' hours: 'horoj' day: 'tago' - days: 'tagoj' \ No newline at end of file + days: 'tagoj' diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index 7326ea977..9a87984d2 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -22,7 +22,7 @@ password: login: command_usage: '&cUso: /login contraseña' wrong_password: '&cContraseña incorrecta' - # TODO success: '&2Successful login!' + success: '&c¡Sesión iniciada!' login_request: '&cInicia sesión con "/login contraseña"' timeout_error: '&fTiempo de espera para inicio de sesión excedido' @@ -33,7 +33,7 @@ error: unregistered_user: '&cUsuario no registrado' not_logged_in: '&c¡No has iniciado sesión!' no_permission: '&cNo tienes permiso' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&fHa ocurrido un error. Por favor contacta al administrador.' max_registration: '&fHas excedido la cantidad máxima de registros para tu cuenta' logged_in: '&c¡Ya has iniciado sesión!' kick_for_vip: '&c¡Un jugador VIP ha ingresado al servidor lleno!' @@ -135,4 +135,4 @@ time: hour: 'hora' hours: 'horas' day: 'día' - days: 'días' \ No newline at end of file + days: 'días' diff --git a/src/main/resources/messages/messages_et.yml b/src/main/resources/messages/messages_et.yml index 2de633a7f..3696593c1 100644 --- a/src/main/resources/messages/messages_et.yml +++ b/src/main/resources/messages/messages_et.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cKasutus: /login ' wrong_password: '&cVale salasõna!' - # TODO success: '&2Successful login!' + success: '&2Edukalt sisselogitud!' login_request: '&cPalun logi sisse kasutades käsklust: /login ' timeout_error: '&4Sisselogimise taimer täis, palun logi sisse ja proovi uuesti!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cSee kasutaja ei ole registreeritud.' not_logged_in: '&cSa ei ole sisse loginud!' no_permission: '&4Sul ei ole selle käskluse kasutamiseks õigust.' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Süsteemi viga, teata sellest serveri meeskonda.' max_registration: '&cSu IP peal (%reg_count/%max_acc %reg_names) on liiga palju kontosid!' logged_in: '&cJuba sisselogitud!' kick_for_vip: '&3VIP kasutaja liitus serveriga, kui see oli täis. Sind visati välja, et talle ruumi teha.' @@ -133,4 +133,4 @@ time: hour: 'tund' hours: 'tundi' day: 'päev' - days: 'päeva' \ No newline at end of file + days: 'päeva' diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index 9d4ce4b7a..b7782ee3a 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cErabili: /login pasahitza' wrong_password: '&cPasahitz okerra' - # TODO success: '&2Successful login!' + success: '&cOngi etorri!' login_request: '&cMesedez erabili "/login pasahitza" saioa hasteko' timeout_error: '&fDenbora gehiegi egon zara saioa hasi gabe' @@ -31,7 +31,7 @@ error: unregistered_user: '&cErabiltzailea ez dago erregistratuta' not_logged_in: '&cSaioa hasi gabe!' no_permission: '&cBaimenik ez' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&fErrorea; Mesedez jarri kontaktuan administratzaile batekin' max_registration: '&fKontuko 2 erabiltzaile bakarrik izan ditzakezu' logged_in: '&cDagoeneko saioa hasita!' kick_for_vip: '&cVIP erabiltzaile bat sartu da zerbitzaria beteta zegoenean!' @@ -133,4 +133,4 @@ time: # TODO hour: 'hour' # TODO hours: 'hours' # TODO day: 'day' - # TODO days: 'days' \ No newline at end of file + # TODO days: 'days' diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index 01b9906c8..5e88b4c1e 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cKäyttötapa: /login salasana' wrong_password: '&cVäärä salasana' - # TODO success: '&2Successful login!' + success: '&cKirjauduit onnistuneesti' login_request: '&cKirjaudu palvelimmelle komennolla "/login salasana"' timeout_error: '&fKirjautumisaika meni umpeen.' @@ -31,7 +31,7 @@ error: unregistered_user: '&cSalasanat eivät täsmää' not_logged_in: '&cEt ole kirjautunut sisään!' no_permission: '&cEi oikeuksia' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&fVirhe: Ota yhteys palveluntarjoojaan!' max_registration: '&fSinulla ei ole oikeuksia tehdä enempää pelaajatilejä!' logged_in: '&cOlet jo kirjautunut!' kick_for_vip: '&cVIP pelaaja liittyi täyteen palvelimeen!' @@ -133,4 +133,4 @@ time: # TODO hour: 'hour' # TODO hours: 'hours' # TODO day: 'day' - # TODO days: 'days' \ No newline at end of file + # TODO days: 'days' diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index c34911249..47e01f2db 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -25,7 +25,7 @@ password: login: command_usage: '&cUsage: /login ' wrong_password: '&cMauvais mot de passe !' - # TODO success: '&2Successful login!' + success: '&aConnexion effectuée !' login_request: '&cPour vous connecter, utilisez "/login "' timeout_error: 'Vous avez été expulsé car vous êtes trop lent pour vous enregistrer/connecter !' @@ -36,7 +36,7 @@ error: unregistered_user: '&cUtilisateur non-inscrit.' not_logged_in: '&cUtilisateur non connecté !' no_permission: '&cVous n''êtes pas autorisé à utiliser cette commande.' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&cUne erreur est apparue, veuillez contacter un administrateur.' max_registration: 'Vous avez atteint la limite d''inscription !%nl%&cVous avez %reg_count sur %max_acc : %reg_names' logged_in: '&aVous êtes déjà connecté.' kick_for_vip: 'Un joueur VIP a rejoint le serveur à votre place (serveur plein).' @@ -138,4 +138,4 @@ time: hour: 'heure' hours: 'heures' day: 'jour' - days: 'jours' \ No newline at end of file + days: 'jours' diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index c8ebe4ab3..11210f4e9 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUso: /login ' wrong_password: '&cContrasinal equivocado' - # TODO success: '&2Successful login!' + success: '&cIdentificación con éxito!' login_request: '&cPor favor, identifícate con "/login "' timeout_error: '&fRematou o tempo da autentificación' @@ -31,7 +31,7 @@ error: unregistered_user: '&cEse nome de usuario non está rexistrado' not_logged_in: '&cNon te identificaches!' no_permission: '&cNon tes o permiso' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&fOcurriu un erro; contacta cun administrador' max_registration: '&fExcediches o máximo de rexistros para a túa Conta' logged_in: '&cXa estás identificado!' kick_for_vip: '&cUn xogador VIP uniuse ao servidor cheo!' @@ -133,4 +133,4 @@ time: # TODO hour: 'hour' # TODO hours: 'hours' # TODO day: 'day' - # TODO days: 'days' \ No newline at end of file + # TODO days: 'days' diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index 773a9d7ed..ea2837122 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cBejelentkezés: "&7/login &c".' wrong_password: '&4A jelszó helytelen!' - # TODO success: '&2Successful login!' + success: '&aSikeresen beléptél!' login_request: '&cKérlek, jelentkezz be: "&7/login &c"!' timeout_error: 'Bejelentkezési időtúllépés!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cEz a felhasználó nincs regisztrálva!' not_logged_in: '&cNem vagy bejelentkezve!' no_permission: '&cNincs jogosultságod a használatára!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&cHiba lépett fel! Lépj kapcsolatba a szerver tulajával!' max_registration: '&cElérted a maximálisan beregisztrálható karakterek számát. (%reg_count/%max_acc %reg_names)' logged_in: '&cMár be vagy jelentkezve!' kick_for_vip: '&3VIP játékos csatlakozott a szerverhez!' @@ -133,4 +133,4 @@ time: hour: 'óra' hours: 'óra' day: 'nap' - days: 'nap' \ No newline at end of file + days: 'nap' diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 8afc1c665..00899b1e3 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUsage: /login ' wrong_password: '&cPassword salah!' - # TODO success: '&2Successful login!' + success: '&2Login berhasil!' login_request: '&cSilahkan login menggunakan command "/login "' timeout_error: '&4Jangka waktu login telah habis, kamu di keluarkan dari server. Silahkan coba lagi!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cUser ini belum terdaftar!' not_logged_in: '&cKamu belum login!' no_permission: '&4Kamu tidak mempunyai izin melakukan ini!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Terjadi kesalahan tak dikenal, silahkan hubungi Administrator!' max_registration: '&Kamu telah mencapai batas maksimum pendaftaran di server ini!' logged_in: '&cKamu telah login!' kick_for_vip: '&3Player VIP mencoba masuk pada saat server sedang penuh!' @@ -133,4 +133,4 @@ time: # TODO hour: 'hour' # TODO hours: 'hours' # TODO day: 'day' - # TODO days: 'days' \ No newline at end of file + # TODO days: 'days' diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index 3aa9fdb6a..570971f87 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -22,7 +22,7 @@ password: login: command_usage: '&cUtilizzo: /login ' wrong_password: '&cPassword non corretta!' - # TODO success: '&2Successful login!' + success: '&2Autenticazione eseguita correttamente!' login_request: '&cPer favore, esegui l''autenticazione con il comando: /login ' timeout_error: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!' @@ -33,7 +33,7 @@ error: unregistered_user: '&cL''utente non ha ancora eseguito la registrazione.' not_logged_in: '&cNon hai ancora eseguito l''autenticazione!' no_permission: '&4Non hai il permesso di eseguire questa operazione.' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Qualcosa è andato storto, riporta questo errore ad un amministratore!' max_registration: '&cHai raggiunto il numero massimo di registrazioni (%reg_count/%max_acc %reg_names) per questo indirizzo IP!' logged_in: '&cHai già eseguito l''autenticazione, non è necessario eseguirla nuovamente!' kick_for_vip: '&3Un utente VIP è entrato mentre il server era pieno e ha preso il tuo posto!' @@ -135,4 +135,4 @@ time: hour: 'ora' hours: 'ore' day: 'giorno' - days: 'giorni' \ No newline at end of file + days: 'giorni' diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index 488f336f3..44fec359d 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -23,7 +23,7 @@ password: login: command_usage: '&c사용법: /login <비밀번호>' wrong_password: '&c비밀번호가 잘못되었습니다!' - # TODO success: '&2Successful login!' + success: '&2로그인 되었습니다!' login_request: '&c다음 명령어로 로그인 해주세요: /login <비밀번호>' timeout_error: '&4로그인 시간이 초과 되어 서버에서 추방당했습니다. 다시 시도하세요!' @@ -34,7 +34,7 @@ error: unregistered_user: '&c이 유저는 등록되지 않았습니다!' not_logged_in: '&c로그인이 되어있지 않습니다!' no_permission: '&4이 작업을 수행할 수 있는 권한이 없습니다!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4예기치 않은 오류가 발생했습니다, 관리자에게 알려주세요!' max_registration: '&c당신은 가입할 수 있는 계정 한도를 초과했습니다 (%reg_count/%max_acc %reg_names)!' logged_in: '&c이미 로그인되어 있습니다!' kick_for_vip: '&3서버가 꽉 차있을땐 VIP 플레이어만 접속이 가능합니다!' @@ -136,4 +136,4 @@ time: hour: '시간' hours: '시간' day: '일' - days: '일' \ No newline at end of file + days: '일' diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index 58d59fa9d..091903d88 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&eKomandos panaudojimas: /login slaptazodis' wrong_password: '&cNeteisingas slaptazosdis' - # TODO success: '&2Successful login!' + success: '&aSekmingai prisijungete' login_request: '&ePrasome prisijungti: /login slaptazodis' timeout_error: '&cNespejote prisijungti' @@ -31,7 +31,7 @@ error: unregistered_user: '&cVartotojas neprisiregistraves' not_logged_in: '&cTu neprisijunges!' no_permission: '&cNera leidimo' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&cAtsirado klaida, praneskite adminstratoriui.' max_registration: '&cJus pasiekete maksimalu registraciju skaiciu.' logged_in: '&cTu aju prisijunges!' kick_for_vip: '&cA VIP prisijunge i pilna serveri!' @@ -133,4 +133,4 @@ time: # TODO hour: 'hour' # TODO hours: 'hours' # TODO day: 'day' - # TODO days: 'days' \ No newline at end of file + # TODO days: 'days' diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index 0f8b8ba57..634c92b1e 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cGebruik: /login ' wrong_password: '&cFout wachtwoord' - # TODO success: '&2Successful login!' + success: '&cSuccesvol ingelogd!' login_request: '&cLog in met: /login ' timeout_error: 'Login time-out: het duurde te lang tot je inlogde.' @@ -31,7 +31,7 @@ error: unregistered_user: '&cDeze gebruikersnaam is niet geregistreerd!' not_logged_in: '&cNiet ingelogd!' no_permission: '&cJe hebt geen rechten om deze actie uit te voeren!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: 'Er is een onverwachte fout opgetreden, neem contact op met een administrator!' max_registration: 'Je hebt het maximum aantal registraties overschreden (%reg_count/%max_acc: %reg_names).' logged_in: '&cJe bent al ingelogd!' kick_for_vip: '&cEen VIP-gebruiker heeft ingelogd toen de server vol was!' @@ -133,4 +133,4 @@ time: hour: 'uur' hours: 'uren' day: 'dag' - days: 'dagen' \ No newline at end of file + days: 'dagen' diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 44c0676a3..b5050fc87 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUżycie: /login hasło' wrong_password: '&cNiepoprawne hasło.' - # TODO success: '&2Successful login!' + success: '&aHasło zaakceptowane!' login_request: '&2Proszę się zalogować przy użyciu &6/login ' timeout_error: '&cUpłynął limit czasu zalogowania' @@ -31,7 +31,7 @@ error: unregistered_user: '&fGracz nie jest zarejestrowany.' not_logged_in: '&4Nie jesteś zalogowany!' no_permission: '&4Nie posiadasz wymaganych uprawnień.' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&fWystąpił błąd, prosimy skontaktować się z administracją serwera.' max_registration: '&cPrzekroczyłeś limit zarejestrowanych kont na serwerze &8(&e%reg_count/%max_acc %reg_names&8) &cdla twojego połączenia.' logged_in: '&fJesteś już zalogowany!' kick_for_vip: '&cGracz VIP dołączył do gry!' @@ -133,4 +133,4 @@ time: hour: 'godziny' hours: 'godzin' day: 'dzień' - days: 'dni' \ No newline at end of file + days: 'dni' diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index 33badd111..b0f707e28 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cUse: /login ' wrong_password: '&cPassword errada!' - # TODO success: '&2Successful login!' + success: '&bAutenticado com sucesso!' login_request: '&cIdentifique-se com "/login "' timeout_error: '&fExcedeu o tempo para autenticação' @@ -31,7 +31,7 @@ error: unregistered_user: '&cUsername não registado' not_logged_in: '&cNão autenticado!' no_permission: '&cSem Permissões' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&fOcorreu um erro; Por favor contacte um administrador' max_registration: '&cAtingiu o numero máximo de %reg_count contas registas, maximo de contas %max_acc' logged_in: '&cJá se encontra autenticado!' kick_for_vip: '&cUm jogador VIP entrou no servidor cheio!' @@ -133,4 +133,4 @@ time: hour: 'hora' hours: 'horas' day: 'dia' - days: 'dias' \ No newline at end of file + days: 'dias' diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml index 11ef01a50..874e70ee3 100644 --- a/src/main/resources/messages/messages_ro.yml +++ b/src/main/resources/messages/messages_ro.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cFoloseste comanda "/login " pentru a te autentifica.' wrong_password: '&cParola gresita!' - # TODO success: '&2Successful login!' + success: '&2Te-ai autentificat cu succes!' login_request: '&cTe rugam sa te autentifici folosind comanda "/login "' timeout_error: '&4A expirat timpul de autentificare si ai fost dat afara de server, te rugam incearca din nou!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cAcest jucator nu este inregistrat!' not_logged_in: '&cNu te-ai autentificat!' no_permission: '&4Nu ai permisiunea!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4A aparut o eroare, te rugam contacteaza un administrator!' max_registration: '&cTe-ai inregistrat cu prea multe conturi pe acelasi ip! (%reg_count/%max_acc %reg_names)' logged_in: '&cEsti deja autentificat!' kick_for_vip: '&3Un V.I.P a intrat pe server cand era plin!' @@ -133,4 +133,4 @@ time: hour: 'ora' hours: 'ore' day: 'zi' - days: 'zile' \ No newline at end of file + days: 'zile' diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 594292b98..de31b258f 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cИспользование: /login <пароль>' wrong_password: '&cНеправильный пароль!' - # TODO success: '&2Successful login!' + success: '&2Вы успешно вошли!' login_request: '&3Авторизация: /login <Пароль>' timeout_error: '&4Время авторизации истекло.' @@ -31,7 +31,7 @@ error: unregistered_user: '&cИгрок с таким именем не зарегистрирован.' not_logged_in: '&cВы ещё не вошли!' no_permission: '&4Недостаточно прав.' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&cПроизошла ошибка. Свяжитесь с администратором.' max_registration: '&cПревышено максимальное количество регистраций на сервере! (%reg_count/%max_acc %reg_names)' logged_in: '&cВы уже авторизированы!' kick_for_vip: '&3VIP-игрок зашёл на переполненный сервер.' @@ -133,4 +133,4 @@ time: hour: 'ч.' hours: 'ч.' day: 'дн.' - days: 'дн.' \ No newline at end of file + days: 'дн.' diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index 204489f4d..deb03a8f7 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -27,7 +27,7 @@ password: login: command_usage: '&cPoužitie: /login ' wrong_password: '&cZadal si zlé heslo.' - # TODO success: '&2Successful login!' + success: '&cBol si úspešne prihlásený!' login_request: '&cPrihlás sa príkazom "/login ".' timeout_error: '&fVypršal čas na prihlásenie, pripoj sa a skús to znovu.' @@ -38,7 +38,7 @@ error: unregistered_user: '&cZadané meno nie je zaregistrované!' not_logged_in: '&cNie si ešte prihlásený!' no_permission: '&cNemáš dostatočné práva na vykonanie tejto činnosti.' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&fNastala chyba, prosím kontaktuj Administrátora.' max_registration: '&fPrekročil si maximum registrovaných účtov(%reg_count/%max_acc|%reg_names).' logged_in: '&cAktuálne si už prihlásený!' kick_for_vip: '&3Uvoľnil si miesto pre VIP hráča!' @@ -140,4 +140,4 @@ time: hour: 'hod.' hours: 'hod.' day: 'd.' - days: 'd.' \ No newline at end of file + days: 'd.' diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index 13dc08dc8..94ac23086 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cKullanim: /login ' wrong_password: '&cYanlis sifre!' - # TODO success: '&2Successful login!' + success: '&2Giris basarili!' login_request: '&cLutfen giris komutunu kullanin "/login "' timeout_error: '&4Giris izni icin verilen zaman suresini astigin icin sunucudan atildin, tekrar deneyin!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cBu oyuncu kayitli degil!' not_logged_in: '&cGiris yapmadin!' no_permission: '&4Bunu yapmak icin iznin yok!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Beklenmedik bir hata olustu, yetkili ile iletisime gecin!' max_registration: '&cSen maksimum kayit sinirini astin (%reg_count/%max_acc %reg_names)!' logged_in: '&cZaten giris yaptin!' kick_for_vip: '&3Bir VIP oyuna giris yaptigi icin atildin!' @@ -133,4 +133,4 @@ time: hour: 'saat' hours: 'saat' day: 'gun' - days: 'gun' \ No newline at end of file + days: 'gun' diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index 20f2a27dd..d5f448fb9 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cСинтаксис: /login <пароль>' wrong_password: '&cНевірний пароль!' - # TODO success: '&2Successful login!' + success: '&2Успішна авторизація!' login_request: '&cДля авторизації, введіть команду "/login <пароль>"' timeout_error: '&4Час для авторизації сплинув. Будь ласка, спробуйте ще раз!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cЦей гравець не є зареєстрованим.' not_logged_in: '&cВи не авторизовані!' no_permission: '&4У вас недостатньо прав, щоб застосувати цю команду!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4[AuthMe] Error. Будь ласка, повідомте адміністратора!' max_registration: '&cВичерпано ліміт реєстрацій (%reg_count/%max_acc %reg_names) для вашого підключення!' logged_in: '&cВи вже авторизовані!' kick_for_vip: '&3Вас кікнуто, внаслідок того, що VIP гравець зайшов на сервер коли небуло вільних місць.' @@ -133,4 +133,4 @@ time: # TODO hour: 'hour' # TODO hours: 'hours' # TODO day: 'day' - # TODO days: 'days' \ No newline at end of file + # TODO days: 'days' diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index a62770045..03190e9bb 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&cSử dụng: /login ' wrong_password: '&cSai mật khẩu!' - # TODO success: '&2Successful login!' + success: '&2Đăng nhập thành công!' login_request: '&cXin vui lòng đăng nhập bằng lệnh "/login "' timeout_error: '&4Thời gian đăng nhập đã hết, bạn đã bị văng khỏi máy chủ. Xin vui lòng thử lại!' @@ -31,7 +31,7 @@ error: unregistered_user: '&cNgười dùng này đã được đăng ký!' not_logged_in: '&cBạn chưa đăng nhập!' no_permission: '&4Bạn không có quyền truy cập lệnh này!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4Lỗi! Vui lòng liên hệ quản trị viên hoặc admin' max_registration: '&cBạn đã vượt quá giới hạn tối đa đăng ký tài khoản (%reg_count/%max_acc %reg_names) cho những lần kết nối tài khoản!' logged_in: '&cBạn đã đăng nhập!' kick_for_vip: '&eChỉ có thành viên VIP mới được tham gia khi máy chủ đầy!' @@ -133,4 +133,4 @@ time: hour: 'giờ' hours: 'giờ' day: 'ngày' - days: 'ngày' \ No newline at end of file + days: 'ngày' diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index 19d436581..c4032e30a 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&8[&6玩家系统&8] &c正确用法:“/login <密码>”' wrong_password: '&8[&6玩家系统&8] &c错误的密码' - # TODO success: '&2Successful login!' + success: '&8[&6玩家系统&8] &c已成功登录!' login_request: '&8[&6玩家系统&8] &c请输入“/login <密码>”以登录' timeout_error: '&8[&6玩家系统&8] &f给你登录的时间已经过了' @@ -31,7 +31,7 @@ error: unregistered_user: '&8[&6玩家系统&8] &c此用户名还未注册过' not_logged_in: '&8[&6玩家系统&8] &c你还未登录!' no_permission: '&8[&6玩家系统&8] &c没有权限' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&8[&6玩家系统&8] &f发现错误,请联系管理员' max_registration: '&8[&6玩家系统&8] &f你不允许再为你的IP在服务器注册更多用户了!' logged_in: '&8[&6玩家系统&8] &c你已经登陆过了!' kick_for_vip: '&8[&6玩家系统&8] &cA VIP玩家加入了已满的服务器!' @@ -133,4 +133,4 @@ time: hour: '小时' hours: '小时' day: '天' - days: '天' \ No newline at end of file + days: '天' diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index 3622458c1..002a52d5f 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -24,7 +24,7 @@ password: login: command_usage: '&8[&6用戶系統&8] &f用法:《 /login <密碼> 》' wrong_password: '&8[&6用戶系統&8] &c你輸入了錯誤的密碼。' - # TODO success: '&2Successful login!' + success: '&8[&6用戶系統&8] &a你成功登入了。' login_request: '&8[&6用戶系統&8] &c請使用這個指令來登入:《 /login <密碼> 》' timeout_error: '&8[&6用戶系統&8] &f登入逾時。' @@ -35,7 +35,7 @@ error: unregistered_user: '&8[&6用戶系統&8] &c此用戶名沒有已登記資料。' not_logged_in: '&8[&6用戶系統&8] &c你還沒有登入 !' no_permission: '&8[&6用戶系統&8] &b嗯~你想幹甚麼?' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&8[&6用戶系統&8] &f發生錯誤,請與管理員聯絡。' max_registration: '&8[&6用戶系統&8] &f你的IP地址已達到註冊數上限。 &7(info: %reg_count/%max_acc %reg_names)' logged_in: '&8[&6用戶系統&8] &c你已經登入過了。' kick_for_vip: '&c喔!因為有VIP玩家登入了伺服器。' @@ -137,4 +137,4 @@ time: hour: '小時' hours: '小時' day: '日' - days: '日' \ No newline at end of file + days: '日' diff --git a/src/main/resources/messages/messages_zhmc.yml b/src/main/resources/messages/messages_zhmc.yml index b8508b99a..6d811c4eb 100644 --- a/src/main/resources/messages/messages_zhmc.yml +++ b/src/main/resources/messages/messages_zhmc.yml @@ -20,7 +20,7 @@ password: login: command_usage: '&b使用方法: 輸入"/login [你的密碼]" 來登入' wrong_password: '&c密碼錯誤!' - # TODO success: '&2Successful login!' + success: '&2你已成功登入!' login_request: '&c [請先登入] 請按T , 然後輸入 "/login [你的密碼]" 。' timeout_error: '&4超過登錄超時,您已從伺服器中踢出,請重試!' @@ -31,7 +31,7 @@ error: unregistered_user: '&c此用戶尚未注冊!' not_logged_in: '&c你尚未登入!' no_permission: '&4您沒有執行此操作的權限!' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&4發生錯誤!請聯繫伺服器管理員!' max_registration: '&c您已超過註冊的最大數量(%reg_count/%max_acc %reg_names)!' logged_in: '&c您已經登錄!' kick_for_vip: '&3一名VIP玩家在服務器已滿時已加入伺服器!' @@ -133,4 +133,4 @@ time: # TODO hour: 'hour' # TODO hours: 'hours' # TODO day: 'day' - # TODO days: 'days' \ No newline at end of file + # TODO days: 'days' diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index 556021dda..2cfff1379 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -23,7 +23,7 @@ password: login: command_usage: '&b【AuthMe】&6用法: &c"/login <密碼>"' wrong_password: '&b【AuthMe】&6密碼錯誤!' - # TODO success: '&2Successful login!' + success: '&b【AuthMe】&6密碼正確,您已成功登入!' login_request: '&b【AuthMe】&6請使用 &c"/login <密碼>" &6來登入。' timeout_error: '&b【AuthMe】&6超過登入時間,請稍後再試一次' @@ -34,7 +34,7 @@ error: unregistered_user: '&b【AuthMe】&6這個帳號還沒有註冊過' not_logged_in: '&b【AuthMe】&6您還沒有登入!' no_permission: '&b【AuthMe】&6您沒有使用該指令的權限。' - # TODO unexpected_error: '&4An unexpected error occurred, please contact an administrator!' + unexpected_error: '&b【AuthMe】&6發生錯誤,請聯繫管理員' max_registration: '&b【AuthMe】&6您的 IP 位置所註冊的帳號數量已經達到最大。' logged_in: '&b【AuthMe】&6您已經登入了!' kick_for_vip: '&b【AuthMe】&6您已經被請出。&c原因 : 有 VIP 玩家登入伺服器' @@ -136,4 +136,4 @@ time: hour: '時' hours: '時' day: '天' - days: '天' \ No newline at end of file + days: '天' diff --git a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java index c75be723e..83f5a5c63 100644 --- a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java +++ b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java @@ -108,7 +108,7 @@ public class MessageUpdaterTest { .collect(Collectors.toSet()); // when - Set messageKeysFromConfigData = MessageUpdater.CONFIGURATION_DATA.getProperties().stream() + Set messageKeysFromConfigData = MessageUpdater.getConfigurationData().getProperties().stream() .map(Property::getPath) .collect(Collectors.toSet()); @@ -125,7 +125,7 @@ public class MessageUpdaterTest { // when Map comments = ReflectionTestUtils.getFieldValue( - ConfigurationData.class, MessageUpdater.CONFIGURATION_DATA, "sectionComments"); + ConfigurationData.class, MessageUpdater.getConfigurationData(), "sectionComments"); // then assertThat(comments.keySet(), equalTo(rootPaths)); diff --git a/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java b/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java new file mode 100644 index 000000000..be10559ef --- /dev/null +++ b/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java @@ -0,0 +1,41 @@ +package fr.xephi.authme.message.updater; + +import fr.xephi.authme.message.MessageKey; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.Assert.fail; + +/** + * Test for {@link OldMessageKeysMigrater}. + */ +public class OldMessageKeysMigraterTest { + + @Test + public void shouldHasOldKeysThatAreNewParentsFirstInMap() { + // given + Set parentPaths = collectParentPathsFromMessageKeys(); + Set encounteredParents = new HashSet<>(); + + // when / then + for (Map.Entry entry : OldMessageKeysMigrater.KEYS_TO_OLD_PATH.entrySet()) { + if (parentPaths.contains(entry.getValue()) && encounteredParents.contains(entry.getValue())) { + fail("Entry migrating old path '" + entry.getValue() + + "' should come before any new paths with it as parent"); + } + String parent = entry.getKey().getKey().split("\\.")[0]; + encounteredParents.add(parent); + } + } + + private Set collectParentPathsFromMessageKeys() { + return Arrays.stream(MessageKey.values()) + .map(mk -> mk.getKey().split("\\.")[0]) + .collect(Collectors.toSet()); + } +} diff --git a/src/test/java/tools/messages/MessagesFileWriter.java b/src/test/java/tools/messages/MessagesFileWriter.java index e47d10a05..2af69e2cb 100644 --- a/src/test/java/tools/messages/MessagesFileWriter.java +++ b/src/test/java/tools/messages/MessagesFileWriter.java @@ -25,7 +25,7 @@ import java.util.regex.Pattern; * were at the top of the file and to separate comments by new lines (which ConfigMe * currently doesn't support). */ -public class MessagesFileWriter { +public final class MessagesFileWriter { /** Marker used inside a text to signal that it should be a comment later on. */ private static final String COMMENT_MARKER = "::COMMENT::"; @@ -52,7 +52,7 @@ public class MessagesFileWriter { // Create property resource with new defaults, save with ConfigMe for proper sections & comments PropertyResource resource = createPropertyResourceWithCommentEntries(); - new SettingsManager(resource, null, MessageUpdater.CONFIGURATION_DATA).save(); + new SettingsManager(resource, null, MessageUpdater.getConfigurationData()).save(); // Go through the newly saved file and replace texts with comment marker to actual YAML comments // and add initial comments back to the file @@ -89,7 +89,7 @@ public class MessagesFileWriter { * @return the first comment generated by ConfigMe (comment of the first root path) */ private static String getFirstCommentByConfigMe() { - ConfigurationData configurationData = MessageUpdater.CONFIGURATION_DATA; + ConfigurationData configurationData = MessageUpdater.getConfigurationData(); String firstRootPath = configurationData.getProperties().get(0).getPath().split("\\.")[0]; return "# " + configurationData.getCommentsForSection(firstRootPath)[0]; } @@ -100,7 +100,7 @@ public class MessagesFileWriter { */ private PropertyResource createPropertyResourceWithCommentEntries() { YamlFileResource resource = new MigraterYamlFileResource(file); - for (Property property : MessageUpdater.CONFIGURATION_DATA.getProperties()) { + for (Property property : MessageUpdater.getConfigurationData().getProperties()) { String text = resource.getString(property.getPath()); if (text == null) { resource.setValue(property.getPath(), COMMENT_MARKER + defaultFile.getString(property.getPath())); @@ -132,6 +132,7 @@ public class MessagesFileWriter { newLines.add(line); } } + newLines.add(""); // Makes sure file ends with new line FileIoUtils.writeToFile(file.toPath(), String.join("\n", newLines)); } From cd61febd76e4882755981804b5c1cb0fdb4f8092 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 2 Feb 2018 20:12:42 +0100 Subject: [PATCH 025/155] #1467 Change /authme messages to only update help text file now --- docs/commands.md | 6 +++--- docs/translations.md | 12 ++++++------ .../fr/xephi/authme/command/CommandInitializer.java | 9 ++++----- ...esCommand.java => UpdateHelpMessagesCommand.java} | 10 ++-------- 4 files changed, 15 insertions(+), 22 deletions(-) rename src/main/java/fr/xephi/authme/command/executable/authme/{MessagesCommand.java => UpdateHelpMessagesCommand.java} (76%) diff --git a/docs/commands.md b/docs/commands.md index cf544f09c..5bb49c2b9 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,5 +1,5 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` @@ -49,7 +49,7 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). - **/authme version**: Show detailed information about the installed AuthMeReloaded version, the developers, contributors, and license. - **/authme converter** [job]: Converter command for AuthMeReloaded.
Requires `authme.admin.converter` -- **/authme messages** [help]: Adds missing messages to the current messages file. +- **/authme messages**: Adds missing texts to the current help messages file.
Requires `authme.admin.updatemessages` - **/authme recent**: Shows the last players that have logged in.
Requires `authme.admin.seerecent` @@ -95,4 +95,4 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Dec 01 19:16:15 CET 2017 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Feb 02 20:09:14 CET 2018 diff --git a/docs/translations.md b/docs/translations.md index 38cf95e8e..2f778c291 100644 --- a/docs/translations.md +++ b/docs/translations.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code @@ -13,7 +13,7 @@ Code | Language | Translated |   [cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 90% | bar [de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 90% | bar [eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 90% | bar -[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 98% | bar +[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 100% | bar [et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 90% | bar [eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 48% | bar [fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 51% | bar @@ -21,11 +21,11 @@ Code | Language | Translated |   [gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 54% | bar [hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 98% | bar [id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 53% | bar -[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 98% | bar +[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | bar [ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 98% | bar [lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 40% | bar [nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 90% | bar -[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 98% | bar +[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | bar [pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 90% | bar [ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 90% | bar [ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 98% | bar @@ -33,7 +33,7 @@ Code | Language | Translated |   [tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 86% | bar [uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 71% | bar [vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 87% | bar -[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 98% | bar +[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 100% | bar [zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 90% | bar [zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 73% | bar [zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 98% | bar @@ -41,4 +41,4 @@ Code | Language | Translated |   --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Jan 14 11:14:59 CET 2018 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Feb 02 20:09:17 CET 2018 diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index 2cd0fc600..dc5f740ff 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -12,7 +12,6 @@ import fr.xephi.authme.command.executable.authme.ForceLoginCommand; import fr.xephi.authme.command.executable.authme.GetEmailCommand; import fr.xephi.authme.command.executable.authme.GetIpCommand; import fr.xephi.authme.command.executable.authme.LastLoginCommand; -import fr.xephi.authme.command.executable.authme.MessagesCommand; import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand; import fr.xephi.authme.command.executable.authme.PurgeCommand; import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand; @@ -26,6 +25,7 @@ import fr.xephi.authme.command.executable.authme.SetSpawnCommand; import fr.xephi.authme.command.executable.authme.SpawnCommand; import fr.xephi.authme.command.executable.authme.SwitchAntiBotCommand; import fr.xephi.authme.command.executable.authme.UnregisterAdminCommand; +import fr.xephi.authme.command.executable.authme.UpdateHelpMessagesCommand; import fr.xephi.authme.command.executable.authme.VersionCommand; import fr.xephi.authme.command.executable.authme.debug.DebugCommand; import fr.xephi.authme.command.executable.captcha.CaptchaCommand; @@ -427,11 +427,10 @@ public class CommandInitializer { CommandDescription.builder() .parent(authmeBase) .labels("messages", "msg") - .description("Add missing messages") - .detailedDescription("Adds missing messages to the current messages file.") - .withArgument("help", "Add 'help' to update the help messages file", true) + .description("Add missing help messages") + .detailedDescription("Adds missing texts to the current help messages file.") .permission(AdminPermission.UPDATE_MESSAGES) - .executableCommand(MessagesCommand.class) + .executableCommand(UpdateHelpMessagesCommand.class) .register(); CommandDescription.builder() diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java similarity index 76% rename from src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java rename to src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java index 6e55c4cbe..c737b98dd 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java @@ -11,10 +11,10 @@ import java.io.IOException; import java.util.List; /** - * Messages command, updates the user's messages file with any missing files + * Messages command, updates the user's help messages file with any missing files * from the provided file in the JAR. */ -public class MessagesCommand implements ExecutableCommand { +public class UpdateHelpMessagesCommand implements ExecutableCommand { @Inject private HelpTranslationGenerator helpTranslationGenerator; @@ -23,12 +23,6 @@ public class MessagesCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments) { - if (!arguments.isEmpty() && "help".equalsIgnoreCase(arguments.get(0))) { - updateHelpFile(sender); - } - } - - private void updateHelpFile(CommandSender sender) { try { helpTranslationGenerator.updateHelpFile(); sender.sendMessage("Successfully updated the help file"); From 6a5f335e16669a7314237a9fedb55c59e1f2b584 Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Sun, 4 Feb 2018 15:12:23 +0100 Subject: [PATCH 026/155] Implement #1483 --- .../fr/xephi/authme/settings/SettingsWarner.java | 11 +++++++++++ src/main/java/fr/xephi/authme/util/Utils.java | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/main/java/fr/xephi/authme/settings/SettingsWarner.java b/src/main/java/fr/xephi/authme/settings/SettingsWarner.java index 176dca9c9..ca049d56b 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsWarner.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsWarner.java @@ -5,9 +5,12 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.crypts.Argon2; import fr.xephi.authme.settings.properties.EmailSettings; +import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.util.Utils; +import org.bukkit.Bukkit; import javax.inject.Inject; @@ -50,6 +53,14 @@ public class SettingsWarner { ConsoleLogger.warning("Warning: Session timeout needs to be positive in order to work!"); } + // Warn if spigot.yml has settings.bungeecord set to true but config.yml has Hooks.bungeecord set to false + if (Utils.isSpigot() && Bukkit.spigot().getConfig().getBoolean("settings.bungeecord") + && !settings.getProperty(HooksSettings.BUNGEECORD)) { + ConsoleLogger.warning("Note: Hooks.bungeecord is set to false but your server appears to be running in" + + " bungeecord mode (see your spigot.yml). In order to allow the datasource caching and the AuthMeBungee" + + " add-on to work properly you have to enable this option!"); + } + // Check if argon2 library is present and can be loaded if (settings.getProperty(SecuritySettings.PASSWORD_HASH).equals(HashAlgorithm.ARGON2) && !Argon2.isLibraryLoaded()) { diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 4db7f61bc..8146f4e25 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -22,6 +22,20 @@ public final class Utils { private Utils() { } + /** + * Returns if the running server instance is craftbukkit or spigot based. + * + * @return true if the running server instance is spigot-based. + */ + public static boolean isSpigot() { + try { + Class.forName("org.spigotmc.SpigotConfig"); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } + /** * Compile Pattern sneaky without throwing Exception. * From 818630034966d4a1c4d9ae1a723d400467a487bd Mon Sep 17 00:00:00 2001 From: Adeuran Date: Thu, 8 Feb 2018 03:12:06 +0900 Subject: [PATCH 027/155] Update messages_ko.yml (#1499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update messages_ko.yml Additional Translate by Adeuran(아몬드노란맛 http://adeuran.tistory.com) * Some of Korean Translate to English 자동가입방지문자 -> CAPTCHA * Update messages_ko.yml --- src/main/resources/messages/messages_ko.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index 2bf52b109..a7a96df73 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -1,4 +1,4 @@ -#Translated by Kirito (kds123321@naver.com), System32(me@syst32.com) +#Translated by Kirito (kds123321@naver.com), System32(me@syst32.com), Adeuran(adeuran@tistory.com) #14.05.2017 Thanks for use # 회원가입 @@ -98,8 +98,8 @@ email_cooldown_error: '&c이메일을 이미 발송했습니다. %time 후에 usage_captcha: '&3로그인 하려면 CAPTCHA 코드를 입력해야 합니다. 이 명령어를 사용하세요: /captcha ' wrong_captcha: '&c잘못된 CAPTCHA 코드 입니다. "/captcha THE_CAPTCHA" 형태로 입력해주세요!' valid_captcha: '&2CAPTCHA 코드가 확인되었습니다!' -# TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha ' -# TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' +captcha_for_registration: '회원가입을 하기 위해서는 먼저 CAPTCHA 코드를 입력해야합니다. 이 명령어를 이 명령어를 사용하세요: /captcha ' +register_captcha_valid: '&2올바른 CAPTCHA 코드입니다! 이제 /register 명령어를 이용하여 회원가입할 수 있습니다.' # 인증 코드 verification_code_required: '&3이 명령어는 매우 민감하게 작동되며, 이메일 인증을 필요로 합니다. 이메일을 확인하고 지시에 따르십시오.' From 189647d9f2365c843fe5ea420e251b7c2002dc11 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 11 Feb 2018 09:22:42 +0100 Subject: [PATCH 028/155] #1467 Fix character issues by always using UTF-8 when reading and writing - Change usages of Bukkit's FileResource to a ConfigMe PropertyReader - Specify UTF-8 for reading and writing --- .../message/updater/JarMessageSource.java | 28 ++--- .../MessageMigraterPropertyReader.java | 47 +++++--- .../message/updater/MessageUpdater.java | 28 ----- .../updater/MigraterYamlFileResource.java | 103 ++++++++++++++++++ .../xephi/authme/ClassesConsistencyTest.java | 2 + .../updater/MigraterYamlFileResourceTest.java | 70 ++++++++++++ .../tools/messages/MessagesFileWriter.java | 2 +- .../fr/xephi/authme/message/chinese_texts.yml | 3 + 8 files changed, 224 insertions(+), 59 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java create mode 100644 src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java create mode 100644 src/test/resources/fr/xephi/authme/message/chinese_texts.yml diff --git a/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java b/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java index c4da59c75..34c95a6ce 100644 --- a/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java +++ b/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java @@ -1,14 +1,12 @@ package fr.xephi.authme.message.updater; import ch.jalu.configme.properties.Property; +import ch.jalu.configme.resource.PropertyReader; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.util.FileUtils; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; /** * Returns messages from the JAR's message files. Favors a local JAR (e.g. messages_nl.yml) @@ -16,8 +14,8 @@ import java.io.InputStreamReader; */ public class JarMessageSource { - private final FileConfiguration localJarConfiguration; - private final FileConfiguration defaultJarConfiguration; + private final PropertyReader localJarMessages; + private final PropertyReader defaultJarMessages; /** * Constructor. @@ -26,29 +24,31 @@ public class JarMessageSource { * @param defaultJarPath path to the default messages file in the JAR (must exist) */ public JarMessageSource(String localJarPath, String defaultJarPath) { - localJarConfiguration = localJarPath.equals(defaultJarPath) ? null : loadJarFile(localJarPath); - defaultJarConfiguration = loadJarFile(defaultJarPath); + localJarMessages = localJarPath.equals(defaultJarPath) ? null : loadJarFile(localJarPath); + defaultJarMessages = loadJarFile(defaultJarPath); - if (defaultJarConfiguration == null) { + if (defaultJarMessages == null) { throw new IllegalStateException("Default JAR file '" + defaultJarPath + "' could not be loaded"); } } public String getMessageFromJar(Property property) { String key = property.getPath(); - String message = localJarConfiguration == null ? null : localJarConfiguration.getString(key); - return message == null ? defaultJarConfiguration.getString(key) : message; + String message = getString(key, localJarMessages); + return message == null ? getString(key, defaultJarMessages) : message; } - private static YamlConfiguration loadJarFile(String jarPath) { + private static String getString(String path, PropertyReader reader) { + return reader == null ? null : reader.getTypedObject(path, String.class); + } + + private static MessageMigraterPropertyReader loadJarFile(String jarPath) { try (InputStream stream = FileUtils.getResourceFromJar(jarPath)) { if (stream == null) { ConsoleLogger.debug("Could not load '" + jarPath + "' from JAR"); return null; } - try (InputStreamReader isr = new InputStreamReader(stream)) { - return YamlConfiguration.loadConfiguration(isr); - } + return MessageMigraterPropertyReader.loadFromStream(stream); } catch (IOException e) { ConsoleLogger.logException("Exception while handling JAR path '" + jarPath + "'", e); } diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java b/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java index 400b25ff3..1174ba043 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java @@ -7,31 +7,44 @@ import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** - * Duplication of ConfigMe's {@link ch.jalu.configme.resource.YamlFileReader} with a character encoding - * fix in {@link #reload}. + * Implementation of {@link PropertyReader} which can read a file or a stream with + * a specified charset. */ public class MessageMigraterPropertyReader implements PropertyReader { - private final File file; + public static final Charset CHARSET = StandardCharsets.UTF_8; + private Map root; /** See same field in {@link ch.jalu.configme.resource.YamlFileReader} for details. */ private boolean hasObjectAsRoot = false; - /** - * Constructor. - * - * @param file the file to load - */ - public MessageMigraterPropertyReader(File file) { - this.file = file; - reload(); + private MessageMigraterPropertyReader(Map valuesMap) { + root = valuesMap; + } + + public static MessageMigraterPropertyReader loadFromFile(File file) { + Map valuesMap; + try (InputStream is = new FileInputStream(file)) { + valuesMap = readStreamToMap(is); + } catch (IOException e) { + throw new IllegalStateException("Error while reading file '" + file + "'", e); + } + + return new MessageMigraterPropertyReader(valuesMap); + } + + public static MessageMigraterPropertyReader loadFromStream(InputStream inputStream) { + Map valuesMap = readStreamToMap(inputStream); + return new MessageMigraterPropertyReader(valuesMap); } @Override @@ -110,15 +123,17 @@ public class MessageMigraterPropertyReader implements PropertyReader { @Override public void reload() { - try (FileInputStream fis = new FileInputStream(file); - InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) { + throw new UnsupportedOperationException("Reload not supported by this implementation"); + } + private static Map readStreamToMap(InputStream inputStream) { + try (InputStreamReader isr = new InputStreamReader(inputStream, CHARSET)) { Object obj = new Yaml().load(isr); - root = obj == null ? new HashMap<>() : (Map) obj; + return obj == null ? new HashMap<>() : (Map) obj; } catch (IOException e) { - throw new ConfigMeException("Could not read file '" + file + "'", e); + throw new ConfigMeException("Could not read stream", e); } catch (ClassCastException e) { - throw new ConfigMeException("Top-level is not a map in '" + file + "'", e); + throw new ConfigMeException("Top-level is not a map", e); } } diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index b4634a712..9e06bafff 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -1,7 +1,6 @@ package fr.xephi.authme.message.updater; import ch.jalu.configme.SettingsManager; -import ch.jalu.configme.beanmapper.leafproperties.LeafPropertiesGenerator; import ch.jalu.configme.configurationdata.ConfigurationData; import ch.jalu.configme.configurationdata.PropertyListBuilder; import ch.jalu.configme.properties.Property; @@ -10,8 +9,6 @@ import ch.jalu.configme.resource.YamlFileResource; import com.google.common.collect.ImmutableMap; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.message.MessageKey; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.Yaml; import java.io.File; import java.util.Arrays; @@ -134,29 +131,4 @@ public class MessageUpdater { return new ConfigurationData(builder.create(), comments); } - /** - * Extension of {@link YamlFileResource} to fine-tune the export style. - */ - public static final class MigraterYamlFileResource extends YamlFileResource { - - private Yaml singleQuoteYaml; - - public MigraterYamlFileResource(File file) { - super(file, new MessageMigraterPropertyReader(file), new LeafPropertiesGenerator()); - } - - @Override - protected Yaml getSingleQuoteYaml() { - if (singleQuoteYaml == null) { - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - options.setAllowUnicode(true); - options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED); - // Overridden setting: don't split lines - options.setSplitLines(false); - singleQuoteYaml = new Yaml(options); - } - return singleQuoteYaml; - } - } } diff --git a/src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java b/src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java new file mode 100644 index 000000000..69b580ae1 --- /dev/null +++ b/src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java @@ -0,0 +1,103 @@ +package fr.xephi.authme.message.updater; + +import ch.jalu.configme.beanmapper.leafproperties.LeafPropertiesGenerator; +import ch.jalu.configme.configurationdata.ConfigurationData; +import ch.jalu.configme.exception.ConfigMeException; +import ch.jalu.configme.properties.Property; +import ch.jalu.configme.resource.PropertyPathTraverser; +import ch.jalu.configme.resource.YamlFileResource; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.List; + +import static fr.xephi.authme.message.updater.MessageMigraterPropertyReader.CHARSET; + +/** + * Extension of {@link YamlFileResource} to fine-tune the export style + * and to be able to specify the character encoding. + */ +public class MigraterYamlFileResource extends YamlFileResource { + + private static final String INDENTATION = " "; + + private final File file; + private Yaml singleQuoteYaml; + + public MigraterYamlFileResource(File file) { + super(file, MessageMigraterPropertyReader.loadFromFile(file), new LeafPropertiesGenerator()); + this.file = file; + } + + @Override + protected Yaml getSingleQuoteYaml() { + if (singleQuoteYaml == null) { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setAllowUnicode(true); + options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED); + // Overridden setting: don't split lines + options.setSplitLines(false); + singleQuoteYaml = new Yaml(options); + } + return singleQuoteYaml; + } + + @Override + public void exportProperties(ConfigurationData configurationData) { + try (FileOutputStream fos = new FileOutputStream(file); + OutputStreamWriter writer = new OutputStreamWriter(fos, CHARSET)) { + PropertyPathTraverser pathTraverser = new PropertyPathTraverser(configurationData); + for (Property property : convertPropertiesToExportableTypes(configurationData.getProperties())) { + + List pathElements = pathTraverser.getPathElements(property); + for (PropertyPathTraverser.PathElement pathElement : pathElements) { + writeComments(writer, pathElement.indentationLevel, pathElement.comments); + writer.append("\n") + .append(indent(pathElement.indentationLevel)) + .append(pathElement.name) + .append(":"); + } + + writer.append(" ") + .append(toYaml(property, pathElements.get(pathElements.size() - 1).indentationLevel)); + } + writer.flush(); + writer.close(); + } catch (IOException e) { + throw new ConfigMeException("Could not save config to '" + file.getPath() + "'", e); + } finally { + singleQuoteYaml = null; + } + } + + private void writeComments(Writer writer, int indentation, String[] comments) throws IOException { + if (comments.length == 0) { + return; + } + String commentStart = "\n" + indent(indentation) + "# "; + for (String comment : comments) { + writer.append(commentStart).append(comment); + } + } + + private String toYaml(Property property, int indent) { + Object value = property.getValue(this); + String representation = transformValue(property, value); + String[] lines = representation.split("\\n"); + return String.join("\n" + indent(indent), lines); + } + + private static String indent(int level) { + String result = ""; + for (int i = 0; i < level; i++) { + result += INDENTATION; + } + return result; + } +} diff --git a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java index db74ce6f2..267f7d7cb 100644 --- a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java @@ -18,6 +18,7 @@ import org.junit.Test; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -49,6 +50,7 @@ public class ClassesConsistencyTest { private static final Set> IMMUTABLE_TYPES = ImmutableSet.of( /* JDK */ int.class, long.class, float.class, String.class, File.class, Enum.class, collectionsUnmodifiableList(), + Charset.class, /* AuthMe */ Property.class, RegistrationMethod.class, /* Guava */ diff --git a/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java b/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java new file mode 100644 index 000000000..f6e6dcd2d --- /dev/null +++ b/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java @@ -0,0 +1,70 @@ +package fr.xephi.authme.message.updater; + +import ch.jalu.configme.configurationdata.ConfigurationData; +import ch.jalu.configme.properties.Property; +import ch.jalu.configme.properties.StringProperty; +import com.google.common.io.Files; +import fr.xephi.authme.TestHelper; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link MigraterYamlFileResource}. + */ +public class MigraterYamlFileResourceTest { + + private static final String CHINESE_MESSAGES_FILE = TestHelper.PROJECT_ROOT + "message/chinese_texts.yml"; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void shouldReadChineseFile() { + // given + File file = TestHelper.getJarFile(CHINESE_MESSAGES_FILE); + + // when + MigraterYamlFileResource resource = new MigraterYamlFileResource(file); + + // then + assertThat(resource.getString("first"), equalTo("错误的密码")); + assertThat(resource.getString("second"), equalTo("为了验证您的身份,您需要将一个电子邮件地址与您的帐户绑定!")); + assertThat(resource.getString("third"), equalTo("您已经可以在当前会话中执行任何敏感命令!")); + } + + @Test + public void shouldWriteWithCorrectCharset() throws IOException { + // given + File file = temporaryFolder.newFile(); + Files.copy(TestHelper.getJarFile(CHINESE_MESSAGES_FILE), file); + MigraterYamlFileResource resource = new MigraterYamlFileResource(file); + String newMessage = "您当前并没有任何邮箱与该账号绑定"; + resource.setValue("third", newMessage); + + // when + resource.exportProperties(buildConfigurationData()); + + // then + resource = new MigraterYamlFileResource(file); + assertThat(resource.getString("first"), equalTo("错误的密码")); + assertThat(resource.getString("second"), equalTo("为了验证您的身份,您需要将一个电子邮件地址与您的帐户绑定!")); + assertThat(resource.getString("third"), equalTo(newMessage)); + } + + private static ConfigurationData buildConfigurationData() { + List> properties = Arrays.asList( + new StringProperty("first", "first"), + new StringProperty("second", "second"), + new StringProperty("third", "third")); + return new ConfigurationData(properties); + } +} diff --git a/src/test/java/tools/messages/MessagesFileWriter.java b/src/test/java/tools/messages/MessagesFileWriter.java index 2af69e2cb..534be000c 100644 --- a/src/test/java/tools/messages/MessagesFileWriter.java +++ b/src/test/java/tools/messages/MessagesFileWriter.java @@ -6,7 +6,7 @@ import ch.jalu.configme.properties.Property; import ch.jalu.configme.resource.PropertyResource; import ch.jalu.configme.resource.YamlFileResource; import fr.xephi.authme.message.updater.MessageUpdater; -import fr.xephi.authme.message.updater.MessageUpdater.MigraterYamlFileResource; +import fr.xephi.authme.message.updater.MigraterYamlFileResource; import org.bukkit.configuration.file.FileConfiguration; import tools.utils.FileIoUtils; diff --git a/src/test/resources/fr/xephi/authme/message/chinese_texts.yml b/src/test/resources/fr/xephi/authme/message/chinese_texts.yml new file mode 100644 index 000000000..5cb7228b2 --- /dev/null +++ b/src/test/resources/fr/xephi/authme/message/chinese_texts.yml @@ -0,0 +1,3 @@ +first: '错误的密码' +second: '为了验证您的身份,您需要将一个电子邮件地址与您的帐户绑定!' +third: '您已经可以在当前会话中执行任何敏感命令!' From 9ef5a983ccfb77ae4371c37cb7d45bbb0466b9ab Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Mon, 12 Feb 2018 23:00:10 +0100 Subject: [PATCH 029/155] Fix #1482 --- src/main/java/fr/xephi/authme/listener/PlayerListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 30332c15d..b4709a564 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -178,6 +178,7 @@ public class PlayerListener implements Listener { String customJoinMessage = settings.getProperty(RegistrationSettings.CUSTOM_JOIN_MESSAGE); if (!customJoinMessage.isEmpty()) { + customJoinMessage = ChatColor.translateAlternateColorCodes('&', customJoinMessage); event.setJoinMessage(customJoinMessage .replace("{PLAYERNAME}", player.getName()) .replace("{DISPLAYNAME}", player.getDisplayName()) From 1626274e9de5a3d32356c0b7286576d7f5aae5e5 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Mon, 12 Feb 2018 23:37:22 +0100 Subject: [PATCH 030/155] Try to solve #1498 --- src/main/java/fr/xephi/authme/task/purge/PurgeTask.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java index 92391af90..27b424150 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java @@ -73,6 +73,11 @@ class PurgeTask extends BukkitRunnable { OfflinePlayer offlinePlayer = offlinePlayers[nextPosition]; if (offlinePlayer.getName() != null && toPurge.remove(offlinePlayer.getName().toLowerCase())) { + try { + permissionsManager.loadUserData(offlinePlayer.getUniqueId()); + } catch (NoSuchMethodError e) { + permissionsManager.loadUserData(offlinePlayer.getName()); + } if (!permissionsManager.hasPermissionOffline(offlinePlayer, PlayerStatePermission.BYPASS_PURGE)) { playerPortion.add(offlinePlayer); namePortion.add(offlinePlayer.getName()); From 9dd4039fdd667b913992e20aef45e60ec4589112 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Tue, 13 Feb 2018 22:15:03 +0100 Subject: [PATCH 031/155] #1467 Create backup before migrating; output newly added message keys - Extract logic for creating a backup timestamp into FileUtils --- .../authme/datasource/SqLiteMigrater.java | 10 ++---- .../MessageMigraterPropertyReader.java | 8 ++++- .../message/updater/MessageUpdater.java | 31 +++++++++++++---- .../xephi/authme/service/BackupService.java | 5 +-- .../java/fr/xephi/authme/util/FileUtils.java | 33 +++++++++++++++++-- .../fr/xephi/authme/util/FileUtilsTest.java | 27 +++++++++++++++ 6 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java b/src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java index 6f8f2a371..aab79f187 100644 --- a/src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java +++ b/src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java @@ -1,5 +1,6 @@ package fr.xephi.authme.datasource; +import com.google.common.io.Files; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -8,14 +9,10 @@ import fr.xephi.authme.util.FileUtils; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; -import java.nio.file.Files; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.sql.Statement; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; /** * Migrates the SQLite database when necessary. @@ -70,11 +67,10 @@ class SqLiteMigrater { File backupDirectory = new File(dataFolder, "backups"); FileUtils.createDirectory(backupDirectory); - DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm"); - String backupName = "backup-" + databaseName + dateFormat.format(new Date()) + ".db"; + String backupName = "backup-" + databaseName + FileUtils.createCurrentTimeString() + ".db"; File backup = new File(backupDirectory, backupName); try { - Files.copy(sqLite.toPath(), backup.toPath()); + Files.copy(sqLite, backup); return backupName; } catch (IOException e) { throw new IllegalStateException("Failed to create SQLite backup before migration", e); diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java b/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java index 1174ba043..ab9f8d3b8 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java @@ -19,7 +19,7 @@ import java.util.Objects; * Implementation of {@link PropertyReader} which can read a file or a stream with * a specified charset. */ -public class MessageMigraterPropertyReader implements PropertyReader { +public final class MessageMigraterPropertyReader implements PropertyReader { public static final Charset CHARSET = StandardCharsets.UTF_8; @@ -31,6 +31,12 @@ public class MessageMigraterPropertyReader implements PropertyReader { root = valuesMap; } + /** + * Creates a new property reader for the given file. + * + * @param file the file to load + * @return the created property reader + */ public static MessageMigraterPropertyReader loadFromFile(File file) { Map valuesMap; try (InputStream is = new FileInputStream(file)) { diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index 9e06bafff..3b429106c 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -7,12 +7,17 @@ import ch.jalu.configme.properties.Property; import ch.jalu.configme.properties.StringProperty; import ch.jalu.configme.resource.YamlFileResource; import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.util.FileUtils; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -60,6 +65,8 @@ public class MessageUpdater { boolean addedMissingKeys = addMissingKeys(jarMessageSource, userResource); if (movedOldKeys || addedMissingKeys) { + backupMessagesFile(userFile); + SettingsManager settingsManager = new SettingsManager(userResource, null, CONFIGURATION_DATA); settingsManager.save(); ConsoleLogger.debug("Successfully saved {0}", userFile); @@ -77,20 +84,32 @@ public class MessageUpdater { } private boolean addMissingKeys(JarMessageSource jarMessageSource, YamlFileResource userResource) { - int addedKeys = 0; + List addedKeys = new ArrayList<>(); for (Property property : CONFIGURATION_DATA.getProperties()) { - if (userResource.getString(property.getPath()) == null) { - userResource.setValue(property.getPath(), jarMessageSource.getMessageFromJar(property)); - ++addedKeys; + final String key = property.getPath(); + if (userResource.getString(key) == null) { + userResource.setValue(key, jarMessageSource.getMessageFromJar(property)); + addedKeys.add(key); } } - if (addedKeys > 0) { - ConsoleLogger.info("Added " + addedKeys + " missing keys to your messages_xx.yml file"); + if (!addedKeys.isEmpty()) { + ConsoleLogger.info( + "Added " + addedKeys.size() + " missing keys to your messages_xx.yml file: " + addedKeys); return true; } return false; } + private static void backupMessagesFile(File messagesFile) { + String backupName = FileUtils.createBackupFilePath(messagesFile); + File backupFile = new File(backupName); + try { + Files.copy(messagesFile, backupFile); + } catch (IOException e) { + throw new IllegalStateException("Could not back up '" + messagesFile + "' to '" + backupFile + "'", e); + } + } + /** * Constructs the {@link ConfigurationData} for exporting a messages file in its entirety. * diff --git a/src/main/java/fr/xephi/authme/service/BackupService.java b/src/main/java/fr/xephi/authme/service/BackupService.java index 0141e8f6e..b85002eaa 100644 --- a/src/main/java/fr/xephi/authme/service/BackupService.java +++ b/src/main/java/fr/xephi/authme/service/BackupService.java @@ -16,8 +16,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.text.SimpleDateFormat; -import java.util.Date; import static fr.xephi.authme.util.Utils.logAndSendMessage; import static fr.xephi.authme.util.Utils.logAndSendWarning; @@ -27,7 +25,6 @@ import static fr.xephi.authme.util.Utils.logAndSendWarning; */ public class BackupService { - private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm"); private final File dataFolder; private final File backupFolder; private final Settings settings; @@ -181,7 +178,7 @@ public class BackupService { * @return the file to back up the data to */ private File constructBackupFile(String fileExtension) { - String dateString = dateFormat.format(new Date()); + String dateString = FileUtils.createCurrentTimeString(); return new File(backupFolder, "backup" + dateString + "." + fileExtension); } diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java index 759f55811..bc1bef41c 100644 --- a/src/main/java/fr/xephi/authme/util/FileUtils.java +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -1,12 +1,14 @@ package fr.xephi.authme.util; +import com.google.common.io.Files; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Files; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import static java.lang.String.format; @@ -15,6 +17,9 @@ import static java.lang.String.format; */ public final class FileUtils { + private static final DateTimeFormatter CURRENT_DATE_STRING_FORMATTER = + DateTimeFormatter.ofPattern("yyyyMMdd_HHmm"); + // Utility class private FileUtils() { } @@ -40,7 +45,7 @@ public final class FileUtils { ConsoleLogger.warning(format("Cannot copy resource '%s' to file '%s': cannot load resource", resourcePath, destinationFile.getPath())); } else { - Files.copy(is, destinationFile.toPath()); + java.nio.file.Files.copy(is, destinationFile.toPath()); return true; } } catch (IOException e) { @@ -138,4 +143,28 @@ public final class FileUtils { public static String makePath(String... elements) { return String.join(File.separator, elements); } + + /** + * Creates a textual representation of the current time (including minutes), e.g. useful for + * automatically generated backup files. + * + * @return string of the current time for use in file names + */ + public static String createCurrentTimeString() { + return LocalDateTime.now().format(CURRENT_DATE_STRING_FORMATTER); + } + + /** + * Returns a path to a new file (which doesn't exist yet) with a timestamp in the name in the same + * folder as the given file and containing the given file's filename. + * + * @param file the file based on which a new file path should be created + * @return path to a file suitably named for storing a backup + */ + public static String createBackupFilePath(File file) { + String filename = "backup_" + Files.getNameWithoutExtension(file.getName()) + + "_" + createCurrentTimeString() + + "." + Files.getFileExtension(file.getName()); + return makePath(file.getParent(), filename); + } } diff --git a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java index a20210bb9..91145cc8c 100644 --- a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java @@ -11,6 +11,7 @@ import java.io.File; import java.io.IOException; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.matchesPattern; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; @@ -20,6 +21,9 @@ import static org.junit.Assert.assertThat; */ public class FileUtilsTest { + /** Regex that matches timestamps such as 20180211_1048. */ + private static final String BACKUP_TIMESTAMP_PATTERN = "20\\d{6}_\\d{4}"; + @BeforeClass public static void initLogger() { TestHelper.setupLogger(); @@ -186,6 +190,29 @@ public class FileUtilsTest { TestHelper.validateHasOnlyPrivateEmptyConstructor(FileUtils.class); } + + @Test + public void shouldCreateCurrentTimestampString() { + // given / when + String currentTimeString = FileUtils.createCurrentTimeString(); + + // then + assertThat(currentTimeString, matchesPattern(BACKUP_TIMESTAMP_PATTERN)); + } + + @Test + public void shouldCreateBackupFile() { + // given + File file = new File("some/folders/config.yml"); + + // when + String backupFile = FileUtils.createBackupFilePath(file); + + // then + String folders = String.join(File.separator,"some", "folders", "").replace("\\", "\\\\"); + assertThat(backupFile, matchesPattern(folders + "backup_config_" + BACKUP_TIMESTAMP_PATTERN + "\\.yml")); + } + private static void createFiles(File... files) throws IOException { for (File file : files) { boolean result = file.getParentFile().mkdirs() & file.createNewFile(); From 386703a1b49d80212f7f8b8bb99518a75c9054cf Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Wed, 14 Feb 2018 02:10:24 +0100 Subject: [PATCH 032/155] Update maven plugins and dependencies --- pom.xml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index b508bfa19..0e81586ab 100644 --- a/pom.xml +++ b/pom.xml @@ -186,7 +186,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.0.0 attach-javadocs @@ -336,12 +336,6 @@ http://repo.onarandombox.com/content/groups/public - - - luck-repo - https://repo.lucko.me/ - - vault-repo @@ -545,7 +539,7 @@ me.lucko.luckperms luckperms-api - 4.0-SNAPSHOT + 4.0 provided From b47bc9daa7243f0650bbdd3df36282315e4f5a42 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Fri, 16 Feb 2018 20:51:51 +0100 Subject: [PATCH 033/155] Fix the permissionex dependency --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0e81586ab..4ada4f73c 100644 --- a/pom.xml +++ b/pom.xml @@ -547,7 +547,7 @@ ru.tehkode PermissionsEx - 1.23.5 + 1.23.5-SNAPSHOT provided From 83e247afe9edbd311b061ac55378ab6242df84f8 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 17 Feb 2018 23:23:44 +0100 Subject: [PATCH 034/155] Minor: Simplify check for Spigot --- src/main/java/fr/xephi/authme/util/Utils.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 8146f4e25..b51eb9cfe 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -28,12 +28,7 @@ public final class Utils { * @return true if the running server instance is spigot-based. */ public static boolean isSpigot() { - try { - Class.forName("org.spigotmc.SpigotConfig"); - } catch (ClassNotFoundException e) { - return false; - } - return true; + return isClassLoaded("org.spigotmc.SpigotConfig"); } /** From 7864bb06aca84e1bf8f757f7c8d35198f90570da Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 23 Feb 2018 23:23:24 +0100 Subject: [PATCH 035/155] Minor cleanups - Fix line length violations - Add JavaDoc to some longer methods - Remove unused imports --- src/main/java/fr/xephi/authme/datasource/MySQL.java | 4 ++-- .../datasource/mysqlextensions/XfBcryptExtension.java | 1 + .../fr/xephi/authme/initialization/OnStartupTasks.java | 4 ++++ .../fr/xephi/authme/permission/PermissionsManager.java | 9 +++++++++ src/main/java/fr/xephi/authme/process/Management.java | 1 - .../fr/xephi/authme/process/join/AsynchronousJoin.java | 1 - .../java/fr/xephi/authme/service/AntiBotService.java | 3 +++ .../fr/xephi/authme/service/TeleportationService.java | 3 ++- .../fr/xephi/authme/service/bungeecord/BungeeSender.java | 1 - 9 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 4e6695787..01a240fe3 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -12,7 +12,6 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.util.StringUtils; -import fr.xephi.authme.util.Utils; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -344,7 +343,8 @@ public class MySQL implements DataSource { if (!columnOthers.isEmpty()) { for (String column : columnOthers) { - try (PreparedStatement pst = con.prepareStatement("UPDATE " + tableName + " SET " + column + "=? WHERE " + col.NAME + "=?;")) { + try (PreparedStatement pst = con.prepareStatement( + "UPDATE " + tableName + " SET " + column + "=? WHERE " + col.NAME + "=?;")) { pst.setString(1, auth.getRealName()); pst.setString(2, auth.getNickname()); pst.executeUpdate(); diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java b/src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java index 46dd41ef9..84ab4c953 100644 --- a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java +++ b/src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java @@ -85,6 +85,7 @@ class XfBcryptExtension extends MySqlExtension { } } + @Override public void extendAuth(PlayerAuth auth, int id, Connection con) throws SQLException { try (PreparedStatement pst = con.prepareStatement( "SELECT data FROM " + xfPrefix + "user_authenticate WHERE " + col.ID + "=?;")) { diff --git a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java index e669a5a77..498e5c266 100644 --- a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java +++ b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java @@ -83,6 +83,10 @@ public class OnStartupTasks { logger.addFilter(new Log4JFilter()); } + /** + * Starts a task that regularly reminds players without a defined email to set their email, + * if enabled. + */ public void scheduleRecallEmailTask() { if (!settings.getProperty(RECALL_PLAYERS)) { return; diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java index ef9d8bdde..55845f745 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java @@ -258,6 +258,15 @@ public class PermissionsManager implements Reloadable { return handler.hasPermissionOffline(player.getName(), permissionNode); } + /** + * Check whether the offline player with the given name has permission for the given permission node. + * This method is used as a last resort when nothing besides the name is known. + * + * @param name The name of the player + * @param permissionNode The permission node to verify + * + * @return true if the player has permission, false otherwise + */ public boolean hasPermissionOffline(String name, PermissionNode permissionNode) { if (permissionNode == null) { return true; diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index a6eb00057..5260fed13 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -12,7 +12,6 @@ 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.Location; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index ed63e3ede..6cb16a674 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -20,7 +20,6 @@ import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.util.PlayerUtils; import org.bukkit.GameMode; -import org.bukkit.Location; import org.bukkit.Server; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index 90c62a24b..b65400992 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -98,6 +98,9 @@ public class AntiBotService implements SettingsDependent { disableTask = bukkitService.runTaskLater(this::stopProtection, duration * TICKS_PER_MINUTE); } + /** + * Transitions the anti bot service from active status back to listening. + */ private void stopProtection() { if (antiBotStatus != AntiBotStatus.ACTIVE) { return; diff --git a/src/main/java/fr/xephi/authme/service/TeleportationService.java b/src/main/java/fr/xephi/authme/service/TeleportationService.java index 38917d4d8..10f9e1178 100644 --- a/src/main/java/fr/xephi/authme/service/TeleportationService.java +++ b/src/main/java/fr/xephi/authme/service/TeleportationService.java @@ -79,7 +79,8 @@ public class TeleportationService implements Reloadable { && settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN)) { final Location location = spawnLoader.getSpawnLocation(player); - SpawnTeleportEvent event = new SpawnTeleportEvent(player, location, playerCache.isAuthenticated(player.getName())); + SpawnTeleportEvent event = new SpawnTeleportEvent(player, location, + playerCache.isAuthenticated(player.getName())); bukkitService.callEvent(event); if(!isEventValid(event)) { return null; diff --git a/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java b/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java index 0b5e6ca64..6c559f6b0 100644 --- a/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java +++ b/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java @@ -12,7 +12,6 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.messaging.Messenger; import javax.inject.Inject; -import java.io.Console; public class BungeeSender implements SettingsDependent { From 329657bd5f482a23e33b5ed7ec90a0130d825dde Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 23 Feb 2018 23:31:22 +0100 Subject: [PATCH 036/155] #1497 Show specific message for invalid YAML files (#1506) * #1497 Throw dedicated exception for invalid YAML files and handle it on startup - Wrap SnakeYAML exceptions when loading config.yml and commands.yml on startup into own exception type - Handle exception type on startup with specific error message * #1497 Fix inaccurate JavaDoc comment --- src/main/java/fr/xephi/authme/AuthMe.java | 10 +++- .../initialization/SettingsProvider.java | 4 +- .../yaml/YamlFileResourceProvider.java | 30 +++++++++++ .../service/yaml/YamlParseException.java | 26 +++++++++ .../commandconfig/CommandManager.java | 4 +- .../fr/xephi/authme/util/ExceptionUtils.java | 36 +++++++++++++ .../yaml/YamlFileResourceProviderTest.java | 48 +++++++++++++++++ .../xephi/authme/util/ExceptionUtilsTest.java | 54 +++++++++++++++++++ .../xephi/authme/service/yaml/invalidYaml.yml | 5 ++ .../xephi/authme/service/yaml/validYaml.yml | 4 ++ 10 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java create mode 100644 src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java create mode 100644 src/main/java/fr/xephi/authme/util/ExceptionUtils.java create mode 100644 src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java create mode 100644 src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java create mode 100644 src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml create mode 100644 src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 15a37ab48..d547f3ae0 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -26,11 +26,13 @@ import fr.xephi.authme.service.BackupService; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.MigrationService; import fr.xephi.authme.service.bungeecord.BungeeReceiver; +import fr.xephi.authme.service.yaml.YamlParseException; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SettingsWarner; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.CleanupTask; import fr.xephi.authme.task.purge.PurgeService; +import fr.xephi.authme.util.ExceptionUtils; import org.apache.commons.lang.SystemUtils; import org.bukkit.Server; import org.bukkit.command.Command; @@ -133,7 +135,13 @@ public class AuthMe extends JavaPlugin { try { initialize(); } catch (Throwable th) { - ConsoleLogger.logException("Aborting initialization of AuthMe:", th); + YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th); + if (yamlParseException == null) { + ConsoleLogger.logException("Aborting initialization of AuthMe:", th); + } else { + ConsoleLogger.logException("File '" + yamlParseException.getFile() + "' contains invalid YAML. " + + "Please run its contents through http://yamllint.com", yamlParseException); + } stopOrUnload(); return; } diff --git a/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java b/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java index e5e69e667..77133715f 100644 --- a/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java +++ b/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java @@ -2,7 +2,7 @@ package fr.xephi.authme.initialization; import ch.jalu.configme.configurationdata.ConfigurationData; import ch.jalu.configme.resource.PropertyResource; -import ch.jalu.configme.resource.YamlFileResource; +import fr.xephi.authme.service.yaml.YamlFileResourceProvider; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SettingsMigrationService; import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; @@ -37,7 +37,7 @@ public class SettingsProvider implements Provider { if (!configFile.exists()) { FileUtils.create(configFile); } - PropertyResource resource = new YamlFileResource(configFile); + PropertyResource resource = YamlFileResourceProvider.loadFromFile(configFile); ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); return new Settings(dataFolder, resource, migrationService, configurationData); } diff --git a/src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java b/src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java new file mode 100644 index 000000000..b13294371 --- /dev/null +++ b/src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java @@ -0,0 +1,30 @@ +package fr.xephi.authme.service.yaml; + +import ch.jalu.configme.resource.YamlFileResource; +import org.yaml.snakeyaml.parser.ParserException; + +import java.io.File; + +/** + * Creates {@link YamlFileResource} objects. + */ +public final class YamlFileResourceProvider { + + private YamlFileResourceProvider() { + } + + /** + * Creates a {@link YamlFileResource} instance for the given file. Wraps SnakeYAML's parse exception + * into an AuthMe exception. + * + * @param file the file to load + * @return the generated resource + */ + public static YamlFileResource loadFromFile(File file) { + try { + return new YamlFileResource(file); + } catch (ParserException e) { + throw new YamlParseException(file.getPath(), e); + } + } +} diff --git a/src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java b/src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java new file mode 100644 index 000000000..b070bcf35 --- /dev/null +++ b/src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java @@ -0,0 +1,26 @@ +package fr.xephi.authme.service.yaml; + +import org.yaml.snakeyaml.parser.ParserException; + +/** + * Exception when a YAML file could not be parsed. + */ +public class YamlParseException extends RuntimeException { + + private final String file; + + /** + * Constructor. + * + * @param file the file a parsing exception occurred with + * @param snakeYamlException the caught exception from SnakeYAML + */ + public YamlParseException(String file, ParserException snakeYamlException) { + super(snakeYamlException); + this.file = file; + } + + public String getFile() { + return file; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java index 12579cbae..50d73c9a5 100644 --- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java @@ -1,11 +1,11 @@ package fr.xephi.authme.settings.commandconfig; import ch.jalu.configme.SettingsManager; -import ch.jalu.configme.resource.YamlFileResource; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.GeoIpService; +import fr.xephi.authme.service.yaml.YamlFileResourceProvider; import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.PlayerUtils; import fr.xephi.authme.util.lazytags.Tag; @@ -149,7 +149,7 @@ public class CommandManager implements Reloadable { FileUtils.copyFileFromResource(file, "commands.yml"); SettingsManager settingsManager = new SettingsManager( - new YamlFileResource(file), commandMigrationService, CommandSettingsHolder.class); + YamlFileResourceProvider.loadFromFile(file), commandMigrationService, CommandSettingsHolder.class); CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS); onJoinCommands = newReplacer(commandConfig.getOnJoin()); onLoginCommands = newOnLoginCmdReplacer(commandConfig.getOnLogin()); diff --git a/src/main/java/fr/xephi/authme/util/ExceptionUtils.java b/src/main/java/fr/xephi/authme/util/ExceptionUtils.java new file mode 100644 index 000000000..6a5adde69 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/ExceptionUtils.java @@ -0,0 +1,36 @@ +package fr.xephi.authme.util; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * Utilities for exceptions. + */ +public final class ExceptionUtils { + + private ExceptionUtils() { + } + + /** + * Returns the first throwable of the given {@code wantedThrowableType} by visiting the provided + * throwable and its causes recursively. + * + * @param wantedThrowableType the throwable type to find + * @param throwable the throwable to start with + * @param the desired throwable subtype + * @return the first throwable found of the given type, or null if none found + */ + public static T findThrowableInCause(Class wantedThrowableType, Throwable throwable) { + Set visitedObjects = Sets.newIdentityHashSet(); + Throwable currentThrowable = throwable; + while (currentThrowable != null && !visitedObjects.contains(currentThrowable)) { + if (wantedThrowableType.isInstance(currentThrowable)) { + return wantedThrowableType.cast(currentThrowable); + } + visitedObjects.add(currentThrowable); + currentThrowable = currentThrowable.getCause(); + } + return null; + } +} diff --git a/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java b/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java new file mode 100644 index 000000000..f870a6b43 --- /dev/null +++ b/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java @@ -0,0 +1,48 @@ +package fr.xephi.authme.service.yaml; + +import ch.jalu.configme.resource.YamlFileResource; +import fr.xephi.authme.TestHelper; +import org.junit.Test; +import org.yaml.snakeyaml.parser.ParserException; + +import java.io.File; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * Test for {@link YamlFileResourceProvider}. + */ +public class YamlFileResourceProviderTest { + + @Test + public void shouldLoadValidFile() { + // given + File yamlFile = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "service/yaml/validYaml.yml"); + + // when + YamlFileResource resource = YamlFileResourceProvider.loadFromFile(yamlFile); + + // then + assertThat(resource.getString("test.jkl"), equalTo("Test test")); + } + + @Test + public void shouldThrowForInvalidFile() { + // given + File yamlFile = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "service/yaml/invalidYaml.yml"); + + // when + try { + YamlFileResourceProvider.loadFromFile(yamlFile); + + // then + fail("Expected exception to be thrown"); + } catch (YamlParseException e) { + assertThat(e.getFile(), equalTo(yamlFile.getPath())); + assertThat(e.getCause(), instanceOf(ParserException.class)); + } + } +} diff --git a/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java b/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java new file mode 100644 index 000000000..af154537e --- /dev/null +++ b/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java @@ -0,0 +1,54 @@ +package fr.xephi.authme.util; + +import fr.xephi.authme.ReflectionTestUtils; +import org.junit.Test; + +import java.util.ConcurrentModificationException; + +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link ExceptionUtils}. + */ +public class ExceptionUtilsTest { + + @Test + public void shouldFindWantedThrowable() { + // given + ConcurrentModificationException initialCme = new ConcurrentModificationException(); + Throwable th = new Throwable(initialCme); + ConcurrentModificationException cme = new ConcurrentModificationException(th); + IllegalStateException ise = new IllegalStateException(cme); + UnsupportedOperationException uoe = new UnsupportedOperationException(ise); + ReflectiveOperationException roe = new ReflectiveOperationException(uoe); + + // when + IllegalStateException resultIse = ExceptionUtils.findThrowableInCause(IllegalStateException.class, roe); + ConcurrentModificationException resultCme = ExceptionUtils.findThrowableInCause(ConcurrentModificationException.class, cme); + StackOverflowError resultSoe = ExceptionUtils.findThrowableInCause(StackOverflowError.class, cme); + + // then + assertThat(resultIse, sameInstance(ise)); + assertThat(resultCme, sameInstance(cme)); + assertThat(resultSoe, nullValue()); + } + + @Test + public void shouldHandleCircularCausesGracefully() { + // given + IllegalStateException ise = new IllegalStateException(); + UnsupportedOperationException uoe = new UnsupportedOperationException(ise); + ReflectiveOperationException roe = new ReflectiveOperationException(uoe); + ReflectionTestUtils.setField(Throwable.class, ise, "cause", roe); + + // when + NullPointerException resultNpe = ExceptionUtils.findThrowableInCause(NullPointerException.class, uoe); + UnsupportedOperationException resultUoe = ExceptionUtils.findThrowableInCause(UnsupportedOperationException.class, uoe); + + // then + assertThat(resultNpe, nullValue()); + assertThat(resultUoe, sameInstance(uoe)); + } +} diff --git a/src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml b/src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml new file mode 100644 index 000000000..02daab8c9 --- /dev/null +++ b/src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml @@ -0,0 +1,5 @@ +# File with invalid YAML +test: + abc: 'test' + def: 'Going to forget a quote here + jkl: 'Test test' diff --git a/src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml b/src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml new file mode 100644 index 000000000..cebbd0b97 --- /dev/null +++ b/src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml @@ -0,0 +1,4 @@ +test: + abc: 'test' + def: 'All quotes are good here' + jkl: 'Test test' From 8d5afa7fbcd4c2e56520f37ac6b1cde6a013a2aa Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 23 Feb 2018 23:37:24 +0100 Subject: [PATCH 037/155] Minor: Use CommonService for permission lookup - Some changes found in a very old patch :) - drop injection of PermissionsManager in favor of CommonService - Rename IsEqualByReflectionMatcher's method to something more specific to differentiate it better from Hamcrest's equalTo() matcher --- .../authme/process/register/AsyncRegister.java | 5 +---- .../register/executors/EmailRegisterExecutor.java | 6 +----- .../fr/xephi/authme/IsEqualByReflectionMatcher.java | 4 ++-- src/test/java/fr/xephi/authme/api/NewAPITest.java | 6 +++--- .../java/fr/xephi/authme/api/v3/AuthMeApiTest.java | 6 +++--- .../executable/register/RegisterCommandTest.java | 12 ++++++------ .../authme/process/register/AsyncRegisterTest.java | 3 --- ...iderTest.java => EmailRegisterExecutorTest.java} | 13 +++++-------- .../fr/xephi/authme/util/ExceptionUtilsTest.java | 7 +++++++ 9 files changed, 28 insertions(+), 34 deletions(-) rename src/test/java/fr/xephi/authme/process/register/executors/{EmailRegisterExecutorProviderTest.java => EmailRegisterExecutorTest.java} (91%) 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 a370b1a94..d50fe9a32 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -5,7 +5,6 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; 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.RegistrationMethod; @@ -35,8 +34,6 @@ public class AsyncRegister implements AsynchronousProcess { @Inject private CommonService service; @Inject - private PermissionsManager permissionsManager; - @Inject private SingletonStore registrationExecutorFactory; @Inject private BungeeSender bungeeSender; @@ -106,7 +103,7 @@ public class AsyncRegister implements AsynchronousProcess { if (maxRegPerIp > 0 && !"127.0.0.1".equalsIgnoreCase(ip) && !"localhost".equalsIgnoreCase(ip) - && !permissionsManager.hasPermission(player, ALLOW_MULTIPLE_ACCOUNTS)) { + && !service.hasPermission(player, ALLOW_MULTIPLE_ACCOUNTS)) { List otherAccounts = database.getAllAuthsByIp(ip); if (otherAccounts.size() >= maxRegPerIp) { service.send(player, MessageKey.MAX_REGISTER_EXCEEDED, Integer.toString(maxRegPerIp), 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 index 0f4153b18..c344447c8 100644 --- a/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java +++ b/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java @@ -4,7 +4,6 @@ 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; @@ -25,9 +24,6 @@ import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWOR */ class EmailRegisterExecutor implements RegistrationExecutor { - @Inject - private PermissionsManager permissionsManager; - @Inject private DataSource dataSource; @@ -46,7 +42,7 @@ class EmailRegisterExecutor implements RegistrationExecutor @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)) { + if (maxRegPerEmail > 0 && !commonService.hasPermission(params.getPlayer(), ALLOW_MULTIPLE_ACCOUNTS)) { int otherAccounts = dataSource.countAuthsByEmail(params.getEmail()); if (otherAccounts >= maxRegPerEmail) { commonService.send(params.getPlayer(), MessageKey.MAX_REGISTER_EXCEEDED, diff --git a/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java b/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java index a6033ffd3..55df85579 100644 --- a/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java +++ b/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java @@ -31,7 +31,7 @@ public final class IsEqualByReflectionMatcher extends TypeSafeMatcher { * @param the object's type * @return the matcher for the expected object */ - public static Matcher isEqualTo(T expected) { + public static Matcher hasEqualValuesOnAllFields(T expected) { return new IsEqualByReflectionMatcher<>(expected); } @@ -70,7 +70,7 @@ public final class IsEqualByReflectionMatcher extends TypeSafeMatcher { Class currentClass = object.getClass(); while (currentClass != null) { for (Field f : currentClass.getDeclaredFields()) { - if (!Modifier.isStatic(f.getModifiers())) { + if (!Modifier.isStatic(f.getModifiers()) && !f.isSynthetic()) { fields.add(f); } } diff --git a/src/test/java/fr/xephi/authme/api/NewAPITest.java b/src/test/java/fr/xephi/authme/api/NewAPITest.java index 15440e3a6..92986c5ee 100644 --- a/src/test/java/fr/xephi/authme/api/NewAPITest.java +++ b/src/test/java/fr/xephi/authme/api/NewAPITest.java @@ -27,7 +27,7 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import static fr.xephi.authme.IsEqualByReflectionMatcher.isEqualTo; +import static fr.xephi.authme.IsEqualByReflectionMatcher.hasEqualValuesOnAllFields; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; @@ -305,7 +305,7 @@ public class NewAPITest { // then verify(management).performRegister(eq(RegistrationMethod.API_REGISTRATION), - argThat(isEqualTo(ApiPasswordRegisterParams.of(player, pass, true)))); + argThat(hasEqualValuesOnAllFields(ApiPasswordRegisterParams.of(player, pass, true)))); } @Test @@ -319,7 +319,7 @@ public class NewAPITest { // then verify(management).performRegister(eq(RegistrationMethod.API_REGISTRATION), - argThat(isEqualTo(ApiPasswordRegisterParams.of(player, pass, false)))); + argThat(hasEqualValuesOnAllFields(ApiPasswordRegisterParams.of(player, pass, false)))); } @Test diff --git a/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java b/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java index 433675f9c..6021b3a92 100644 --- a/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java +++ b/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java @@ -30,7 +30,7 @@ import java.util.List; import java.util.stream.Collectors; import java.time.Instant; -import static fr.xephi.authme.IsEqualByReflectionMatcher.isEqualTo; +import static fr.xephi.authme.IsEqualByReflectionMatcher.hasEqualValuesOnAllFields; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; @@ -427,7 +427,7 @@ public class AuthMeApiTest { // then verify(management).performRegister(eq(RegistrationMethod.API_REGISTRATION), - argThat(isEqualTo(ApiPasswordRegisterParams.of(player, pass, true)))); + argThat(hasEqualValuesOnAllFields(ApiPasswordRegisterParams.of(player, pass, true)))); } @Test @@ -441,7 +441,7 @@ public class AuthMeApiTest { // then verify(management).performRegister(eq(RegistrationMethod.API_REGISTRATION), - argThat(isEqualTo(ApiPasswordRegisterParams.of(player, pass, false)))); + argThat(hasEqualValuesOnAllFields(ApiPasswordRegisterParams.of(player, pass, false)))); } @Test 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 a4d8f7aa7..48b13c931 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 @@ -30,7 +30,7 @@ import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; -import static fr.xephi.authme.IsEqualByReflectionMatcher.isEqualTo; +import static fr.xephi.authme.IsEqualByReflectionMatcher.hasEqualValuesOnAllFields; import static org.hamcrest.Matchers.containsString; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -101,7 +101,7 @@ public class RegisterCommandTest { // then verify(registrationCaptchaManager).isCaptchaRequired("test2"); verify(management).performRegister(eq(RegistrationMethod.TWO_FACTOR_REGISTRATION), - argThat(isEqualTo(TwoFactorRegisterParams.of(player)))); + argThat(hasEqualValuesOnAllFields(TwoFactorRegisterParams.of(player)))); verifyZeroInteractions(emailService); } @@ -221,7 +221,7 @@ public class RegisterCommandTest { verify(validationService).validateEmail(playerMail); verify(emailService).hasAllInformation(); verify(management).performRegister(eq(RegistrationMethod.EMAIL_REGISTRATION), - argThat(isEqualTo(EmailRegisterParams.of(player, playerMail)))); + argThat(hasEqualValuesOnAllFields(EmailRegisterParams.of(player, playerMail)))); } @Test @@ -250,7 +250,7 @@ public class RegisterCommandTest { // then verify(registrationCaptchaManager).isCaptchaRequired("newPlayer"); verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION), - argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", null)))); + argThat(hasEqualValuesOnAllFields(PasswordRegisterParams.of(player, "myPass", null)))); } @Test @@ -268,7 +268,7 @@ public class RegisterCommandTest { // then verify(validationService).validateEmail(email); verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION), - argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", email)))); + argThat(hasEqualValuesOnAllFields(PasswordRegisterParams.of(player, "myPass", email)))); } @Test @@ -303,7 +303,7 @@ public class RegisterCommandTest { // then verify(registrationCaptchaManager).isCaptchaRequired("Doa"); verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION), - argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", null)))); + argThat(hasEqualValuesOnAllFields(PasswordRegisterParams.of(player, "myPass", null)))); } @Test 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 e044a754a..77f91ca88 100644 --- a/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java +++ b/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java @@ -5,7 +5,6 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; 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; @@ -39,8 +38,6 @@ public class AsyncRegisterTest { @Mock private PlayerCache playerCache; @Mock - private PermissionsManager permissionsManager; - @Mock private CommonService commonService; @Mock private DataSource dataSource; diff --git a/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProviderTest.java b/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorTest.java similarity index 91% rename from src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProviderTest.java rename to src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorTest.java index a04aa7130..290566bcd 100644 --- a/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProviderTest.java +++ b/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorTest.java @@ -5,7 +5,6 @@ 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.permission.PlayerStatePermission; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.security.PasswordSecurity; @@ -35,13 +34,11 @@ import static org.mockito.Mockito.verifyZeroInteractions; * Test for {@link EmailRegisterExecutor}. */ @RunWith(MockitoJUnitRunner.class) -public class EmailRegisterExecutorProviderTest { +public class EmailRegisterExecutorTest { @InjectMocks private EmailRegisterExecutor executor; - @Mock - private PermissionsManager permissionsManager; @Mock private DataSource dataSource; @Mock @@ -68,7 +65,7 @@ public class EmailRegisterExecutorProviderTest { // then assertThat(result, equalTo(false)); verify(dataSource).countAuthsByEmail(email); - verify(permissionsManager).hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS); + verify(commonService).hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS); verify(commonService).send(player, MessageKey.MAX_REGISTER_EXCEEDED, "3", "4", "@"); } @@ -77,7 +74,7 @@ public class EmailRegisterExecutorProviderTest { // given 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); + given(commonService.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(true); EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com"); // when @@ -85,7 +82,7 @@ public class EmailRegisterExecutorProviderTest { // then assertThat(result, equalTo(true)); - verify(permissionsManager).hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS); + verify(commonService).hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS); } @Test @@ -102,7 +99,7 @@ public class EmailRegisterExecutorProviderTest { // then assertThat(result, equalTo(true)); - verify(permissionsManager).hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS); + verify(commonService).hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS); verify(dataSource).countAuthsByEmail(email); } diff --git a/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java b/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java index af154537e..8685d7f33 100644 --- a/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java @@ -1,6 +1,7 @@ package fr.xephi.authme.util; import fr.xephi.authme.ReflectionTestUtils; +import fr.xephi.authme.TestHelper; import org.junit.Test; import java.util.ConcurrentModificationException; @@ -51,4 +52,10 @@ public class ExceptionUtilsTest { assertThat(resultNpe, nullValue()); assertThat(resultUoe, sameInstance(uoe)); } + + @Test + public void shouldHaveHiddenConstructor() { + // given / when / then + TestHelper.validateHasOnlyPrivateEmptyConstructor(ExceptionUtils.class); + } } From d07e887472bdfff5b8b9a327200661a7d90e60fc Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Tue, 27 Feb 2018 01:14:37 +0100 Subject: [PATCH 038/155] Update libraries --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4ada4f73c..2aa08039b 100644 --- a/pom.xml +++ b/pom.xml @@ -389,7 +389,7 @@ com.google.guava guava - 23.6-jre + 24.0-jre compile true @@ -424,7 +424,7 @@ com.zaxxer HikariCP - 2.7.6 + 2.7.8 compile true @@ -802,7 +802,7 @@ org.mockito mockito-core test - 2.13.0 + 2.15.0 hamcrest-core From 37fb3a2c54509ac48df60a750822e8dc4830eae2 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 27 Feb 2018 08:49:57 +0100 Subject: [PATCH 039/155] Bye bye old repository RIP 2015-2018 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2aa08039b..6b87a6785 100644 --- a/pom.xml +++ b/pom.xml @@ -344,8 +344,8 @@ - xephi-repo - http://ci.xephi.fr/plugin/repository/everything/ + codemc-repo + http://ci.codemc.org/plugin/repository/maven-public/ From 2aaf58182cc9b35a9a7e97094ceb91e0334e5bc7 Mon Sep 17 00:00:00 2001 From: Maxetto Date: Wed, 28 Feb 2018 16:18:08 +0100 Subject: [PATCH 040/155] Be able to replace sender name in every message Closes #829 --- src/main/java/fr/xephi/authme/message/Messages.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 11227affc..9af2a4d66 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -18,6 +18,8 @@ public class Messages { // Custom Authme tag replaced to new line private static final String NEWLINE_TAG = "%nl%"; + private static final String PLAYER_TAG = "%username%"; + /** Contains the keys of the singular messages for time units. */ private static final Map TIME_UNIT_SINGULARS = ImmutableMap.builder() .put(TimeUnit.SECONDS, MessageKey.SECOND) @@ -51,7 +53,7 @@ public class Messages { public void send(CommandSender sender, MessageKey key) { String[] lines = retrieve(key); for (String line : lines) { - sender.sendMessage(line); + sender.sendMessage(line.replace(PLAYER_TAG, sender.getName())); } } @@ -65,7 +67,7 @@ public class Messages { * @param replacements The replacements to apply for the tags */ public void send(CommandSender sender, MessageKey key, String... replacements) { - String message = retrieveSingle(key, replacements); + String message = retrieveSingle(key, replacements).replace(PLAYER_TAG, sender.getName()); for (String line : message.split("\n")) { sender.sendMessage(line); } From 430716b206194035dc0640cb6df55f0a8da004bd Mon Sep 17 00:00:00 2001 From: Maxetto Date: Wed, 28 Feb 2018 18:05:51 +0100 Subject: [PATCH 041/155] Fix build --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b87a6785..56ef29401 100644 --- a/pom.xml +++ b/pom.xml @@ -345,7 +345,7 @@ codemc-repo - http://ci.codemc.org/plugin/repository/maven-public/ + https://repo.codemc.org/repository/maven-public/ From 3541f21985892905e103e8777fc90c32ed571695 Mon Sep 17 00:00:00 2001 From: Maxetto Date: Wed, 28 Feb 2018 18:25:50 +0100 Subject: [PATCH 042/155] I forgot this wasn't Apache's StringUtils --- src/main/java/fr/xephi/authme/message/Messages.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 9af2a4d66..7cc0ac285 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -53,7 +53,7 @@ public class Messages { public void send(CommandSender sender, MessageKey key) { String[] lines = retrieve(key); for (String line : lines) { - sender.sendMessage(line.replace(PLAYER_TAG, sender.getName())); + sender.sendMessage(line.replaceAll(PLAYER_TAG, sender.getName())); } } @@ -67,7 +67,7 @@ public class Messages { * @param replacements The replacements to apply for the tags */ public void send(CommandSender sender, MessageKey key, String... replacements) { - String message = retrieveSingle(key, replacements).replace(PLAYER_TAG, sender.getName()); + String message = retrieveSingle(key, replacements).replaceAll(PLAYER_TAG, sender.getName()); for (String line : message.split("\n")) { sender.sendMessage(line); } From fe075a5c6028c8c29cc50ad6605cddead385754d Mon Sep 17 00:00:00 2001 From: Maxetto Date: Thu, 1 Mar 2018 18:04:30 +0100 Subject: [PATCH 043/155] Remove VIP status to OP by default Closes #1040. --- src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 0f74a3ac3..2bae573d7 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -283,4 +283,4 @@ permissions: authme.vip: description: When the server is full and someone with this permission joins the server, someone will be kicked. - default: op + default: false From 6c168b6b13cd029b6659008905909ed731a6b205 Mon Sep 17 00:00:00 2001 From: Maxetto Date: Fri, 2 Mar 2018 12:18:36 +0100 Subject: [PATCH 044/155] Fix last commit --- .../java/fr/xephi/authme/permission/PlayerStatePermission.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java index 0f2d4312f..2a0517225 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java @@ -19,7 +19,7 @@ public enum PlayerStatePermission implements PermissionNode { /** * When the server is full and someone with this permission joins the server, someone will be kicked. */ - IS_VIP("authme.vip", DefaultPermission.OP_ONLY), + IS_VIP("authme.vip", DefaultPermission.NOT_ALLOWED), /** * Permission to be able to register multiple accounts. From 914c4adc0b12364eb3d7f1ac13cea8b9a2f76f2b Mon Sep 17 00:00:00 2001 From: games647 Date: Sun, 4 Mar 2018 14:29:08 +0100 Subject: [PATCH 045/155] Fix ProtocolLib services are disabling after authme reload Using the previous else branch we would always call unregister if the feature was enabled before. --- .../protocollib/ProtocolLibService.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java b/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java index 585a810ab..43d6fed47 100644 --- a/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java +++ b/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java @@ -5,9 +5,9 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.initialization.SettingsDependent; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.RestrictionSettings; -import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -46,7 +46,7 @@ public class ProtocolLibService implements SettingsDependent { if (protectInvBeforeLogin) { ConsoleLogger.warning("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); } - + if (denyTabCompleteBeforeLogin) { ConsoleLogger.warning("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it..."); } @@ -56,16 +56,21 @@ public class ProtocolLibService implements SettingsDependent { } // Set up packet adapters - if (protectInvBeforeLogin && inventoryPacketAdapter == null) { - inventoryPacketAdapter = new InventoryPacketAdapter(plugin, playerCache); - inventoryPacketAdapter.register(); + if (protectInvBeforeLogin) { + if (inventoryPacketAdapter == null) { + inventoryPacketAdapter = new InventoryPacketAdapter(plugin, playerCache); + inventoryPacketAdapter.register(); + } } else if (inventoryPacketAdapter != null) { inventoryPacketAdapter.unregister(); inventoryPacketAdapter = null; } - if (denyTabCompleteBeforeLogin && tabCompletePacketAdapter == null) { - tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, playerCache); - tabCompletePacketAdapter.register(); + + if (denyTabCompleteBeforeLogin) { + if (tabCompletePacketAdapter == null) { + tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, playerCache); + tabCompletePacketAdapter.register(); + } } else if (tabCompletePacketAdapter != null) { tabCompletePacketAdapter.unregister(); tabCompletePacketAdapter = null; @@ -106,7 +111,7 @@ public class ProtocolLibService implements SettingsDependent { this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN); //it was true and will be deactivated now, so we need to restore the inventory for every player - if (oldProtectInventory && !protectInvBeforeLogin) { + if (oldProtectInventory && !protectInvBeforeLogin && inventoryPacketAdapter != null) { inventoryPacketAdapter.unregister(); for (Player onlinePlayer : bukkitService.getOnlinePlayers()) { if (!playerCache.isAuthenticated(onlinePlayer.getName())) { From c0e1b8082f0c00257e60ff83708fc209e417b4b1 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Sun, 4 Mar 2018 17:32:16 +0100 Subject: [PATCH 046/155] #642 - Quick Command Protection manager --- .../data/QuickCommandsProtectionManager.java | 51 +++++++++++++++++++ .../authme/permission/PlayerPermission.java | 7 ++- .../properties/ProtectionSettings.java | 4 ++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java diff --git a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java new file mode 100644 index 000000000..38a007f32 --- /dev/null +++ b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java @@ -0,0 +1,51 @@ +package fr.xephi.authme.data; + +import fr.xephi.authme.initialization.HasCleanup; +import fr.xephi.authme.initialization.SettingsDependent; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerPermission; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.ProtectionSettings; +import fr.xephi.authme.util.expiring.ExpiringSet; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.util.concurrent.TimeUnit; + +public class QuickCommandsProtectionManager implements SettingsDependent, HasCleanup { + + private PermissionsManager permissionsManager; + + private final ExpiringSet latestLogin; + + @Inject + public QuickCommandsProtectionManager(Settings settings, PermissionsManager permissionsManager) { + this.permissionsManager = permissionsManager; + long countTimeout = settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS); + latestLogin = new ExpiringSet<>(countTimeout, TimeUnit.MILLISECONDS); + reload(settings); + } + + public void setLogin(String name) { + latestLogin.add(name); + } + + public boolean shouldSaveLogin(Player player) { + return permissionsManager.hasPermission(player, PlayerPermission.QUICK_COMMANDS_PROTECTION); + } + + public boolean isAllowed(String name) { + return !latestLogin.contains(name); + } + + @Override + public void reload(Settings settings) { + long countTimeout = settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS); + latestLogin.setExpiration(countTimeout, TimeUnit.MILLISECONDS); + } + + @Override + public void performCleanup() { + latestLogin.removeExpiredEntries(); + } +} diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java index 1a89490f2..71ee84b4d 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java @@ -68,7 +68,12 @@ public enum PlayerPermission implements PermissionNode { /** * Permission to use the email verification codes feature. */ - VERIFICATION_CODE("authme.player.security.verificationcode"); + VERIFICATION_CODE("authme.player.security.verificationcode"), + + /** + * Permission to use the email verification codes feature. + */ + QUICK_COMMANDS_PROTECTION("authme.player.protection.quickcommandsprotection"); /** * The permission node. diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index f0a6c2f74..bb665c0f7 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -55,6 +55,10 @@ public final class ProtectionSettings implements SettingsHolder { public static final Property ANTIBOT_DELAY = newProperty("Protection.antiBotDelay", 60); + @Comment("Kicks the player that issued a command before the defined time after the login process") + public static final Property QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS = + newProperty("Protection.quickCommands.denyCommandsBeforeSeconds", 1000); + private ProtectionSettings() { } From 5abc9b9d452c2e39e407086e2b7931e3a2554889 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Sun, 4 Mar 2018 18:36:21 +0100 Subject: [PATCH 047/155] 'session expired' message spam fix --- .../xephi/authme/service/SessionService.java | 24 ++++++++++++------- .../fr/xephi/authme/service/SessionState.java | 13 ++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/service/SessionState.java diff --git a/src/main/java/fr/xephi/authme/service/SessionService.java b/src/main/java/fr/xephi/authme/service/SessionService.java index dd676cc87..87f1bfc7d 100644 --- a/src/main/java/fr/xephi/authme/service/SessionService.java +++ b/src/main/java/fr/xephi/authme/service/SessionService.java @@ -45,11 +45,12 @@ public class SessionService implements Reloadable { database.setUnlogged(name); database.revokeSession(name); PlayerAuth auth = database.getAuth(name); - if (hasValidSessionData(auth, player)) { + SessionState state = hasValidSessionData(auth, player); + if(state.equals(SessionState.VALID)) { RestoreSessionEvent event = bukkitService.createAndCallEvent( isAsync -> new RestoreSessionEvent(player, isAsync)); return !event.isCancelled(); - } else { + } else if(state.equals(SessionState.IP_CHANGED)) { service.send(player, MessageKey.SESSION_EXPIRED); } } @@ -64,17 +65,24 @@ public class SessionService implements Reloadable { * @param player the associated player * @return true if the player may resume his login session, false otherwise */ - private boolean hasValidSessionData(PlayerAuth auth, Player player) { + private SessionState hasValidSessionData(PlayerAuth auth, Player player) { if (auth == null) { ConsoleLogger.warning("No PlayerAuth in database for '" + player.getName() + "' during session check"); - return false; + return SessionState.NOT_VALID; } else if (auth.getLastLogin() == null) { - return false; + return SessionState.NOT_VALID; } long timeSinceLastLogin = System.currentTimeMillis() - auth.getLastLogin(); - return PlayerUtils.getPlayerIp(player).equals(auth.getLastIp()) - && timeSinceLastLogin > 0 - && timeSinceLastLogin < service.getProperty(PluginSettings.SESSIONS_TIMEOUT) * MILLIS_PER_MINUTE; + + if(timeSinceLastLogin > 0 + && timeSinceLastLogin < service.getProperty(PluginSettings.SESSIONS_TIMEOUT) * MILLIS_PER_MINUTE) { + if(PlayerUtils.getPlayerIp(player).equals(auth.getLastIp())) { + return SessionState.VALID; + } else { + return SessionState.IP_CHANGED; + } + } + return SessionState.OUTDATED; } public void grantSession(String name) { diff --git a/src/main/java/fr/xephi/authme/service/SessionState.java b/src/main/java/fr/xephi/authme/service/SessionState.java new file mode 100644 index 000000000..801f36bc8 --- /dev/null +++ b/src/main/java/fr/xephi/authme/service/SessionState.java @@ -0,0 +1,13 @@ +package fr.xephi.authme.service; + +public enum SessionState { + + VALID, + + NOT_VALID, + + OUTDATED, + + IP_CHANGED + +} From 9db38a3bf4a840b5685015dd8e43fb12bc1194c3 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Sun, 4 Mar 2018 18:38:46 +0100 Subject: [PATCH 048/155] SessionService#hasValidSessionData description update --- src/main/java/fr/xephi/authme/service/SessionService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/service/SessionService.java b/src/main/java/fr/xephi/authme/service/SessionService.java index 87f1bfc7d..27f31b850 100644 --- a/src/main/java/fr/xephi/authme/service/SessionService.java +++ b/src/main/java/fr/xephi/authme/service/SessionService.java @@ -63,7 +63,7 @@ public class SessionService implements Reloadable { * * @param auth the player auth * @param player the associated player - * @return true if the player may resume his login session, false otherwise + * @return SessionState based on the state of the session (VALID, NOT_VALID, OUTDATED, IP_CHANGED) */ private SessionState hasValidSessionData(PlayerAuth auth, Player player) { if (auth == null) { From be2c6ae1162f209780ffd4b457d9a577aefb7d17 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Sun, 4 Mar 2018 19:23:08 +0100 Subject: [PATCH 049/155] Fixing tests --- src/main/java/fr/xephi/authme/service/SessionService.java | 1 + .../java/fr/xephi/authme/service/SessionServiceTest.java | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/SessionService.java b/src/main/java/fr/xephi/authme/service/SessionService.java index 27f31b850..f9f43948e 100644 --- a/src/main/java/fr/xephi/authme/service/SessionService.java +++ b/src/main/java/fr/xephi/authme/service/SessionService.java @@ -45,6 +45,7 @@ public class SessionService implements Reloadable { database.setUnlogged(name); database.revokeSession(name); PlayerAuth auth = database.getAuth(name); + SessionState state = hasValidSessionData(auth, player); if(state.equals(SessionState.VALID)) { RestoreSessionEvent event = bukkitService.createAndCallEvent( diff --git a/src/test/java/fr/xephi/authme/service/SessionServiceTest.java b/src/test/java/fr/xephi/authme/service/SessionServiceTest.java index 754487c15..441b53561 100644 --- a/src/test/java/fr/xephi/authme/service/SessionServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/SessionServiceTest.java @@ -106,7 +106,6 @@ public class SessionServiceTest { // then assertThat(result, equalTo(false)); verify(commonService).getProperty(PluginSettings.SESSIONS_ENABLED); - verify(commonService).send(player, MessageKey.SESSION_EXPIRED); verify(dataSource).hasSession(name); verify(dataSource).setUnlogged(name); verify(dataSource).revokeSession(name); @@ -132,7 +131,6 @@ public class SessionServiceTest { // then assertThat(result, equalTo(false)); verify(commonService).getProperty(PluginSettings.SESSIONS_ENABLED); - verify(commonService).send(player, MessageKey.SESSION_EXPIRED); verify(dataSource).hasSession(name); verify(dataSource).setUnlogged(name); verify(dataSource).revokeSession(name); @@ -145,9 +143,10 @@ public class SessionServiceTest { String ip = "127.3.12.15"; Player player = mockPlayerWithNameAndIp(name, ip); given(dataSource.hasSession(name)).willReturn(true); + given(commonService.getProperty(PluginSettings.SESSIONS_TIMEOUT)).willReturn(8); PlayerAuth auth = PlayerAuth.builder() .name(name) - .lastLogin(System.currentTimeMillis()) + .lastLogin(System.currentTimeMillis() - 7 * 60 * 1000) .lastIp("8.8.8.8").build(); given(dataSource.getAuth(name)).willReturn(auth); From 57809194f32f0623c26fbd0ceca96d5886851aa8 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Sun, 4 Mar 2018 19:45:05 +0100 Subject: [PATCH 050/155] Renaming function hasValidSessionData -> fetchSessionStatus --- src/main/java/fr/xephi/authme/service/SessionService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/SessionService.java b/src/main/java/fr/xephi/authme/service/SessionService.java index f9f43948e..d530f5855 100644 --- a/src/main/java/fr/xephi/authme/service/SessionService.java +++ b/src/main/java/fr/xephi/authme/service/SessionService.java @@ -46,7 +46,7 @@ public class SessionService implements Reloadable { database.revokeSession(name); PlayerAuth auth = database.getAuth(name); - SessionState state = hasValidSessionData(auth, player); + SessionState state = fetchSessionStatus(auth, player); if(state.equals(SessionState.VALID)) { RestoreSessionEvent event = bukkitService.createAndCallEvent( isAsync -> new RestoreSessionEvent(player, isAsync)); @@ -66,8 +66,9 @@ public class SessionService implements Reloadable { * @param player the associated player * @return SessionState based on the state of the session (VALID, NOT_VALID, OUTDATED, IP_CHANGED) */ - private SessionState hasValidSessionData(PlayerAuth auth, Player player) { + private SessionState fetchSessionStatus(PlayerAuth auth, Player player) { if (auth == null) { + ConsoleLogger.warning("No PlayerAuth in database for '" + player.getName() + "' during session check"); return SessionState.NOT_VALID; } else if (auth.getLastLogin() == null) { From 00de15016a1eeaba4349fc224c275b66f904b28f Mon Sep 17 00:00:00 2001 From: Maxetto Date: Mon, 5 Mar 2018 19:38:24 +0100 Subject: [PATCH 051/155] Follow remarks #1509 --- .../authme/RegisterAdminCommand.java | 2 +- .../fr/xephi/authme/data/TempbanManager.java | 2 +- .../data/limbo/LimboPlayerTaskManager.java | 4 +- .../xephi/authme/listener/OnJoinVerifier.java | 6 +- .../xephi/authme/listener/PlayerListener.java | 6 +- .../fr/xephi/authme/message/Messages.java | 70 ++++++++++++++----- .../authme/process/join/AsynchronousJoin.java | 4 +- .../process/login/AsynchronousLogin.java | 2 +- .../register/ProcessSyncPasswordRegister.java | 2 +- .../xephi/authme/service/CommonService.java | 4 +- .../authme/RegisterAdminCommandTest.java | 2 +- .../xephi/authme/data/TempbanManagerTest.java | 4 +- .../limbo/LimboPlayerTaskManagerTest.java | 16 ++--- .../authme/listener/OnJoinVerifierTest.java | 6 +- .../authme/listener/PlayerListenerTest.java | 2 +- .../message/MessagesIntegrationTest.java | 47 +++++++++++-- .../authme/service/CommonServiceTest.java | 7 +- .../fr/xephi/authme/message/messages_test.yml | 2 + 18 files changed, 132 insertions(+), 56 deletions(-) diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index 0b88227a1..5c28fdc69 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -74,7 +74,7 @@ public class RegisterAdminCommand implements ExecutableCommand { final Player player = bukkitService.getPlayerExact(playerName); if (player != null) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> - player.kickPlayer(commonService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER))); + player.kickPlayer(commonService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER, player))); } }); } diff --git a/src/main/java/fr/xephi/authme/data/TempbanManager.java b/src/main/java/fr/xephi/authme/data/TempbanManager.java index a09212bde..288864376 100644 --- a/src/main/java/fr/xephi/authme/data/TempbanManager.java +++ b/src/main/java/fr/xephi/authme/data/TempbanManager.java @@ -97,7 +97,7 @@ public class TempbanManager implements SettingsDependent, HasCleanup { if (isEnabled) { final String name = player.getName(); final String ip = PlayerUtils.getPlayerIp(player); - final String reason = messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS); + final String reason = messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS, player); final Date expires = new Date(); long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE); diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java index 9b9373e61..2362b7736 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java @@ -51,7 +51,7 @@ class LimboPlayerTaskManager { int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL); MessageResult messageResult = getMessageKey(player.getName(), isRegistered); if (interval > 0) { - String[] joinMessage = messages.retrieveSingle(messageResult.messageKey, messageResult.args).split("\n"); + String[] joinMessage = messages.retrieveSingle(messageResult.messageKey, player, messageResult.args).split("\n"); MessageTask messageTask = new MessageTask(player, joinMessage); bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND); limbo.setMessageTask(messageTask); @@ -67,7 +67,7 @@ class LimboPlayerTaskManager { void registerTimeoutTask(Player player, LimboPlayer limbo) { final int timeout = settings.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; if (timeout > 0) { - String message = messages.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR); + String message = messages.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR, player); BukkitTask task = bukkitService.runTaskLater(new TimeoutTask(player, message, playerCache), timeout); limbo.setTimeoutTask(task); } diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index ffd008059..c10b8fcc5 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -123,7 +123,7 @@ public class OnJoinVerifier implements Reloadable { return false; } else if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { // Server is full and player is NOT VIP; set kick message and proceed with kick - event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)); + event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)); return true; } @@ -135,12 +135,12 @@ public class OnJoinVerifier implements Reloadable { } Player nonVipPlayer = generateKickPlayer(onlinePlayers); if (nonVipPlayer != null) { - nonVipPlayer.kickPlayer(messages.retrieveSingle(MessageKey.KICK_FOR_VIP)); + nonVipPlayer.kickPlayer(messages.retrieveSingle(MessageKey.KICK_FOR_VIP, player)); event.allow(); return false; } else { ConsoleLogger.info("VIP player " + player.getName() + " tried to join, but the server was full"); - event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)); + event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)); return true; } } diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index b4709a564..4d906c58c 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -21,6 +21,8 @@ import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.ChatColor; import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -257,7 +259,7 @@ public class PlayerListener implements Listener { try { runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress()); } catch (FailedVerificationException e) { - event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); + event.setKickMessage(m.retrieveSingle(e.getReason(), name, e.getArgs())); event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); } } @@ -285,7 +287,7 @@ public class PlayerListener implements Listener { try { runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress()); } catch (FailedVerificationException e) { - event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); + event.setKickMessage(m.retrieveSingle(e.getReason(), player, e.getArgs())); event.setResult(PlayerLoginEvent.Result.KICK_OTHER); } } diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 7cc0ac285..3d9041111 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -18,7 +18,7 @@ public class Messages { // Custom Authme tag replaced to new line private static final String NEWLINE_TAG = "%nl%"; - private static final String PLAYER_TAG = "%username%"; + private static final String USERNAME_TAG = "%username%"; /** Contains the keys of the singular messages for time units. */ private static final Map TIME_UNIT_SINGULARS = ImmutableMap.builder() @@ -51,9 +51,9 @@ public class Messages { * @param key The key of the message to send */ public void send(CommandSender sender, MessageKey key) { - String[] lines = retrieve(key); + String[] lines = retrieve(key, sender); for (String line : lines) { - sender.sendMessage(line.replaceAll(PLAYER_TAG, sender.getName())); + sender.sendMessage(line); } } @@ -67,7 +67,7 @@ public class Messages { * @param replacements The replacements to apply for the tags */ public void send(CommandSender sender, MessageKey key, String... replacements) { - String message = retrieveSingle(key, replacements).replaceAll(PLAYER_TAG, sender.getName()); + String message = retrieveSingle(key, sender, replacements); for (String line : message.split("\n")) { sender.sendMessage(line); } @@ -77,10 +77,11 @@ public class Messages { * Retrieve the message from the text file and return it split by new line as an array. * * @param key The message key to retrieve + * @param sender The entity to send the message to * @return The message split by new lines */ - public String[] retrieve(MessageKey key) { - String message = retrieveMessage(key); + public String[] retrieve(MessageKey key, CommandSender sender) { + String message = retrieveMessage(key, sender); if (message.isEmpty()) { // Return empty array instead of array with 1 empty string as entry return new String[0]; @@ -101,18 +102,37 @@ public class Messages { ? TIME_UNIT_SINGULARS.get(duration.getTimeUnit()) : TIME_UNIT_PLURALS.get(duration.getTimeUnit()); - return value + " " + retrieveMessage(timeUnitKey); + return value + " " + retrieveMessage(timeUnitKey, ""); } /** * Retrieve the message from the text file. * * @param key The message key to retrieve + * @param sender The entity to send the message to * @return The message from the file */ - private String retrieveMessage(MessageKey key) { - return formatMessage( - messagesFileHandler.getMessage(key.getKey())); + private String retrieveMessage(MessageKey key, CommandSender sender) { + String message = messagesFileHandler.getMessage(key.getKey()); + + return ChatColor.translateAlternateColorCodes('&', message) + .replace(NEWLINE_TAG, "\n") + .replace(USERNAME_TAG, sender.getName()); + } + + /** + * Retrieve the message from the text file. + * + * @param key The message key to retrieve + * @param name The name of the entity to send the message to + * @return The message from the file + */ + private String retrieveMessage(MessageKey key, String name) { + String message = messagesFileHandler.getMessage(key.getKey()); + + return ChatColor.translateAlternateColorCodes('&', message) + .replace(NEWLINE_TAG, "\n") + .replace(USERNAME_TAG, name); } /** @@ -121,11 +141,12 @@ public class Messages { * the message key contains. * * @param key The key of the message to send + * @param sender The entity to send the message to * @param replacements The replacements to apply for the tags * @return The message from the file with replacements */ - public String retrieveSingle(MessageKey key, String... replacements) { - String message = retrieveMessage(key); + public String retrieveSingle(MessageKey key, CommandSender sender, String... replacements) { + String message = retrieveMessage(key, sender); String[] tags = key.getTags(); if (replacements.length == tags.length) { for (int i = 0; i < tags.length; ++i) { @@ -137,9 +158,26 @@ public class Messages { return message; } - private static String formatMessage(String message) { - return ChatColor.translateAlternateColorCodes('&', message) - .replace(NEWLINE_TAG, "\n"); + /** + * Retrieve the given message code with the given tag replacements. Note that this method + * logs an error if the number of supplied replacements doesn't correspond to the number of tags + * the message key contains. + * + * @param key The key of the message to send + * @param name The name of the entity to send the message to + * @param replacements The replacements to apply for the tags + * @return The message from the file with replacements + */ + public String retrieveSingle(MessageKey key, String name, String... replacements) { + String message = retrieveMessage(key, name); + String[] tags = key.getTags(); + if (replacements.length == tags.length) { + for (int i = 0; i < tags.length; ++i) { + message = message.replace(tags[i], replacements[i]); + } + } else { + ConsoleLogger.warning("Invalid number of replacements for message key '" + key + "'"); + } + return message; } - } diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index 6cb16a674..da9e5127d 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -138,7 +138,7 @@ public class AsynchronousJoin implements AsynchronousProcess { private void handlePlayerWithUnmetNameRestriction(Player player, String ip) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { - player.kickPlayer(service.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR)); + player.kickPlayer(service.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR, player)); if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) { server.banIP(ip); } @@ -188,7 +188,7 @@ public class AsynchronousJoin implements AsynchronousProcess { && countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( - () -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE))); + () -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE, player))); return false; } return true; diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index c52202ef4..2731c59a1 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -197,7 +197,7 @@ public class AsynchronousLogin implements AsynchronousProcess { tempbanManager.tempbanPlayer(player); } else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( - () -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD))); + () -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD, player))); } else { service.send(player, MessageKey.WRONG_PASSWORD); diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index 589c88aa2..f59bb28b1 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -58,7 +58,7 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { // Kick Player after Registration is enabled, kick the player if (service.getProperty(RegistrationSettings.FORCE_KICK_AFTER_REGISTER)) { - player.kickPlayer(service.retrieveSingleMessage(MessageKey.REGISTER_SUCCESS)); + player.kickPlayer(service.retrieveSingleMessage(MessageKey.REGISTER_SUCCESS, player)); return; } diff --git a/src/main/java/fr/xephi/authme/service/CommonService.java b/src/main/java/fr/xephi/authme/service/CommonService.java index 2422b1fa1..c95171b1c 100644 --- a/src/main/java/fr/xephi/authme/service/CommonService.java +++ b/src/main/java/fr/xephi/authme/service/CommonService.java @@ -66,8 +66,8 @@ public class CommonService { * @param key the key of the message * @return the message */ - public String retrieveSingleMessage(MessageKey key) { - return messages.retrieveSingle(key); + public String retrieveSingleMessage(MessageKey key, CommandSender sender) { + return messages.retrieveSingle(key, sender); } /** diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java index ce52ece6b..162de38ac 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java @@ -160,7 +160,7 @@ public class RegisterAdminCommandTest { Player player = mock(Player.class); given(bukkitService.getPlayerExact(user)).willReturn(player); String kickForAdminRegister = "Admin registered you -- log in again"; - given(commandService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister); + given(commandService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER, player)).willReturn(kickForAdminRegister); CommandSender sender = mock(CommandSender.class); setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); setBukkitServiceToRunTaskOptionallyAsync(bukkitService); diff --git a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java b/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java index e5fb70429..5b1da8c7a 100644 --- a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java @@ -149,7 +149,7 @@ public class TempbanManagerTest { String ip = "123.45.67.89"; TestHelper.mockPlayerIp(player, ip); String banReason = "IP ban too many logins"; - given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); + given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS, player)).willReturn(banReason); Settings settings = mockSettings(2, 100, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); setBukkitServiceToScheduleSyncDelayedTask(bukkitService); @@ -195,7 +195,7 @@ public class TempbanManagerTest { String ip = "22.44.66.88"; TestHelper.mockPlayerIp(player, ip); String banReason = "kick msg"; - given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); + given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS, player)).willReturn(banReason); Settings settings = mockSettings(10, 60, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); manager.increaseCount(ip, "user"); diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java index 9a8d876ca..79fa4f4dc 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java @@ -71,7 +71,7 @@ public class LimboPlayerTaskManagerTest { Player player = mock(Player.class); LimboPlayer limboPlayer = mock(LimboPlayer.class); MessageKey key = MessageKey.REGISTER_MESSAGE; - given(messages.retrieveSingle(key)).willReturn("Please register!"); + given(messages.retrieveSingle(key, player)).willReturn("Please register!"); int interval = 12; given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(interval); @@ -80,7 +80,7 @@ public class LimboPlayerTaskManagerTest { // then verify(limboPlayer).setMessageTask(any(MessageTask.class)); - verify(messages).retrieveSingle(key); + verify(messages).retrieveSingle(key, player); verify(bukkitService).runTaskTimer( any(MessageTask.class), eq(2L * TICKS_PER_SECOND), eq((long) interval * TICKS_PER_SECOND)); } @@ -110,7 +110,7 @@ public class LimboPlayerTaskManagerTest { MessageTask existingMessageTask = mock(MessageTask.class); limboPlayer.setMessageTask(existingMessageTask); given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(8); - given(messages.retrieveSingle(MessageKey.REGISTER_MESSAGE)).willReturn("Please register!"); + given(messages.retrieveSingle(MessageKey.REGISTER_MESSAGE, player)).willReturn("Please register!"); // when limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); @@ -119,7 +119,7 @@ public class LimboPlayerTaskManagerTest { assertThat(limboPlayer.getMessageTask(), not(nullValue())); assertThat(limboPlayer.getMessageTask(), not(sameInstance(existingMessageTask))); verify(registrationCaptchaManager).isCaptchaRequired(name); - verify(messages).retrieveSingle(MessageKey.REGISTER_MESSAGE); + verify(messages).retrieveSingle(MessageKey.REGISTER_MESSAGE, player); verify(existingMessageTask).cancel(); } @@ -134,14 +134,14 @@ public class LimboPlayerTaskManagerTest { given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true); String captcha = "M032"; given(registrationCaptchaManager.getCaptchaCodeOrGenerateNew(name)).willReturn(captcha); - given(messages.retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha)).willReturn("Need to use captcha"); + given(messages.retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, player, captcha)).willReturn("Need to use captcha"); // when limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); // then assertThat(limboPlayer.getMessageTask(), not(nullValue())); - verify(messages).retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha); + verify(messages).retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, player, captcha); } @Test @@ -159,7 +159,7 @@ public class LimboPlayerTaskManagerTest { // then verify(limboPlayer).setTimeoutTask(bukkitTask); verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(600L)); // 30 * TICKS_PER_SECOND - verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR); + verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR, player); } @Test @@ -194,7 +194,7 @@ public class LimboPlayerTaskManagerTest { verify(existingTask).cancel(); assertThat(limboPlayer.getTimeoutTask(), equalTo(bukkitTask)); verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(360L)); // 18 * TICKS_PER_SECOND - verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR); + verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR, player); } } diff --git a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java index 7ea3085a6..7d8c95176 100644 --- a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java +++ b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java @@ -101,7 +101,7 @@ public class OnJoinVerifierTest { event.setResult(PlayerLoginEvent.Result.KICK_FULL); given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(false); String serverFullMessage = "server is full"; - given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)).willReturn(serverFullMessage); + given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)).willReturn(serverFullMessage); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); @@ -125,7 +125,7 @@ public class OnJoinVerifierTest { given(permissionsManager.hasPermission(onlinePlayers.get(1), PlayerStatePermission.IS_VIP)).willReturn(false); returnOnlineListFromBukkitServer(onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); - given(messages.retrieveSingle(MessageKey.KICK_FOR_VIP)).willReturn("kick for vip"); + given(messages.retrieveSingle(MessageKey.KICK_FOR_VIP, player)).willReturn("kick for vip"); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); @@ -149,7 +149,7 @@ public class OnJoinVerifierTest { given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true); returnOnlineListFromBukkitServer(onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); - given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)).willReturn("kick full server"); + given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)).willReturn("kick full server"); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index d4204315c..9fd750ff7 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -596,7 +596,7 @@ public class PlayerListenerTest { MessageKey.INVALID_NAME_CHARACTERS, "[a-z]"); doThrow(exception).when(onJoinVerifier).checkIsValidName(name); String message = "Invalid characters!"; - given(messages.retrieveSingle(exception.getReason(), exception.getArgs())).willReturn(message); + given(messages.retrieveSingle(exception.getReason(), player, exception.getArgs())).willReturn(message); // when listener.onPlayerLogin(event); diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 30af10773..69bbfe5d2 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -81,9 +81,11 @@ public class MessagesIntegrationTest { public void shouldLoadMessageAndSplitAtNewLines() { // given MessageKey key = MessageKey.UNKNOWN_USER; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when - String[] message = messages.retrieve(key); + String[] message = messages.retrieve(key, sender); // then String[] lines = new String[]{"We've got", "new lines", "and ' apostrophes"}; @@ -94,9 +96,11 @@ public class MessagesIntegrationTest { public void shouldLoadMessageAsStringWithNewLines() { // given MessageKey key = MessageKey.UNKNOWN_USER; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when - String message = messages.retrieveSingle(key); + String message = messages.retrieveSingle(key, sender); // then assertThat(message, equalTo("We've got\nnew lines\nand ' apostrophes")); @@ -106,9 +110,11 @@ public class MessagesIntegrationTest { public void shouldFormatColorCodes() { // given MessageKey key = MessageKey.LOGIN_SUCCESS; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when - String[] message = messages.retrieve(key); + String[] message = messages.retrieve(key, sender); // then assertThat(message, arrayWithSize(1)); @@ -120,6 +126,7 @@ public class MessagesIntegrationTest { // given MessageKey key = MessageKey.EMAIL_ALREADY_USED_ERROR; CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when messages.send(sender, key); @@ -133,6 +140,7 @@ public class MessagesIntegrationTest { // given MessageKey key = MessageKey.LOGIN_SUCCESS; Player player = Mockito.mock(Player.class); + given(player.getName()).willReturn("Tester"); // when messages.send(player, key); @@ -146,6 +154,7 @@ public class MessagesIntegrationTest { // given MessageKey key = MessageKey.UNKNOWN_USER; Player player = Mockito.mock(Player.class); + given(player.getName()).willReturn("Tester"); // when messages.send(player, key); @@ -157,11 +166,26 @@ public class MessagesIntegrationTest { assertThat(captor.getAllValues(), contains(lines)); } + @Test + public void shouldSendMessageToPlayerWithNameReplacement() { + // given + MessageKey key = MessageKey.REGISTER_MESSAGE; + Player player = Mockito.mock(Player.class); + given(player.getName()).willReturn("Tester"); + + // when + messages.send(player, key); + + // then + verify(player).sendMessage("§3Please Tester, register to the server with the command: /register "); + } + @Test public void shouldSendMessageToPlayerWithTagReplacement() { // given MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; CommandSender sender = Mockito.mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when messages.send(sender, key, "1234"); @@ -175,6 +199,7 @@ public class MessagesIntegrationTest { // given MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when messages.send(sender, key); @@ -189,9 +214,11 @@ public class MessagesIntegrationTest { Logger logger = mock(Logger.class); ConsoleLogger.setLogger(logger); MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when - messages.send(mock(CommandSender.class), key, "rep", "rep2"); + messages.send(sender, key, "rep", "rep2"); // then verify(logger).warning(argThat(containsString("Invalid number of replacements"))); @@ -203,9 +230,11 @@ public class MessagesIntegrationTest { Logger logger = mock(Logger.class); ConsoleLogger.setLogger(logger); MessageKey key = MessageKey.UNKNOWN_USER; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when - messages.send(mock(CommandSender.class), key, "Replacement"); + messages.send(sender, key, "Replacement"); // then verify(logger).warning(argThat(containsString("Invalid number of replacements"))); @@ -216,9 +245,11 @@ public class MessagesIntegrationTest { // given // Key is present in both files MessageKey key = MessageKey.WRONG_PASSWORD; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when - String message = messages.retrieveSingle(key); + String message = messages.retrieveSingle(key, sender); // then assertThat(message, equalTo("§cWrong password!")); @@ -228,9 +259,11 @@ public class MessagesIntegrationTest { public void shouldRetrieveMessageWithReplacements() { // given MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn("Tester"); // when - String result = messages.retrieveSingle(key, "24680"); + String result = messages.retrieveSingle(key, sender.getName(), "24680"); // then assertThat(result, equalTo("Use /captcha 24680 to solve the captcha")); diff --git a/src/test/java/fr/xephi/authme/service/CommonServiceTest.java b/src/test/java/fr/xephi/authme/service/CommonServiceTest.java index 87cba2005..e4ff2817b 100644 --- a/src/test/java/fr/xephi/authme/service/CommonServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/CommonServiceTest.java @@ -83,15 +83,16 @@ public class CommonServiceTest { public void shouldRetrieveSingleMessage() { // given MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; + Player player = mock(Player.class); String text = "Test text"; - given(messages.retrieveSingle(key)).willReturn(text); + given(messages.retrieveSingle(key, player)).willReturn(text); // when - String result = commonService.retrieveSingleMessage(key); + String result = commonService.retrieveSingleMessage(key, player); // then assertThat(result, equalTo(text)); - verify(messages).retrieveSingle(key); + verify(messages).retrieveSingle(key, player); } @Test diff --git a/src/test/resources/fr/xephi/authme/message/messages_test.yml b/src/test/resources/fr/xephi/authme/message/messages_test.yml index b7670dc2d..86b38986e 100644 --- a/src/test/resources/fr/xephi/authme/message/messages_test.yml +++ b/src/test/resources/fr/xephi/authme/message/messages_test.yml @@ -2,6 +2,8 @@ error: unregistered_user: 'We''ve got%nl%new lines%nl%and '' apostrophes' +registration: + register_request: '&3Please %username%, register to the server with the command: /register ' login: success: '&cHere we have&bdefined some colors &dand some other <hings' wrong_password: '&cWrong password!' From f9d1a3aef2a144fce8c7c26b9f7146bca2a36414 Mon Sep 17 00:00:00 2001 From: Maxetto Date: Mon, 5 Mar 2018 19:42:43 +0100 Subject: [PATCH 052/155] Those were not meant to be there --- src/main/java/fr/xephi/authme/listener/PlayerListener.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 4d906c58c..6482cd637 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -21,8 +21,6 @@ import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.ChatColor; import org.bukkit.Location; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; From 9954c82cb6cbf11ec24a7fe21359eca3ba9bac07 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 5 Mar 2018 19:50:58 +0100 Subject: [PATCH 053/155] #1141 Add TOTP key field to database and PlayerAuth - Add new field for storing TOTP key - Implement data source methods for manipulation of its value --- .../fr/xephi/authme/data/auth/PlayerAuth.java | 12 +++++++ .../authme/datasource/CacheDataSource.java | 9 ++++++ .../fr/xephi/authme/datasource/Columns.java | 2 ++ .../xephi/authme/datasource/DataSource.java | 19 ++++++++++++ .../fr/xephi/authme/datasource/FlatFile.java | 5 +++ .../fr/xephi/authme/datasource/MySQL.java | 20 ++++++++++++ .../fr/xephi/authme/datasource/SQLite.java | 21 +++++++++++++ .../settings/properties/DatabaseSettings.java | 4 +++ .../AbstractDataSourceIntegrationTest.java | 31 +++++++++++++++++++ .../authme/datasource/sql-initialize.sql | 5 +-- 10 files changed, 126 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java b/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java index 4c8b8ee30..534c0c019 100644 --- a/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java +++ b/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java @@ -26,6 +26,7 @@ public class PlayerAuth { /** The player's name in the correct casing, e.g. "Xephi". */ private String realName; private HashedPassword password; + private String totpKey; private String email; private String lastIp; private int groupId; @@ -160,6 +161,10 @@ public class PlayerAuth { this.registrationDate = registrationDate; } + public String getTotpKey() { + return totpKey; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof PlayerAuth)) { @@ -195,6 +200,7 @@ public class PlayerAuth { private String name; private String realName; private HashedPassword password; + private String totpKey; private String lastIp; private String email; private int groupId = -1; @@ -219,6 +225,7 @@ public class PlayerAuth { auth.nickname = checkNotNull(name).toLowerCase(); auth.realName = firstNonNull(realName, "Player"); auth.password = firstNonNull(password, new HashedPassword("")); + auth.totpKey = totpKey; auth.email = DB_EMAIL_DEFAULT.equals(email) ? null : email; auth.lastIp = lastIp; // Don't check against default value 127.0.0.1 as it may be a legit value auth.groupId = groupId; @@ -258,6 +265,11 @@ public class PlayerAuth { return password(new HashedPassword(hash, salt)); } + public Builder totpKey(String totpKey) { + this.totpKey = totpKey; + return this; + } + public Builder lastIp(String lastIp) { this.lastIp = lastIp; return this; diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 39f04a53c..0926def86 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -268,6 +268,15 @@ public class CacheDataSource implements DataSource { return source.getRecentlyLoggedInPlayers(); } + @Override + public boolean setTotpKey(String user, String totpKey) { + boolean result = source.setTotpKey(user, totpKey); + if (result) { + cachedAuths.refresh(user); + } + return result; + } + @Override public void invalidateCache(String playerName) { cachedAuths.invalidate(playerName); diff --git a/src/main/java/fr/xephi/authme/datasource/Columns.java b/src/main/java/fr/xephi/authme/datasource/Columns.java index 946c33de6..0d372a239 100644 --- a/src/main/java/fr/xephi/authme/datasource/Columns.java +++ b/src/main/java/fr/xephi/authme/datasource/Columns.java @@ -14,6 +14,7 @@ public final class Columns { public final String REAL_NAME; public final String PASSWORD; public final String SALT; + public final String TOTP_KEY; public final String LAST_IP; public final String LAST_LOGIN; public final String GROUP; @@ -35,6 +36,7 @@ public final class Columns { REAL_NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_REALNAME); PASSWORD = settings.getProperty(DatabaseSettings.MYSQL_COL_PASSWORD); SALT = settings.getProperty(DatabaseSettings.MYSQL_COL_SALT); + TOTP_KEY = settings.getProperty(DatabaseSettings.MYSQL_COL_TOTP_KEY); LAST_IP = settings.getProperty(DatabaseSettings.MYSQL_COL_LAST_IP); LAST_LOGIN = settings.getProperty(DatabaseSettings.MYSQL_COL_LASTLOGIN); GROUP = settings.getProperty(DatabaseSettings.MYSQL_COL_GROUP); diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index 6f97951db..b6e09fff9 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -232,6 +232,25 @@ public interface DataSource extends Reloadable { */ List getRecentlyLoggedInPlayers(); + /** + * Sets the given TOTP key to the player's account. + * + * @param user the name of the player to modify + * @param totpKey the totp key to set + * @return True upon success, false upon failure + */ + boolean setTotpKey(String user, String totpKey); + + /** + * Removes the TOTP key if present of the given player's account. + * + * @param user the name of the player to modify + * @return True upon success, false upon failure + */ + default boolean removeTotpKey(String user) { + return setTotpKey(user, null); + } + /** * Reload the data source. */ diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index d234da556..f70b0376f 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -398,6 +398,11 @@ public class FlatFile implements DataSource { throw new UnsupportedOperationException("Flat file no longer supported"); } + @Override + public boolean setTotpKey(String user, String totpKey) { + throw new UnsupportedOperationException("Flat file no longer supported"); + } + /** * Creates a PlayerAuth object from the read data. * diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 01a240fe3..93014edc3 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -248,6 +248,11 @@ public class MySQL implements DataSource { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.HAS_SESSION + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.IS_LOGGED); } + + if (isColumnMissing(md, col.TOTP_KEY)) { + st.executeUpdate("ALTER TABLE " + tableName + + " ADD COLUMN " + col.TOTP_KEY + " VARCHAR(16);"); + } } ConsoleLogger.info("MySQL setup finished"); } @@ -728,6 +733,20 @@ public class MySQL implements DataSource { return players; } + @Override + public boolean setTotpKey(String user, String totpKey) { + String sql = "UPDATE " + tableName + " SET " + col.TOTP_KEY + " = ? WHERE " + col.NAME + " = ?"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { + pst.setString(1, totpKey); + pst.setString(2, user.toLowerCase()); + pst.executeUpdate(); + return true; + } catch (SQLException e) { + logSqlException(e); + } + return false; + } + private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT); int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP); @@ -735,6 +754,7 @@ public class MySQL implements DataSource { .name(row.getString(col.NAME)) .realName(row.getString(col.REAL_NAME)) .password(row.getString(col.PASSWORD), salt) + .totpKey(row.getString(col.TOTP_KEY)) .lastLogin(getNullableLong(row, col.LAST_LOGIN)) .lastIp(row.getString(col.LAST_IP)) .email(row.getString(col.EMAIL)) diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 422d57b17..df46f9301 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -171,6 +171,11 @@ public class SQLite implements DataSource { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.HAS_SESSION + " INT NOT NULL DEFAULT '0';"); } + + if (isColumnMissing(md, col.TOTP_KEY)) { + st.executeUpdate("ALTER TABLE " + tableName + + " ADD COLUMN " + col.TOTP_KEY + " VARCHAR(16);"); + } } ConsoleLogger.info("SQLite Setup finished"); } @@ -654,6 +659,21 @@ public class SQLite implements DataSource { return players; } + + @Override + public boolean setTotpKey(String user, String totpKey) { + String sql = "UPDATE " + tableName + " SET " + col.TOTP_KEY + " = ? WHERE " + col.NAME + " = ?"; + try (PreparedStatement pst = con.prepareStatement(sql)) { + pst.setString(1, totpKey); + pst.setString(2, user.toLowerCase()); + pst.executeUpdate(); + return true; + } catch (SQLException e) { + logSqlException(e); + } + return false; + } + private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null; @@ -662,6 +682,7 @@ public class SQLite implements DataSource { .email(row.getString(col.EMAIL)) .realName(row.getString(col.REAL_NAME)) .password(row.getString(col.PASSWORD), salt) + .totpKey(row.getString(col.TOTP_KEY)) .lastLogin(getNullableLong(row, col.LAST_LOGIN)) .lastIp(row.getString(col.LAST_IP)) .registrationDate(row.getLong(col.REGISTRATION_DATE)) diff --git a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java index 66ddd3cd5..8ebccf941 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java @@ -79,6 +79,10 @@ public final class DatabaseSettings implements SettingsHolder { public static final Property MYSQL_COL_HASSESSION = newProperty("DataSource.mySQLColumnHasSession", "hasSession"); + @Comment("Column for storing a player's TOTP key (for two-factor authentication)") + public static final Property MYSQL_COL_TOTP_KEY = + newProperty("DataSource.mySQLtotpKey", "totp"); + @Comment("Column for storing the player's last IP") public static final Property MYSQL_COL_LAST_IP = newProperty("DataSource.mySQLColumnIp", "ip"); diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index 530ab56be..0e8094825 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -103,12 +103,14 @@ public abstract class AbstractDataSourceIntegrationTest { assertThat(bobbyAuth, hasRegistrationInfo("127.0.4.22", 1436778723L)); assertThat(bobbyAuth.getLastLogin(), equalTo(1449136800L)); assertThat(bobbyAuth.getPassword(), equalToHash("$SHA$11aa0706173d7272$dbba966")); + assertThat(bobbyAuth.getTotpKey(), equalTo("JBSWY3DPEHPK3PXP")); assertThat(userAuth, hasAuthBasicData("user", "user", "user@example.org", "34.56.78.90")); assertThat(userAuth, hasAuthLocation(124.1, 76.3, -127.8, "nether", 0.23f, 4.88f)); assertThat(userAuth, hasRegistrationInfo(null, 0)); assertThat(userAuth.getLastLogin(), equalTo(1453242857L)); assertThat(userAuth.getPassword(), equalToHash("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32")); + assertThat(userAuth.getTotpKey(), nullValue()); } @Test @@ -494,4 +496,33 @@ public abstract class AbstractDataSourceIntegrationTest { contains("user24", "user20", "user22", "user29", "user28", "user16", "user18", "user12", "user14", "user11")); } + + @Test + public void shouldSetTotpKey() { + // given + DataSource dataSource = getDataSource(); + String newTotpKey = "My new TOTP key"; + + // when + dataSource.setTotpKey("BObBy", newTotpKey); + dataSource.setTotpKey("does-not-exist", "bogus"); + + // then + assertThat(dataSource.getAuth("bobby").getTotpKey(), equalTo(newTotpKey)); + } + + @Test + public void shouldRemoveTotpKey() { + // given + DataSource dataSource = getDataSource(); + + // when + dataSource.removeTotpKey("BoBBy"); + dataSource.removeTotpKey("user"); + dataSource.removeTotpKey("does-not-exist"); + + // then + assertThat(dataSource.getAuth("bobby").getTotpKey(), nullValue()); + assertThat(dataSource.getAuth("user").getTotpKey(), nullValue()); + } } diff --git a/src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql b/src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql index 306df4769..f8153d0d2 100644 --- a/src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql +++ b/src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql @@ -4,6 +4,7 @@ CREATE TABLE authme ( id INTEGER AUTO_INCREMENT, username VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, + totp VARCHAR(16), ip VARCHAR(40), lastlogin BIGINT, regdate BIGINT NOT NULL, @@ -22,7 +23,7 @@ CREATE TABLE authme ( CONSTRAINT table_const_prim PRIMARY KEY (id) ); -INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, yaw, pitch, email, isLogged, realname, salt, regdate, regip) -VALUES (1,'bobby','$SHA$11aa0706173d7272$dbba966','123.45.67.89',1449136800,1.05,2.1,4.2,'world',-0.44,2.77,'your@email.com',0,'Bobby',NULL,1436778723,'127.0.4.22'); +INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, yaw, pitch, email, isLogged, realname, salt, regdate, regip, totp) +VALUES (1,'bobby','$SHA$11aa0706173d7272$dbba966','123.45.67.89',1449136800,1.05,2.1,4.2,'world',-0.44,2.77,'your@email.com',0,'Bobby',NULL,1436778723,'127.0.4.22','JBSWY3DPEHPK3PXP'); INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, yaw, pitch, email, isLogged, realname, salt, regdate) VALUES (NULL,'user','b28c32f624a4eb161d6adc9acb5bfc5b','34.56.78.90',1453242857,124.1,76.3,-127.8,'nether',0.23,4.88,'user@example.org',0,'user','f750ba32',0); From 11d039c818861d2bb3f83f188d0b464998051e23 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Mon, 5 Mar 2018 20:20:25 +0100 Subject: [PATCH 054/155] space after 'if'(s) --- .../java/fr/xephi/authme/service/SessionService.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/SessionService.java b/src/main/java/fr/xephi/authme/service/SessionService.java index d530f5855..f821a64b3 100644 --- a/src/main/java/fr/xephi/authme/service/SessionService.java +++ b/src/main/java/fr/xephi/authme/service/SessionService.java @@ -47,11 +47,11 @@ public class SessionService implements Reloadable { PlayerAuth auth = database.getAuth(name); SessionState state = fetchSessionStatus(auth, player); - if(state.equals(SessionState.VALID)) { + if (state.equals(SessionState.VALID)) { RestoreSessionEvent event = bukkitService.createAndCallEvent( isAsync -> new RestoreSessionEvent(player, isAsync)); return !event.isCancelled(); - } else if(state.equals(SessionState.IP_CHANGED)) { + } else if (state.equals(SessionState.IP_CHANGED)) { service.send(player, MessageKey.SESSION_EXPIRED); } } @@ -68,7 +68,6 @@ public class SessionService implements Reloadable { */ private SessionState fetchSessionStatus(PlayerAuth auth, Player player) { if (auth == null) { - ConsoleLogger.warning("No PlayerAuth in database for '" + player.getName() + "' during session check"); return SessionState.NOT_VALID; } else if (auth.getLastLogin() == null) { @@ -76,9 +75,9 @@ public class SessionService implements Reloadable { } long timeSinceLastLogin = System.currentTimeMillis() - auth.getLastLogin(); - if(timeSinceLastLogin > 0 + if (timeSinceLastLogin > 0 && timeSinceLastLogin < service.getProperty(PluginSettings.SESSIONS_TIMEOUT) * MILLIS_PER_MINUTE) { - if(PlayerUtils.getPlayerIp(player).equals(auth.getLastIp())) { + if (PlayerUtils.getPlayerIp(player).equals(auth.getLastIp())) { return SessionState.VALID; } else { return SessionState.IP_CHANGED; From 844bd2422167104b7e09d8b02230affea9f1f6a5 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Mon, 5 Mar 2018 21:00:24 +0100 Subject: [PATCH 055/155] Fixed minor code issues --- .../fr/xephi/authme/data/QuickCommandsProtectionManager.java | 2 +- .../java/fr/xephi/authme/permission/PlayerPermission.java | 2 +- .../xephi/authme/settings/properties/ProtectionSettings.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java index 38a007f32..692c1dbc5 100644 --- a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java +++ b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java @@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit; public class QuickCommandsProtectionManager implements SettingsDependent, HasCleanup { - private PermissionsManager permissionsManager; + private final PermissionsManager permissionsManager; private final ExpiringSet latestLogin; diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java index 71ee84b4d..f95fd09ba 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java @@ -71,7 +71,7 @@ public enum PlayerPermission implements PermissionNode { VERIFICATION_CODE("authme.player.security.verificationcode"), /** - * Permission to use the email verification codes feature. + * Permission that enables on join quick commands checks for the player. */ QUICK_COMMANDS_PROTECTION("authme.player.protection.quickcommandsprotection"); diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index bb665c0f7..c7d0f4a82 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -55,9 +55,9 @@ public final class ProtectionSettings implements SettingsHolder { public static final Property ANTIBOT_DELAY = newProperty("Protection.antiBotDelay", 60); - @Comment("Kicks the player that issued a command before the defined time after the login process") + @Comment("Kicks the player that issued a command before the defined time after the join process") public static final Property QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS = - newProperty("Protection.quickCommands.denyCommandsBeforeSeconds", 1000); + newProperty("Protection.quickCommands.denyCommandsBeforeMilliseconds", 1000); private ProtectionSettings() { } From 9b7139a17a0df1030f4cda32c8bf170d74ffb909 Mon Sep 17 00:00:00 2001 From: Maxetto Date: Wed, 7 Mar 2018 19:08:36 +0100 Subject: [PATCH 056/155] Add Display Name tag replacement too #1509 --- .../fr/xephi/authme/message/Messages.java | 13 +++++++-- src/main/resources/messages/messages_bg.yml | 5 ++++ src/main/resources/messages/messages_br.yml | 4 +++ src/main/resources/messages/messages_cz.yml | 5 ++++ src/main/resources/messages/messages_de.yml | 5 ++++ src/main/resources/messages/messages_en.yml | 5 ++++ src/main/resources/messages/messages_eo.yml | 5 ++++ src/main/resources/messages/messages_es.yml | 4 +++ src/main/resources/messages/messages_et.yml | 5 ++++ src/main/resources/messages/messages_eu.yml | 5 ++++ src/main/resources/messages/messages_fi.yml | 5 ++++ src/main/resources/messages/messages_fr.yml | 5 +++- src/main/resources/messages/messages_gl.yml | 5 ++++ src/main/resources/messages/messages_hu.yml | 5 ++++ src/main/resources/messages/messages_id.yml | 5 ++++ src/main/resources/messages/messages_it.yml | 28 +++++++++++-------- src/main/resources/messages/messages_ko.yml | 4 +++ src/main/resources/messages/messages_lt.yml | 5 ++++ src/main/resources/messages/messages_nl.yml | 5 ++++ src/main/resources/messages/messages_pl.yml | 5 ++++ src/main/resources/messages/messages_pt.yml | 5 ++++ src/main/resources/messages/messages_ro.yml | 5 ++++ src/main/resources/messages/messages_ru.yml | 5 ++++ src/main/resources/messages/messages_sk.yml | 4 +++ src/main/resources/messages/messages_tr.yml | 5 ++++ src/main/resources/messages/messages_uk.yml | 5 ++++ src/main/resources/messages/messages_vn.yml | 5 ++++ src/main/resources/messages/messages_zhcn.yml | 5 ++++ src/main/resources/messages/messages_zhhk.yml | 4 +++ src/main/resources/messages/messages_zhmc.yml | 5 ++++ src/main/resources/messages/messages_zhtw.yml | 4 +++ 31 files changed, 165 insertions(+), 15 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 3d9041111..4439a1b7d 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -5,6 +5,7 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.util.expiring.Duration; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import javax.inject.Inject; import java.util.Map; @@ -18,7 +19,9 @@ public class Messages { // Custom Authme tag replaced to new line private static final String NEWLINE_TAG = "%nl%"; + // Global tag replacements private static final String USERNAME_TAG = "%username%"; + private static final String DISPLAYNAME_TAG = "%displayname%"; /** Contains the keys of the singular messages for time units. */ private static final Map TIME_UNIT_SINGULARS = ImmutableMap.builder() @@ -114,10 +117,15 @@ public class Messages { */ private String retrieveMessage(MessageKey key, CommandSender sender) { String message = messagesFileHandler.getMessage(key.getKey()); + String displayName = sender.getName(); + if (sender instanceof Player) { + displayName = ((Player) sender).getDisplayName(); + } return ChatColor.translateAlternateColorCodes('&', message) .replace(NEWLINE_TAG, "\n") - .replace(USERNAME_TAG, sender.getName()); + .replace(USERNAME_TAG, sender.getName()) + .replace(DISPLAYNAME_TAG, displayName); } /** @@ -132,7 +140,8 @@ public class Messages { return ChatColor.translateAlternateColorCodes('&', message) .replace(NEWLINE_TAG, "\n") - .replace(USERNAME_TAG, name); + .replace(USERNAME_TAG, name) + .replace(DISPLAYNAME_TAG, name); } /** diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index ee21f3f57..c49e2cb00 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cРегистрациите са изключени!' diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 05d8aa6f8..3000840bb 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -1,6 +1,10 @@ #Tradução pt/br Authme Reloaded #Feito por GabrielDev(DeathRush) e Frani (PotterCraft_) # http://gamersboard.com.br/ | www.magitechserver.com +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. # Registration registration: diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index 0b0e9e02a..3b97a5aec 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cRegistrace je zakázána!' diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 18693d476..108d7dad8 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cRegistrierungen sind deaktiviert' diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index f2aa1d44a..e5e20055a 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: register_request: '&3Please, register to the server with the command: /register ' diff --git a/src/main/resources/messages/messages_eo.yml b/src/main/resources/messages/messages_eo.yml index f438b19da..dd1657728 100644 --- a/src/main/resources/messages/messages_eo.yml +++ b/src/main/resources/messages/messages_eo.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cEn-ludo registriĝo estas malebligita!' diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index 9a87984d2..ce0d8a369 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -1,4 +1,8 @@ # This file must be in ANSI if win, or UTF-8 if linux. +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. # Registration registration: diff --git a/src/main/resources/messages/messages_et.yml b/src/main/resources/messages/messages_et.yml index 3696593c1..df6bd053c 100644 --- a/src/main/resources/messages/messages_et.yml +++ b/src/main/resources/messages/messages_et.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cMängusisene registreerimine välja lülitatud!' diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index b7782ee3a..4eec94c34 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cErregistroa desgaitua' diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index 5e88b4c1e..9a6578bdd 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cRekisteröinti on suljettu!' diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index 47e01f2db..93358b2e8 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -1,7 +1,10 @@ # Traduction par: André & Twonox # Pour afficher une apostrophe, vous devez en mettre deux consécutivement (ex: «J''ai» au lieu de «J'ai») -# Pour passer à la ligne, utilisez: %nl% +# List of global tags: +# %nl% - Pour passer à la ligne. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. # Registration registration: diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index 11210f4e9..9c47a222d 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cO rexistro está deshabilitado' diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index ea2837122..609545976 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cA regisztráció letiltva!' diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 00899b1e3..9fda2668d 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cRegister dalam game tidak diaktifkan!' diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index 570971f87..40b0059ec 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -1,6 +1,10 @@ -# Lingua Italiana creata da Maxetto e sgdc3. +# Lingua Italiana creata da Maxetto. +# Tag globali disponibili: +# %nl% - Vai a capo. +# %username% - Sostituisce il nome dell'utente che riceve il messaggio. +# %displayname% - Sostituisce il nickname (e i colori) dell'utente che riceve il messaggio. -# Registration +# Registrazione registration: disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.' name_taken: '&cHai già eseguito la registrazione, non puoi eseguirla nuovamente.' @@ -10,7 +14,7 @@ registration: success: '&2Registrato correttamente!' kicked_admin_registered: 'Un amministratore ti ha appena registrato, per favore rientra nel server' -# Password errors on registration +# Errori della password durante la registrazione password: match_error: '&cLe password non corrispondono!' name_in_password: '&cNon puoi usare il tuo nome utente come password, per favore scegline un''altra...' @@ -18,7 +22,7 @@ password: forbidden_characters: '&4La tua password contiene caratteri non consentiti. I caratteri consentiti sono: %valid_chars' wrong_length: '&cLa password che hai inserito è troppo corta o troppo lunga, per favore scegline un''altra...' -# Login +# Autenticazione login: command_usage: '&cUtilizzo: /login ' wrong_password: '&cPassword non corretta!' @@ -26,7 +30,7 @@ login: login_request: '&cPer favore, esegui l''autenticazione con il comando: /login ' timeout_error: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!' -# Errors +# Errori error: denied_command: '&cPer poter usare questo comando devi essere autenticato!' denied_chat: '&cPer poter scrivere messaggi in chat devi essere autenticato!' @@ -45,12 +49,12 @@ antibot: auto_enabled: '&4Il servizio di AntiBot è stato automaticamente abilitato a seguito delle numerose connessioni!' auto_disabled: '&2Il servizio di AntiBot è stato automaticamente disabilitato dopo %m minuti!' -# Unregister +# Rimozione dal Database unregister: success: '&2Sei stato correttamente rimosso dal database!' command_usage: '&cUtilizzo: /unregister ' -# Other messages +# Altri messaggi misc: account_not_activated: '&cIl tuo account non è stato ancora verificato, controlla fra le tue email per scoprire come attivarlo!' password_changed: '&2Password cambiata correttamente!' @@ -61,12 +65,12 @@ misc: accounts_owned_self: 'Possiedi %count account:' accounts_owned_other: 'Il giocatore %name possiede %count account:' -# Session messages +# Messaggi della sessione session: valid_session: '&2Autenticato automaticamente attraverso la precedente sessione!' invalid_session: '&cIl tuo indirizzo IP è cambiato e la tua sessione è stata terminata!' -# Error messages when joining +# Messaggi di errore durante l'accesso on_join_validation: same_ip_online: 'Un giocatore con il tuo stesso IP è già connesso sul server!' same_nick_online: '&4Un giocatore con il tuo stesso nome utente è già connesso sul server!' @@ -96,7 +100,7 @@ email: change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' -# Password recovery by email +# Recupero password via Email recovery: forgot_password_hint: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery ' command_usage: '&cUtilizzo: /email recovery ' @@ -116,7 +120,7 @@ captcha: captcha_for_registration: 'Per poterti registrare devi prima risolvere un captcha, per favore scrivi: /captcha %captcha_code' register_captcha_valid: '&2Il captcha inserito è valido! Ora puoi eseguire la registrazione con: /register ' -# Verification code +# Codice di verifica verification: code_required: '&3Questo comando va a modificare dati sensibili e richiede una verifica tramite email! Controlla la tua posta in arrivo e segui le istruzioni nell''email.' command_usage: '&cUtilizzo: /verification ' @@ -126,7 +130,7 @@ verification: code_expired: '&3Il tuo codice è scaduto! Esegui nuovamente un comando che modifica dati sensibili per ricevere uno nuovo codice!' email_needed: '&3Per verificare la tua identità devi collegare un indirizzo email al tuo account!' -# Time units +# Unità di tempo time: second: 'secondo' seconds: 'secondi' diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index 0713fcb51..f934f4ccb 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -1,5 +1,9 @@ #Translated by Kirito (kds123321@naver.com), System32(me@syst32.com), Adeuran(adeuran@tistory.com) #14.05.2017 Thanks for use +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. # Registration registration: diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index 091903d88..2952d4497 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&6Registracija yra isjungta' diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index 634c92b1e..00b290de9 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cRegistratie is uitgeschakeld!' diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index b5050fc87..427ad302b 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&4Rejestracja jest wyłączona.' diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index b0f707e28..fe41dd553 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cRegisto de novos utilizadores desactivado' diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml index 874e70ee3..16b144497 100644 --- a/src/main/resources/messages/messages_ro.yml +++ b/src/main/resources/messages/messages_ro.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cInregistrarea in joc nu este activata!' diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index de31b258f..82d9d48e0 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cРегистрация отключена.' diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index deb03a8f7..030e721d7 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -4,6 +4,10 @@ # in future there can be more translators # # if they are not listed here # # check Translators on GitHub Wiki. # +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. # Registration registration: diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index 94ac23086..119f037e3 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cOyun icin kayit olma kapatildi!' diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index d5f448fb9..a1579c303 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cВнутрішньоігрову реєстрацію зараз вимкнено.' diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index 03190e9bb..61bbda945 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&cKhông cho phép đăng ký tài khoản trong máy chủ!' diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index c4032e30a..d53a44486 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&8[&6玩家系统&8] &c目前服务器暂时禁止注册,请到服务器论坛以得到更多资讯' diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index 002a52d5f..ae29c65b1 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -1,6 +1,10 @@ # Translator: lifehome # # Last modif: 1508689979 UTC # # -------------------------------------------- # +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. # Registration registration: diff --git a/src/main/resources/messages/messages_zhmc.yml b/src/main/resources/messages/messages_zhmc.yml index 6d811c4eb..76fa6ccc4 100644 --- a/src/main/resources/messages/messages_zhmc.yml +++ b/src/main/resources/messages/messages_zhmc.yml @@ -1,3 +1,8 @@ +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. + # Registration registration: disabled: '&c遊戲內註冊已停用!' diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index 2cfff1379..91696b5d8 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -1,5 +1,9 @@ # Translators: MineWolf50, lifehome, haer0248 # # -------------------------------------------- # +# List of global tags: +# %nl% - Goes to new line. +# %username% - Replaces the username of the player receiving the message. +# %displayname% - Replaces the nickname (and colors) of the player receiving the message. # Registration registration: From bc4cb5046dcaa3537832aafc4cd15deb33ddea8e Mon Sep 17 00:00:00 2001 From: Maxetto Date: Wed, 7 Mar 2018 19:29:24 +0100 Subject: [PATCH 057/155] Fix yet another build --- .../fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java | 4 ++-- src/main/java/fr/xephi/authme/message/Messages.java | 2 +- src/main/java/fr/xephi/authme/service/CommonService.java | 1 + .../fr/xephi/authme/message/MessagesIntegrationTest.java | 5 ++++- src/test/resources/fr/xephi/authme/message/messages_test.yml | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java index 2362b7736..f8e2e7baf 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java @@ -49,9 +49,9 @@ class LimboPlayerTaskManager { */ void registerMessageTask(Player player, LimboPlayer limbo, boolean isRegistered) { int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL); - MessageResult messageResult = getMessageKey(player.getName(), isRegistered); + MessageResult result = getMessageKey(player.getName(), isRegistered); if (interval > 0) { - String[] joinMessage = messages.retrieveSingle(messageResult.messageKey, player, messageResult.args).split("\n"); + String[] joinMessage = messages.retrieveSingle(result.messageKey, player, result.args).split("\n"); MessageTask messageTask = new MessageTask(player, joinMessage); bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND); limbo.setMessageTask(messageTask); diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 4439a1b7d..13017b3a8 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -116,7 +116,7 @@ public class Messages { * @return The message from the file */ private String retrieveMessage(MessageKey key, CommandSender sender) { - String message = messagesFileHandler.getMessage(key.getKey()); + String message = messagesFileHandler.getMessage(key.getKey()); String displayName = sender.getName(); if (sender instanceof Player) { displayName = ((Player) sender).getDisplayName(); diff --git a/src/main/java/fr/xephi/authme/service/CommonService.java b/src/main/java/fr/xephi/authme/service/CommonService.java index c95171b1c..ea63c58e1 100644 --- a/src/main/java/fr/xephi/authme/service/CommonService.java +++ b/src/main/java/fr/xephi/authme/service/CommonService.java @@ -64,6 +64,7 @@ public class CommonService { * Retrieves a message in one piece. * * @param key the key of the message + * @param sender The entity to send the message to * @return the message */ public String retrieveSingleMessage(MessageKey key, CommandSender sender) { diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 69bbfe5d2..125ad36af 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -141,6 +141,7 @@ public class MessagesIntegrationTest { MessageKey key = MessageKey.LOGIN_SUCCESS; Player player = Mockito.mock(Player.class); given(player.getName()).willReturn("Tester"); + given(player.getDisplayName()).willReturn("§cTesty"); // when messages.send(player, key); @@ -155,6 +156,7 @@ public class MessagesIntegrationTest { MessageKey key = MessageKey.UNKNOWN_USER; Player player = Mockito.mock(Player.class); given(player.getName()).willReturn("Tester"); + given(player.getDisplayName()).willReturn("§cTesty"); // when messages.send(player, key); @@ -172,12 +174,13 @@ public class MessagesIntegrationTest { MessageKey key = MessageKey.REGISTER_MESSAGE; Player player = Mockito.mock(Player.class); given(player.getName()).willReturn("Tester"); + given(player.getDisplayName()).willReturn("§cTesty"); // when messages.send(player, key); // then - verify(player).sendMessage("§3Please Tester, register to the server with the command: /register "); + verify(player).sendMessage("§3Please Tester, register to the §cTesty§3."); } @Test diff --git a/src/test/resources/fr/xephi/authme/message/messages_test.yml b/src/test/resources/fr/xephi/authme/message/messages_test.yml index 86b38986e..93287786a 100644 --- a/src/test/resources/fr/xephi/authme/message/messages_test.yml +++ b/src/test/resources/fr/xephi/authme/message/messages_test.yml @@ -3,7 +3,7 @@ error: unregistered_user: 'We''ve got%nl%new lines%nl%and '' apostrophes' registration: - register_request: '&3Please %username%, register to the server with the command: /register ' + register_request: '&3Please %username%, register to the %displayname%&3.' login: success: '&cHere we have&bdefined some colors &dand some other <hings' wrong_password: '&cWrong password!' From 7a3e2e9f71089147dfe8056838c5e33f49289169 Mon Sep 17 00:00:00 2001 From: Maxetto Date: Wed, 7 Mar 2018 19:37:20 +0100 Subject: [PATCH 058/155] Use spaces instead of tabs --- .../fr/xephi/authme/message/Messages.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 13017b3a8..69be2687e 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -117,15 +117,15 @@ public class Messages { */ private String retrieveMessage(MessageKey key, CommandSender sender) { String message = messagesFileHandler.getMessage(key.getKey()); - String displayName = sender.getName(); - if (sender instanceof Player) { - displayName = ((Player) sender).getDisplayName(); - } - - return ChatColor.translateAlternateColorCodes('&', message) - .replace(NEWLINE_TAG, "\n") - .replace(USERNAME_TAG, sender.getName()) - .replace(DISPLAYNAME_TAG, displayName); + String displayName = sender.getName(); + if (sender instanceof Player) { + displayName = ((Player) sender).getDisplayName(); + } + + return ChatColor.translateAlternateColorCodes('&', message) + .replace(NEWLINE_TAG, "\n") + .replace(USERNAME_TAG, sender.getName()) + .replace(DISPLAYNAME_TAG, displayName); } /** @@ -136,12 +136,12 @@ public class Messages { * @return The message from the file */ private String retrieveMessage(MessageKey key, String name) { - String message = messagesFileHandler.getMessage(key.getKey()); - - return ChatColor.translateAlternateColorCodes('&', message) - .replace(NEWLINE_TAG, "\n") - .replace(USERNAME_TAG, name) - .replace(DISPLAYNAME_TAG, name); + String message = messagesFileHandler.getMessage(key.getKey()); + + return ChatColor.translateAlternateColorCodes('&', message) + .replace(NEWLINE_TAG, "\n") + .replace(USERNAME_TAG, name) + .replace(DISPLAYNAME_TAG, name); } /** From c3cf9e3ee0ebd9b56c3f08bf5dd100cf0d1ba7c2 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Wed, 7 Mar 2018 20:11:53 +0100 Subject: [PATCH 059/155] #1141 Rough version of TOTP commands to add and remove a code for a player --- pom.xml | 11 ++ .../authme/command/CommandInitializer.java | 69 +++++++++-- .../executable/totp/AddTotpCommand.java | 41 +++++++ .../executable/totp/ConfirmTotpCommand.java | 39 ++++++ .../executable/totp/RemoveTotpCommand.java | 37 ++++++ .../executable/totp/TotpBaseCommand.java | 29 +++++ .../authme/permission/PlayerPermission.java | 7 +- .../fr/xephi/authme/security/TotpService.java | 113 ++++++++++++++++++ .../xephi/authme/service/BukkitService.java | 7 ++ src/main/resources/plugin.yml | 9 ++ .../command/CommandInitializerTest.java | 2 +- .../authme/security/TotpServiceTest.java | 51 ++++++++ .../authme/service/BukkitServiceTest.java | 13 ++ 13 files changed, 416 insertions(+), 12 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java create mode 100644 src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java create mode 100644 src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java create mode 100644 src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java create mode 100644 src/main/java/fr/xephi/authme/security/TotpService.java create mode 100644 src/test/java/fr/xephi/authme/security/TotpServiceTest.java diff --git a/pom.xml b/pom.xml index 56ef29401..2d8b1287c 100644 --- a/pom.xml +++ b/pom.xml @@ -286,6 +286,10 @@ org.bstats fr.xephi.authme.libs.org.bstats + + com.warrenstrange + fr.xephi.authme.libs.com.warrenstrange + @@ -461,6 +465,13 @@ true + + + com.warrenstrange + googleauth + 1.1.2 + + org.spigotmc diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index dc5f740ff..7f0d33e78 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -40,6 +40,10 @@ import fr.xephi.authme.command.executable.email.ShowEmailCommand; import fr.xephi.authme.command.executable.login.LoginCommand; import fr.xephi.authme.command.executable.logout.LogoutCommand; import fr.xephi.authme.command.executable.register.RegisterCommand; +import fr.xephi.authme.command.executable.totp.AddTotpCommand; +import fr.xephi.authme.command.executable.totp.ConfirmTotpCommand; +import fr.xephi.authme.command.executable.totp.RemoveTotpCommand; +import fr.xephi.authme.command.executable.totp.TotpBaseCommand; import fr.xephi.authme.command.executable.unregister.UnregisterCommand; import fr.xephi.authme.command.executable.verification.VerificationCommand; import fr.xephi.authme.permission.AdminPermission; @@ -134,6 +138,9 @@ public class CommandInitializer { .executableCommand(ChangePasswordCommand.class) .register(); + // Create totp base command + CommandDescription totpBase = buildTotpBaseCommand(); + // Register the base captcha command CommandDescription captchaBase = CommandDescription.builder() .parent(null) @@ -156,16 +163,8 @@ public class CommandInitializer { .executableCommand(VerificationCommand.class) .register(); - List baseCommands = ImmutableList.of( - authMeBase, - emailBase, - loginBase, - logoutBase, - registerBase, - unregisterBase, - changePasswordBase, - captchaBase, - verificationBase); + List baseCommands = ImmutableList.of(authMeBase, emailBase, loginBase, logoutBase, + registerBase, unregisterBase, changePasswordBase, totpBase, captchaBase, verificationBase); setHelpOnAllBases(baseCommands); commands = baseCommands; @@ -543,6 +542,56 @@ public class CommandInitializer { return emailBase; } + /** + * Creates a command description object for {@code /totp} including its children. + * + * @return the totp base command description + */ + private CommandDescription buildTotpBaseCommand() { + // Register the base totp command + CommandDescription totpBase = CommandDescription.builder() + .parent(null) + .labels("totp", "2fa") + .description("TOTP commands") + .detailedDescription("Performs actions related to two-factor authentication.") + .executableCommand(TotpBaseCommand.class) + .register(); + + // Register totp add + CommandDescription.builder() + .parent(totpBase) + .labels("add") + .description("Enables TOTP") + .detailedDescription("Enables two-factor authentication for your account.") + .permission(PlayerPermission.TOGGLE_TOTP_STATUS) + .executableCommand(AddTotpCommand.class) + .register(); + + // Register totp confirm + CommandDescription.builder() + .parent(totpBase) + .labels("confirm") + .description("Enables TOTP after successful code") + .detailedDescription("Saves the generated TOTP secret after confirmation.") + .withArgument("code", "Code from the given secret from /totp add", false) + .permission(PlayerPermission.TOGGLE_TOTP_STATUS) + .executableCommand(ConfirmTotpCommand.class) + .register(); + + // Register totp remove + CommandDescription.builder() + .parent(totpBase) + .labels("remove") + .description("Removes TOTP") + .detailedDescription("Disables two-factor authentication for your account.") + .withArgument("code", "Current 2FA code", false) + .permission(PlayerPermission.TOGGLE_TOTP_STATUS) + .executableCommand(RemoveTotpCommand.class) + .register(); + + return totpBase; + } + /** * Sets the help command on all base commands, e.g. to register /authme help or /register help. * diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java new file mode 100644 index 000000000..44eef5d81 --- /dev/null +++ b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java @@ -0,0 +1,41 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.security.TotpService; +import fr.xephi.authme.security.TotpService.TotpGenerationResult; +import fr.xephi.authme.service.CommonService; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.util.List; + +/** + * Command for a player to enable TOTP. + */ +public class AddTotpCommand extends PlayerCommand { + + @Inject + private TotpService totpService; + + @Inject + private DataSource dataSource; + + @Inject + private CommonService commonService; + + @Override + protected void runCommand(Player player, List arguments) { + PlayerAuth auth = dataSource.getAuth(player.getName()); + if (auth.getTotpKey() == null) { + TotpGenerationResult createdTotpInfo = totpService.generateTotpKey(player); + commonService.send(player, MessageKey.TWO_FACTOR_CREATE, + createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl()); + } else { + player.sendMessage(ChatColor.RED + "Two-factor authentication is already enabled for your account!"); + } + } +} diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java new file mode 100644 index 000000000..1bf59f7d8 --- /dev/null +++ b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java @@ -0,0 +1,39 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.security.TotpService; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.util.List; + +/** + * Command to enable TOTP by supplying the proper code as confirmation. + */ +public class ConfirmTotpCommand extends PlayerCommand { + + @Inject + private TotpService totpService; + + @Inject + private DataSource dataSource; + + @Override + protected void runCommand(Player player, List arguments) { + // TODO #1141: Check if player already has TOTP + + final String totpKey = totpService.retrieveGeneratedSecret(player); + if (totpKey == null) { + player.sendMessage("No TOTP key has been generated for you or it has expired. Please run /totp add"); + } else { + boolean isTotpCodeValid = totpService.confirmCodeForGeneratedTotpKey(player, arguments.get(0)); + if (isTotpCodeValid) { + dataSource.setTotpKey(player.getName(), totpKey); + player.sendMessage("Successfully enabled two-factor authentication for your account"); + } else { + player.sendMessage("Wrong code or code has expired. Please use /totp add again"); + } + } + } +} diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java new file mode 100644 index 000000000..abf0b79ca --- /dev/null +++ b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java @@ -0,0 +1,37 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.security.TotpService; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.util.List; + +/** + * Command for a player to remove 2FA authentication. + */ +public class RemoveTotpCommand extends PlayerCommand { + + @Inject + private DataSource dataSource; + + @Inject + private TotpService totpService; + + @Override + protected void runCommand(Player player, List arguments) { + PlayerAuth auth = dataSource.getAuth(player.getName()); + if (auth.getTotpKey() == null) { + player.sendMessage("Two-factor authentication is not enabled for your account!"); + } else { + if (totpService.verifyCode(auth, arguments.get(0))) { + dataSource.removeTotpKey(auth.getNickname()); + player.sendMessage("Successfully removed two-factor authentication from your account"); + } else { + player.sendMessage("Invalid code!"); + } + } + } +} diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java new file mode 100644 index 000000000..2b170a030 --- /dev/null +++ b/src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java @@ -0,0 +1,29 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.command.CommandMapper; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.command.FoundCommandResult; +import fr.xephi.authme.command.help.HelpProvider; +import org.bukkit.command.CommandSender; + +import javax.inject.Inject; +import java.util.Collections; +import java.util.List; + +/** + * Base command for /totp. + */ +public class TotpBaseCommand implements ExecutableCommand { + + @Inject + private CommandMapper commandMapper; + + @Inject + private HelpProvider helpProvider; + + @Override + public void executeCommand(CommandSender sender, List arguments) { + FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Collections.singletonList("totp")); + helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN); + } +} diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java index 1a89490f2..12a4a4229 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java @@ -68,7 +68,12 @@ public enum PlayerPermission implements PermissionNode { /** * Permission to use the email verification codes feature. */ - VERIFICATION_CODE("authme.player.security.verificationcode"); + VERIFICATION_CODE("authme.player.security.verificationcode"), + + /** + * Permission to enable and disable TOTP. + */ + TOGGLE_TOTP_STATUS("authme.player.totp"); /** * The permission node. diff --git a/src/main/java/fr/xephi/authme/security/TotpService.java b/src/main/java/fr/xephi/authme/security/TotpService.java new file mode 100644 index 000000000..dd8257b68 --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/TotpService.java @@ -0,0 +1,113 @@ +package fr.xephi.authme.security; + +import com.warrenstrange.googleauth.GoogleAuthenticator; +import com.warrenstrange.googleauth.GoogleAuthenticatorKey; +import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.initialization.HasCleanup; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.util.expiring.ExpiringMap; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.util.concurrent.TimeUnit; + +/** + * Service for TOTP actions. + */ +public class TotpService implements HasCleanup { + + private static final int NEW_TOTP_KEY_EXPIRATION_MINUTES = 5; + + private final ExpiringMap totpKeys; + private final GoogleAuthenticator authenticator; + private final BukkitService bukkitService; + + @Inject + TotpService(BukkitService bukkitService) { + this.bukkitService = bukkitService; + this.totpKeys = new ExpiringMap<>(NEW_TOTP_KEY_EXPIRATION_MINUTES, TimeUnit.MINUTES); + this.authenticator = new GoogleAuthenticator(); + } + + /** + * Generates a new TOTP key and returns the corresponding QR code. The generated key is saved temporarily + * for the user and can be later retrieved with a confirmation code from {@link #confirmCodeForGeneratedTotpKey}. + * + * @param player the player to save the TOTP key for + * @return TOTP generation result + */ + public TotpGenerationResult generateTotpKey(Player player) { + GoogleAuthenticatorKey credentials = authenticator.createCredentials(); + totpKeys.put(player.getName().toLowerCase(), credentials.getKey()); + String qrCodeUrl = GoogleAuthenticatorQRGenerator.getOtpAuthURL( + bukkitService.getIp(), player.getName(), credentials); + return new TotpGenerationResult(credentials.getKey(), qrCodeUrl); + } + + /** + * Returns the generated TOTP secret of a player, if available and not yet expired. + * + * @param player the player to retrieve the TOTP key for + * @return the totp secret + */ + public String retrieveGeneratedSecret(Player player) { + return totpKeys.get(player.getName().toLowerCase()); + } + + /** + * Returns if the new totp code matches the newly generated totp key. + * + * @param player the player to retrieve the code for + * @param totpCodeConfirmation the input code confirmation + * @return the TOTP secret that was generated for the player, or null if not available or if the code is incorrect + */ + // Maybe by allowing to retrieve without confirmation and exposing verifyCode(String, String) + public boolean confirmCodeForGeneratedTotpKey(Player player, String totpCodeConfirmation) { + String totpSecret = totpKeys.get(player.getName().toLowerCase()); + if (totpSecret != null) { + if (checkCode(totpSecret, totpCodeConfirmation)) { + totpKeys.remove(player.getName().toLowerCase()); + return true; + } + } + return false; + } + + public boolean verifyCode(PlayerAuth auth, String totpCode) { + return checkCode(auth.getTotpKey(), totpCode); + } + + private boolean checkCode(String totpKey, String inputCode) { + try { + Integer totpCode = Integer.valueOf(inputCode); + return authenticator.authorize(totpKey, totpCode); + } catch (NumberFormatException e) { + // ignore + } + return false; + } + + @Override + public void performCleanup() { + totpKeys.removeExpiredEntries(); + } + + public static final class TotpGenerationResult { + private final String totpKey; + private final String authenticatorQrCodeUrl; + + TotpGenerationResult(String totpKey, String authenticatorQrCodeUrl) { + this.totpKey = totpKey; + this.authenticatorQrCodeUrl = authenticatorQrCodeUrl; + } + + public String getTotpKey() { + return totpKey; + } + + public String getAuthenticatorQrCodeUrl() { + return authenticatorQrCodeUrl; + } + } +} diff --git a/src/main/java/fr/xephi/authme/service/BukkitService.java b/src/main/java/fr/xephi/authme/service/BukkitService.java index 6c9cd0cb8..f7808098e 100644 --- a/src/main/java/fr/xephi/authme/service/BukkitService.java +++ b/src/main/java/fr/xephi/authme/service/BukkitService.java @@ -376,4 +376,11 @@ public class BukkitService implements SettingsDependent { public BanEntry banIp(String ip, String reason, Date expires, String source) { return Bukkit.getServer().getBanList(BanList.Type.IP).addBan(ip, reason, expires, source); } + + /** + * @return the IP string that this server is bound to, otherwise empty string + */ + public String getIp() { + return Bukkit.getServer().getIp(); + } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2bae573d7..e60721de2 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -46,6 +46,11 @@ commands: aliases: - changepass - cp + totp: + description: TOTP commands + usage: /totp add|remove + aliases: + - 2fa captcha: description: Captcha command usage: /captcha @@ -233,6 +238,7 @@ permissions: authme.player.register: true authme.player.security.verificationcode: true authme.player.seeownaccounts: true + authme.player.totp: true authme.player.unregister: true authme.player.canbeforced: description: Permission for users a login can be forced to. @@ -277,6 +283,9 @@ permissions: authme.player.seeownaccounts: description: Permission to use to see own other accounts. default: true + authme.player.totp: + description: Permission to enable and disable TOTP. + default: true authme.player.unregister: description: Command permission to unregister. default: true diff --git a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java index 2b8215baa..133d01940 100644 --- a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java @@ -44,7 +44,7 @@ public class CommandInitializerTest { // It obviously doesn't make sense to test much of the concrete data // that is being initialized; we just want to guarantee with this test // that data is indeed being initialized and we take a few "probes" - assertThat(commands, hasSize(9)); + assertThat(commands, hasSize(10)); assertThat(commandsIncludeLabel(commands, "authme"), equalTo(true)); assertThat(commandsIncludeLabel(commands, "register"), equalTo(true)); assertThat(commandsIncludeLabel(commands, "help"), equalTo(false)); diff --git a/src/test/java/fr/xephi/authme/security/TotpServiceTest.java b/src/test/java/fr/xephi/authme/security/TotpServiceTest.java new file mode 100644 index 000000000..6350b4c4c --- /dev/null +++ b/src/test/java/fr/xephi/authme/security/TotpServiceTest.java @@ -0,0 +1,51 @@ +package fr.xephi.authme.security; + +import fr.xephi.authme.security.TotpService.TotpGenerationResult; +import fr.xephi.authme.service.BukkitService; +import org.bukkit.entity.Player; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static fr.xephi.authme.AuthMeMatchers.stringWithLength; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link TotpService}. + */ +@RunWith(MockitoJUnitRunner.class) +public class TotpServiceTest { + + @InjectMocks + private TotpService totpService; + + @Mock + private BukkitService bukkitService; + + @Test + public void shouldGenerateTotpKey() { + // given + Player player = mock(Player.class); + given(player.getName()).willReturn("Bobby"); + given(bukkitService.getIp()).willReturn("127.48.44.4"); + + // when + TotpGenerationResult key1 = totpService.generateTotpKey(player); + TotpGenerationResult key2 = totpService.generateTotpKey(player); + + // then + assertThat(key1.getTotpKey(), stringWithLength(16)); + assertThat(key2.getTotpKey(), stringWithLength(16)); + assertThat(key1.getAuthenticatorQrCodeUrl(), startsWith("https://chart.googleapis.com/chart?chs=200x200")); + assertThat(key2.getAuthenticatorQrCodeUrl(), startsWith("https://chart.googleapis.com/chart?chs=200x200")); + assertThat(key1.getTotpKey(), not(equalTo(key2.getTotpKey()))); + } + +} diff --git a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java index 232d11ce2..e6787c109 100644 --- a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java @@ -332,6 +332,19 @@ public class BukkitServiceTest { assertThat(event.getPlayer(), equalTo(player)); } + @Test + public void shouldReturnServerIp() { + // given + String ip = "99.99.99.99"; + given(server.getIp()).willReturn(ip); + + // when + String result = bukkitService.getIp(); + + // then + assertThat(result, equalTo(ip)); + } + // Note: This method is used through reflections public static Player[] onlinePlayersImpl() { return new Player[]{ From 586b98f74ff2420dcd3b712998d3386797345b59 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Wed, 7 Mar 2018 23:19:27 +0100 Subject: [PATCH 060/155] Update CI and repository links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ee54889dc..48ee415a1 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - [Discord](https://discord.gg/Vn9eCyE) - CI Services: - - [Official Jenkins](http://ci.xephi.fr/job/AuthMeReloaded) (**DEVELOPMENT BUILDS**) +  - Jenkins: [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/AuthMeReloaded) (**DEVELOPMENT BUILDS**) - CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) - Project status: @@ -19,8 +19,8 @@ - Code climate: [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) - Development resources: - - JavaDocs - - Maven Repository +  - JavaDocs +  - Maven Repository - Statistics: - bStats: [AuthMe on bstats.org](https://bstats.org/plugin/bukkit/AuthMe) From d80a9b27cb915e4e7a27e8a4b6b454ffc8e15cf1 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Wed, 7 Mar 2018 23:41:28 +0100 Subject: [PATCH 061/155] Try to fix javadocs generation --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56ef29401..3f0f4610c 100644 --- a/pom.xml +++ b/pom.xml @@ -191,7 +191,7 @@ attach-javadocs - aggregate + javadoc jar From e67255c50562d8d7fb7d138731881f01d24a0456 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Wed, 7 Mar 2018 23:45:08 +0100 Subject: [PATCH 062/155] Fix link markdown --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 48ee415a1..5c60afb50 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - [Discord](https://discord.gg/Vn9eCyE) - CI Services: -  - Jenkins: [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/AuthMeReloaded) (**DEVELOPMENT BUILDS**) + - Jenkins: [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/AuthMeReloaded) (**DEVELOPMENT BUILDS**) - CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) - Project status: @@ -19,8 +19,8 @@ - Code climate: [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) - Development resources: -  - JavaDocs -  - Maven Repository + - JavaDocs + - Maven Repository - Statistics: - bStats: [AuthMe on bstats.org](https://bstats.org/plugin/bukkit/AuthMe) From 6ddfa3fd2bc5b547eedf31c741944d3c9ec36d73 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 8 Mar 2018 01:24:28 +0100 Subject: [PATCH 063/155] [CI-SKIP] Add bStats graph image --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5c60afb50..7d5a5f767 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ - Statistics: - bStats: [AuthMe on bstats.org](https://bstats.org/plugin/bukkit/AuthMe) + ![Graph](https://bstats.org/signatures/bukkit/AuthMe.svg)


From e72d5d5e819f496c39e41af8b9080c64f376e1c1 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 9 Mar 2018 18:37:01 +0100 Subject: [PATCH 064/155] #1141 Require TOTP code to be passed with /login (temporary) - Temporarily require the TOTP code to be provided with /login - Future implementation should require it as a second step --- .../authme/command/CommandInitializer.java | 1 + .../authme/debug/PlayerAuthViewer.java | 1 + .../executable/login/LoginCommand.java | 5 +- .../fr/xephi/authme/process/Management.java | 4 +- .../process/login/AsynchronousLogin.java | 23 +++- .../authme/debug/PlayerAuthViewerTest.java | 106 ++++++++++++++++++ .../executable/login/LoginCommandTest.java | 16 ++- 7 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index 7f0d33e78..dd3cc37b8 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -89,6 +89,7 @@ public class CommandInitializer { .description("Login command") .detailedDescription("Command to log in using AuthMeReloaded.") .withArgument("password", "Login password", false) + .withArgument("2facode", "TOTP code", true) .permission(PlayerPermission.LOGIN) .executableCommand(LoginCommand.class) .register(); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java index 07e160a40..ae314e098 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java @@ -77,6 +77,7 @@ class PlayerAuthViewer implements DebugSection { HashedPassword hashedPass = auth.getPassword(); sender.sendMessage("Hash / salt (partial): '" + safeSubstring(hashedPass.getHash(), 6) + "' / '" + safeSubstring(hashedPass.getSalt(), 4) + "'"); + sender.sendMessage("TOTP code (partial): '" + safeSubstring(auth.getTotpKey(), 3) + "'"); } /** diff --git a/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java b/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java index 32a4d87da..e2878271d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java @@ -18,8 +18,9 @@ public class LoginCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments) { - final String password = arguments.get(0); - management.performLogin(player, password); + String password = arguments.get(0); + String totpCode = arguments.size() > 1 ? arguments.get(1) : null; + management.performLogin(player, password, totpCode); } @Override diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index 5260fed13..a5a879e05 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -49,8 +49,8 @@ public class Management { } - public void performLogin(Player player, String password) { - runTask(() -> asynchronousLogin.login(player, password)); + public void performLogin(Player player, String password, String totpCode) { + runTask(() -> asynchronousLogin.login(player, password, totpCode)); } public void forceLogin(Player player) { diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index c52202ef4..6cadf820b 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -18,6 +18,7 @@ import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.TotpService; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.SessionService; @@ -78,6 +79,9 @@ public class AsynchronousLogin implements AsynchronousProcess { @Inject private BungeeSender bungeeSender; + @Inject + private TotpService totpService; + AsynchronousLogin() { } @@ -86,10 +90,11 @@ public class AsynchronousLogin implements AsynchronousProcess { * * @param player the player to log in * @param password the password to log in with + * @param totpCode the totp code (nullable) */ - public void login(Player player, String password) { + public void login(Player player, String password, String totpCode) { PlayerAuth auth = getPlayerAuth(player); - if (auth != null && checkPlayerInfo(player, auth, password)) { + if (auth != null && checkPlayerInfo(player, auth, password, totpCode)) { performLogin(player, auth); } } @@ -156,10 +161,11 @@ public class AsynchronousLogin implements AsynchronousProcess { * @param player the player requesting to log in * @param auth the PlayerAuth object of the player * @param password the password supplied by the player + * @param totpCode the input totp code (nullable) * @return true if the password matches and all other conditions are met (e.g. no captcha required), * false otherwise */ - private boolean checkPlayerInfo(Player player, PlayerAuth auth, String password) { + private boolean checkPlayerInfo(Player player, PlayerAuth auth, String password, String totpCode) { final String name = player.getName().toLowerCase(); // If captcha is required send a message to the player and deny to log in @@ -174,6 +180,17 @@ public class AsynchronousLogin implements AsynchronousProcess { loginCaptchaManager.increaseLoginFailureCount(name); tempbanManager.increaseCount(ip, name); + if (auth.getTotpKey() != null) { + if (totpCode == null) { + player.sendMessage( + "You have two-factor authentication enabled. Please provide it: /login <2faCode>"); + return false; + } else if (!totpService.verifyCode(auth, totpCode)) { + player.sendMessage("Invalid code for two-factor authentication. Please try again"); + return false; + } + } + if (passwordSecurity.comparePassword(password, auth.getPassword(), player.getName())) { return true; } else { diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java new file mode 100644 index 000000000..8f34bcbf8 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java @@ -0,0 +1,106 @@ +package fr.xephi.authme.command.executable.authme.debug; + +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import org.bukkit.command.CommandSender; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; + +/** + * Test for {@link PlayerAuthViewer}. + */ +@RunWith(MockitoJUnitRunner.class) +public class PlayerAuthViewerTest { + + @InjectMocks + private PlayerAuthViewer authViewer; + + @Mock + private DataSource dataSource; + + @Test + public void shouldMakeExample() { + // given + CommandSender sender = mock(CommandSender.class); + + // when + authViewer.execute(sender, Collections.emptyList()); + + // then + verify(sender).sendMessage(argThat(containsString("Example: /authme debug db Bobby"))); + } + + @Test + public void shouldHandleMissingPlayer() { + // given + CommandSender sender = mock(CommandSender.class); + + // when + authViewer.execute(sender, Collections.singletonList("bogus")); + + // then + verify(dataSource).getAuth("bogus"); + verify(sender).sendMessage(argThat(containsString("No record exists for 'bogus'"))); + } + + @Test + public void shouldDisplayAuthInfo() { + // given + CommandSender sender = mock(CommandSender.class); + PlayerAuth auth = PlayerAuth.builder().name("george").realName("George") + .password("abcdefghijkl", "mnopqrst") + .lastIp("127.1.2.7").registrationDate(1111140000000L) + .totpKey("SECRET1321") + .build(); + given(dataSource.getAuth("George")).willReturn(auth); + + // when + authViewer.execute(sender, Collections.singletonList("George")); + + // then + ArgumentCaptor textCaptor = ArgumentCaptor.forClass(String.class); + verify(sender, atLeastOnce()).sendMessage(textCaptor.capture()); + assertThat(textCaptor.getAllValues(), hasItem(containsString("Player george / George"))); + assertThat(textCaptor.getAllValues(), hasItem(containsString("Registration: 2005-03-18T"))); + assertThat(textCaptor.getAllValues(), hasItem(containsString("Hash / salt (partial): 'abcdef...' / 'mnop...'"))); + assertThat(textCaptor.getAllValues(), hasItem(containsString("TOTP code (partial): 'SEC...'"))); + } + + @Test + public void shouldHandleCornerCases() { + // given + CommandSender sender = mock(CommandSender.class); + PlayerAuth auth = PlayerAuth.builder().name("tar") + .password("abcd", null) + .lastIp("127.1.2.7").registrationDate(0L) + .build(); + given(dataSource.getAuth("Tar")).willReturn(auth); + + // when + authViewer.execute(sender, Collections.singletonList("Tar")); + + // then + ArgumentCaptor textCaptor = ArgumentCaptor.forClass(String.class); + verify(sender, atLeastOnce()).sendMessage(textCaptor.capture()); + assertThat(textCaptor.getAllValues(), hasItem(containsString("Player tar / Player"))); + assertThat(textCaptor.getAllValues(), hasItem(containsString("Registration: Not available (0)"))); + assertThat(textCaptor.getAllValues(), hasItem(containsString("Last login: Not available (null)"))); + assertThat(textCaptor.getAllValues(), hasItem(containsString("Hash / salt (partial): 'ab...' / ''"))); + assertThat(textCaptor.getAllValues(), hasItem(containsString("TOTP code (partial): ''"))); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java index 38307f93d..1335ca6f6 100644 --- a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java @@ -11,12 +11,12 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.Arrays; import java.util.Collections; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -57,7 +57,19 @@ public class LoginCommandTest { command.executeCommand(sender, Collections.singletonList("password")); // then - verify(management).performLogin(eq(sender), eq("password")); + verify(management).performLogin(sender, "password", null); + } + + @Test + public void shouldCallManagementForPasswordAndTotpCode() { + // given + Player sender = mock(Player.class); + + // when + command.executeCommand(sender, Arrays.asList("pwd", "12345")); + + // then + verify(management).performLogin(sender, "pwd", "12345"); } @Test From eb9cd31a65e064acaf607e66fc44cfacec9513ea Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 10 Mar 2018 16:21:53 +0100 Subject: [PATCH 065/155] #1141 Split TOTP permissions for add/remove, refactor TOTP services - Split TotpService further into GenerateTotpService and TotpAuthenticator, which wraps the GoogleAuthenticator impl - Add missing tests for the services - Change GenerateTotpService's interface to behave like a collection for more intuitive method behavior --- .../authme/command/CommandInitializer.java | 89 +++++++------- .../executable/totp/AddTotpCommand.java | 8 +- .../executable/totp/ConfirmTotpCommand.java | 16 +-- .../executable/totp/RemoveTotpCommand.java | 2 +- .../authme/permission/PlayerPermission.java | 9 +- .../process/login/AsynchronousLogin.java | 2 +- .../fr/xephi/authme/security/TotpService.java | 113 ------------------ .../security/totp/GenerateTotpService.java | 69 +++++++++++ .../security/totp/TotpAuthenticator.java | 67 +++++++++++ .../authme/security/totp/TotpService.java | 18 +++ src/main/resources/plugin.yml | 14 ++- .../authme/security/TotpServiceTest.java | 51 -------- .../totp/GenerateTotpServiceTest.java | 113 ++++++++++++++++++ .../security/totp/TotpAuthenticatorTest.java | 84 +++++++++++++ .../authme/security/totp/TotpServiceTest.java | 46 +++++++ ...lpTranslationGeneratorIntegrationTest.java | 2 +- .../fr/xephi/authme/message/help_test.yml | 3 + 17 files changed, 478 insertions(+), 228 deletions(-) delete mode 100644 src/main/java/fr/xephi/authme/security/TotpService.java create mode 100644 src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java create mode 100644 src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java create mode 100644 src/main/java/fr/xephi/authme/security/totp/TotpService.java delete mode 100644 src/test/java/fr/xephi/authme/security/TotpServiceTest.java create mode 100644 src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java create mode 100644 src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java create mode 100644 src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index dd3cc37b8..e1890626b 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -59,6 +59,9 @@ import java.util.List; */ public class CommandInitializer { + private static final boolean OPTIONAL = true; + private static final boolean MANDATORY = false; + private List commands; public CommandInitializer() { @@ -88,8 +91,8 @@ public class CommandInitializer { .labels("login", "l", "log") .description("Login command") .detailedDescription("Command to log in using AuthMeReloaded.") - .withArgument("password", "Login password", false) - .withArgument("2facode", "TOTP code", true) + .withArgument("password", "Login password", MANDATORY) + .withArgument("2facode", "TOTP code", OPTIONAL) .permission(PlayerPermission.LOGIN) .executableCommand(LoginCommand.class) .register(); @@ -110,8 +113,8 @@ public class CommandInitializer { .labels("register", "reg") .description("Register an account") .detailedDescription("Command to register using AuthMeReloaded.") - .withArgument("password", "Password", true) - .withArgument("verifyPassword", "Verify password", true) + .withArgument("password", "Password", OPTIONAL) + .withArgument("verifyPassword", "Verify password", OPTIONAL) .permission(PlayerPermission.REGISTER) .executableCommand(RegisterCommand.class) .register(); @@ -122,7 +125,7 @@ public class CommandInitializer { .labels("unregister", "unreg") .description("Unregister an account") .detailedDescription("Command to unregister using AuthMeReloaded.") - .withArgument("password", "Password", false) + .withArgument("password", "Password", MANDATORY) .permission(PlayerPermission.UNREGISTER) .executableCommand(UnregisterCommand.class) .register(); @@ -133,8 +136,8 @@ public class CommandInitializer { .labels("changepassword", "changepass", "cp") .description("Change password of an account") .detailedDescription("Command to change your password using AuthMeReloaded.") - .withArgument("oldPassword", "Old password", false) - .withArgument("newPassword", "New password", false) + .withArgument("oldPassword", "Old password", MANDATORY) + .withArgument("newPassword", "New password", MANDATORY) .permission(PlayerPermission.CHANGE_PASSWORD) .executableCommand(ChangePasswordCommand.class) .register(); @@ -148,7 +151,7 @@ public class CommandInitializer { .labels("captcha") .description("Captcha command") .detailedDescription("Captcha command for AuthMeReloaded.") - .withArgument("captcha", "The Captcha", false) + .withArgument("captcha", "The Captcha", MANDATORY) .permission(PlayerPermission.CAPTCHA) .executableCommand(CaptchaCommand.class) .register(); @@ -159,7 +162,7 @@ public class CommandInitializer { .labels("verification") .description("Verification command") .detailedDescription("Command to complete the verification process for AuthMeReloaded.") - .withArgument("code", "The code", false) + .withArgument("code", "The code", MANDATORY) .permission(PlayerPermission.VERIFICATION_CODE) .executableCommand(VerificationCommand.class) .register(); @@ -191,8 +194,8 @@ public class CommandInitializer { .labels("register", "reg", "r") .description("Register a player") .detailedDescription("Register the specified player with the specified password.") - .withArgument("player", "Player name", false) - .withArgument("password", "Password", false) + .withArgument("player", "Player name", MANDATORY) + .withArgument("password", "Password", MANDATORY) .permission(AdminPermission.REGISTER) .executableCommand(RegisterAdminCommand.class) .register(); @@ -203,7 +206,7 @@ public class CommandInitializer { .labels("unregister", "unreg", "unr") .description("Unregister a player") .detailedDescription("Unregister the specified player.") - .withArgument("player", "Player name", false) + .withArgument("player", "Player name", MANDATORY) .permission(AdminPermission.UNREGISTER) .executableCommand(UnregisterAdminCommand.class) .register(); @@ -214,7 +217,7 @@ public class CommandInitializer { .labels("forcelogin", "login") .description("Enforce login player") .detailedDescription("Enforce the specified player to login.") - .withArgument("player", "Online player name", true) + .withArgument("player", "Online player name", OPTIONAL) .permission(AdminPermission.FORCE_LOGIN) .executableCommand(ForceLoginCommand.class) .register(); @@ -225,8 +228,8 @@ public class CommandInitializer { .labels("password", "changepassword", "changepass", "cp") .description("Change a player's password") .detailedDescription("Change the password of a player.") - .withArgument("player", "Player name", false) - .withArgument("pwd", "New password", false) + .withArgument("player", "Player name", MANDATORY) + .withArgument("pwd", "New password", MANDATORY) .permission(AdminPermission.CHANGE_PASSWORD) .executableCommand(ChangePasswordAdminCommand.class) .register(); @@ -237,7 +240,7 @@ public class CommandInitializer { .labels("lastlogin", "ll") .description("Player's last login") .detailedDescription("View the date of the specified players last login.") - .withArgument("player", "Player name", true) + .withArgument("player", "Player name", OPTIONAL) .permission(AdminPermission.LAST_LOGIN) .executableCommand(LastLoginCommand.class) .register(); @@ -248,7 +251,7 @@ public class CommandInitializer { .labels("accounts", "account") .description("Display player accounts") .detailedDescription("Display all accounts of a player by his player name or IP.") - .withArgument("player", "Player name or IP", true) + .withArgument("player", "Player name or IP", OPTIONAL) .permission(AdminPermission.ACCOUNTS) .executableCommand(AccountsCommand.class) .register(); @@ -259,7 +262,7 @@ public class CommandInitializer { .labels("email", "mail", "getemail", "getmail") .description("Display player's email") .detailedDescription("Display the email address of the specified player if set.") - .withArgument("player", "Player name", true) + .withArgument("player", "Player name", OPTIONAL) .permission(AdminPermission.GET_EMAIL) .executableCommand(GetEmailCommand.class) .register(); @@ -270,8 +273,8 @@ public class CommandInitializer { .labels("setemail", "setmail", "chgemail", "chgmail") .description("Change player's email") .detailedDescription("Change the email address of the specified player.") - .withArgument("player", "Player name", false) - .withArgument("email", "Player email", false) + .withArgument("player", "Player name", MANDATORY) + .withArgument("email", "Player email", MANDATORY) .permission(AdminPermission.CHANGE_EMAIL) .executableCommand(SetEmailCommand.class) .register(); @@ -282,7 +285,7 @@ public class CommandInitializer { .labels("getip", "ip") .description("Get player's IP") .detailedDescription("Get the IP address of the specified online player.") - .withArgument("player", "Player name", false) + .withArgument("player", "Player name", MANDATORY) .permission(AdminPermission.GET_IP) .executableCommand(GetIpCommand.class) .register(); @@ -333,7 +336,7 @@ public class CommandInitializer { .labels("purge", "delete") .description("Purge old data") .detailedDescription("Purge old AuthMeReloaded data longer than the specified number of days ago.") - .withArgument("days", "Number of days", false) + .withArgument("days", "Number of days", MANDATORY) .permission(AdminPermission.PURGE) .executableCommand(PurgeCommand.class) .register(); @@ -344,8 +347,8 @@ public class CommandInitializer { .labels("purgeplayer") .description("Purges the data of one player") .detailedDescription("Purges data of the given player.") - .withArgument("player", "The player to purge", false) - .withArgument("options", "'force' to run without checking if player is registered", true) + .withArgument("player", "The player to purge", MANDATORY) + .withArgument("options", "'force' to run without checking if player is registered", OPTIONAL) .permission(AdminPermission.PURGE_PLAYER) .executableCommand(PurgePlayerCommand.class) .register(); @@ -367,7 +370,7 @@ public class CommandInitializer { "resetlastposition", "resetlastpos") .description("Purge player's last position") .detailedDescription("Purge the last know position of the specified player or all of them.") - .withArgument("player/*", "Player name or * for all players", false) + .withArgument("player/*", "Player name or * for all players", MANDATORY) .permission(AdminPermission.PURGE_LAST_POSITION) .executableCommand(PurgeLastPositionCommand.class) .register(); @@ -388,7 +391,7 @@ public class CommandInitializer { .labels("switchantibot", "toggleantibot", "antibot") .description("Switch AntiBot mode") .detailedDescription("Switch or toggle the AntiBot mode to the specified state.") - .withArgument("mode", "ON / OFF", true) + .withArgument("mode", "ON / OFF", OPTIONAL) .permission(AdminPermission.SWITCH_ANTIBOT) .executableCommand(SwitchAntiBotCommand.class) .register(); @@ -419,7 +422,7 @@ public class CommandInitializer { .description("Converter command") .detailedDescription("Converter command for AuthMeReloaded.") .withArgument("job", "Conversion job: xauth / crazylogin / rakamak / " - + "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", true) + + "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", OPTIONAL) .permission(AdminPermission.CONVERTER) .executableCommand(ConverterCommand.class) .register(); @@ -447,9 +450,9 @@ public class CommandInitializer { .labels("debug", "dbg") .description("Debug features") .detailedDescription("Allows various operations for debugging.") - .withArgument("child", "The child to execute", true) - .withArgument("arg", "argument (depends on debug section)", true) - .withArgument("arg", "argument (depends on debug section)", true) + .withArgument("child", "The child to execute", OPTIONAL) + .withArgument("arg", "argument (depends on debug section)", OPTIONAL) + .withArgument("arg", "argument (depends on debug section)", OPTIONAL) .permission(DebugSectionPermissions.DEBUG_COMMAND) .executableCommand(DebugCommand.class) .register(); @@ -488,8 +491,8 @@ public class CommandInitializer { .labels("add", "addemail", "addmail") .description("Add Email") .detailedDescription("Add a new email address to your account.") - .withArgument("email", "Email address", false) - .withArgument("verifyEmail", "Email address verification", false) + .withArgument("email", "Email address", MANDATORY) + .withArgument("verifyEmail", "Email address verification", MANDATORY) .permission(PlayerPermission.ADD_EMAIL) .executableCommand(AddEmailCommand.class) .register(); @@ -500,8 +503,8 @@ public class CommandInitializer { .labels("change", "changeemail", "changemail") .description("Change Email") .detailedDescription("Change an email address of your account.") - .withArgument("oldEmail", "Old email address", false) - .withArgument("newEmail", "New email address", false) + .withArgument("oldEmail", "Old email address", MANDATORY) + .withArgument("newEmail", "New email address", MANDATORY) .permission(PlayerPermission.CHANGE_EMAIL) .executableCommand(ChangeEmailCommand.class) .register(); @@ -513,7 +516,7 @@ public class CommandInitializer { .description("Recover password using email") .detailedDescription("Recover your account using an Email address by sending a mail containing " + "a new password.") - .withArgument("email", "Email address", false) + .withArgument("email", "Email address", MANDATORY) .permission(PlayerPermission.RECOVER_EMAIL) .executableCommand(RecoverEmailCommand.class) .register(); @@ -524,7 +527,7 @@ public class CommandInitializer { .labels("code") .description("Submit code to recover password") .detailedDescription("Recover your account by submitting a code delivered to your email.") - .withArgument("code", "Recovery code", false) + .withArgument("code", "Recovery code", MANDATORY) .permission(PlayerPermission.RECOVER_EMAIL) .executableCommand(ProcessCodeCommand.class) .register(); @@ -535,7 +538,7 @@ public class CommandInitializer { .labels("setpassword") .description("Set new password after recovery") .detailedDescription("Set a new password after successfully recovering your account.") - .withArgument("password", "New password", false) + .withArgument("password", "New password", MANDATORY) .permission(PlayerPermission.RECOVER_EMAIL) .executableCommand(SetPasswordCommand.class) .register(); @@ -564,7 +567,7 @@ public class CommandInitializer { .labels("add") .description("Enables TOTP") .detailedDescription("Enables two-factor authentication for your account.") - .permission(PlayerPermission.TOGGLE_TOTP_STATUS) + .permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH) .executableCommand(AddTotpCommand.class) .register(); @@ -574,8 +577,8 @@ public class CommandInitializer { .labels("confirm") .description("Enables TOTP after successful code") .detailedDescription("Saves the generated TOTP secret after confirmation.") - .withArgument("code", "Code from the given secret from /totp add", false) - .permission(PlayerPermission.TOGGLE_TOTP_STATUS) + .withArgument("code", "Code from the given secret from /totp add", MANDATORY) + .permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH) .executableCommand(ConfirmTotpCommand.class) .register(); @@ -585,8 +588,8 @@ public class CommandInitializer { .labels("remove") .description("Removes TOTP") .detailedDescription("Disables two-factor authentication for your account.") - .withArgument("code", "Current 2FA code", false) - .permission(PlayerPermission.TOGGLE_TOTP_STATUS) + .withArgument("code", "Current 2FA code", MANDATORY) + .permission(PlayerPermission.DISABLE_TWO_FACTOR_AUTH) .executableCommand(RemoveTotpCommand.class) .register(); @@ -607,7 +610,7 @@ public class CommandInitializer { .labels(helpCommandLabels) .description("View help") .detailedDescription("View detailed help for /" + base.getLabels().get(0) + " commands.") - .withArgument("query", "The command or query to view help for.", true) + .withArgument("query", "The command or query to view help for.", OPTIONAL) .executableCommand(HelpCommand.class) .register(); } diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java index 44eef5d81..8fc9aaf10 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java @@ -4,8 +4,8 @@ import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.security.TotpService; -import fr.xephi.authme.security.TotpService.TotpGenerationResult; +import fr.xephi.authme.security.totp.GenerateTotpService; +import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import fr.xephi.authme.service.CommonService; import org.bukkit.ChatColor; import org.bukkit.entity.Player; @@ -19,7 +19,7 @@ import java.util.List; public class AddTotpCommand extends PlayerCommand { @Inject - private TotpService totpService; + private GenerateTotpService generateTotpService; @Inject private DataSource dataSource; @@ -31,7 +31,7 @@ public class AddTotpCommand extends PlayerCommand { protected void runCommand(Player player, List arguments) { PlayerAuth auth = dataSource.getAuth(player.getName()); if (auth.getTotpKey() == null) { - TotpGenerationResult createdTotpInfo = totpService.generateTotpKey(player); + TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player); commonService.send(player, MessageKey.TWO_FACTOR_CREATE, createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl()); } else { diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java index 1bf59f7d8..34bdf56d2 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java @@ -2,7 +2,8 @@ package fr.xephi.authme.command.executable.totp; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.security.TotpService; +import fr.xephi.authme.security.totp.GenerateTotpService; +import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -14,7 +15,7 @@ import java.util.List; public class ConfirmTotpCommand extends PlayerCommand { @Inject - private TotpService totpService; + private GenerateTotpService generateTotpService; @Inject private DataSource dataSource; @@ -23,13 +24,14 @@ public class ConfirmTotpCommand extends PlayerCommand { protected void runCommand(Player player, List arguments) { // TODO #1141: Check if player already has TOTP - final String totpKey = totpService.retrieveGeneratedSecret(player); - if (totpKey == null) { + final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player); + if (totpDetails == null) { player.sendMessage("No TOTP key has been generated for you or it has expired. Please run /totp add"); } else { - boolean isTotpCodeValid = totpService.confirmCodeForGeneratedTotpKey(player, arguments.get(0)); - if (isTotpCodeValid) { - dataSource.setTotpKey(player.getName(), totpKey); + boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, arguments.get(0)); + if (isCodeValid) { + generateTotpService.removeGenerateTotpKey(player); + dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey()); player.sendMessage("Successfully enabled two-factor authentication for your account"); } else { player.sendMessage("Wrong code or code has expired. Please use /totp add again"); diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java index abf0b79ca..0a2a371d6 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java @@ -3,7 +3,7 @@ package fr.xephi.authme.command.executable.totp; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.security.TotpService; +import fr.xephi.authme.security.totp.TotpService; import org.bukkit.entity.Player; import javax.inject.Inject; diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java index 12a4a4229..33b5fd1e4 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java @@ -71,9 +71,14 @@ public enum PlayerPermission implements PermissionNode { VERIFICATION_CODE("authme.player.security.verificationcode"), /** - * Permission to enable and disable TOTP. + * Permission to enable two-factor authentication. */ - TOGGLE_TOTP_STATUS("authme.player.totp"); + ENABLE_TWO_FACTOR_AUTH("authme.player.totpadd"), + + /** + * Permission to disable two-factor authentication. + */ + DISABLE_TWO_FACTOR_AUTH("authme.player.totpremove"); /** * The permission node. diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index 6cadf820b..629ce1aa6 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -18,7 +18,7 @@ import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.security.TotpService; +import fr.xephi.authme.security.totp.TotpService; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.SessionService; diff --git a/src/main/java/fr/xephi/authme/security/TotpService.java b/src/main/java/fr/xephi/authme/security/TotpService.java deleted file mode 100644 index dd8257b68..000000000 --- a/src/main/java/fr/xephi/authme/security/TotpService.java +++ /dev/null @@ -1,113 +0,0 @@ -package fr.xephi.authme.security; - -import com.warrenstrange.googleauth.GoogleAuthenticator; -import com.warrenstrange.googleauth.GoogleAuthenticatorKey; -import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.initialization.HasCleanup; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.util.expiring.ExpiringMap; -import org.bukkit.entity.Player; - -import javax.inject.Inject; -import java.util.concurrent.TimeUnit; - -/** - * Service for TOTP actions. - */ -public class TotpService implements HasCleanup { - - private static final int NEW_TOTP_KEY_EXPIRATION_MINUTES = 5; - - private final ExpiringMap totpKeys; - private final GoogleAuthenticator authenticator; - private final BukkitService bukkitService; - - @Inject - TotpService(BukkitService bukkitService) { - this.bukkitService = bukkitService; - this.totpKeys = new ExpiringMap<>(NEW_TOTP_KEY_EXPIRATION_MINUTES, TimeUnit.MINUTES); - this.authenticator = new GoogleAuthenticator(); - } - - /** - * Generates a new TOTP key and returns the corresponding QR code. The generated key is saved temporarily - * for the user and can be later retrieved with a confirmation code from {@link #confirmCodeForGeneratedTotpKey}. - * - * @param player the player to save the TOTP key for - * @return TOTP generation result - */ - public TotpGenerationResult generateTotpKey(Player player) { - GoogleAuthenticatorKey credentials = authenticator.createCredentials(); - totpKeys.put(player.getName().toLowerCase(), credentials.getKey()); - String qrCodeUrl = GoogleAuthenticatorQRGenerator.getOtpAuthURL( - bukkitService.getIp(), player.getName(), credentials); - return new TotpGenerationResult(credentials.getKey(), qrCodeUrl); - } - - /** - * Returns the generated TOTP secret of a player, if available and not yet expired. - * - * @param player the player to retrieve the TOTP key for - * @return the totp secret - */ - public String retrieveGeneratedSecret(Player player) { - return totpKeys.get(player.getName().toLowerCase()); - } - - /** - * Returns if the new totp code matches the newly generated totp key. - * - * @param player the player to retrieve the code for - * @param totpCodeConfirmation the input code confirmation - * @return the TOTP secret that was generated for the player, or null if not available or if the code is incorrect - */ - // Maybe by allowing to retrieve without confirmation and exposing verifyCode(String, String) - public boolean confirmCodeForGeneratedTotpKey(Player player, String totpCodeConfirmation) { - String totpSecret = totpKeys.get(player.getName().toLowerCase()); - if (totpSecret != null) { - if (checkCode(totpSecret, totpCodeConfirmation)) { - totpKeys.remove(player.getName().toLowerCase()); - return true; - } - } - return false; - } - - public boolean verifyCode(PlayerAuth auth, String totpCode) { - return checkCode(auth.getTotpKey(), totpCode); - } - - private boolean checkCode(String totpKey, String inputCode) { - try { - Integer totpCode = Integer.valueOf(inputCode); - return authenticator.authorize(totpKey, totpCode); - } catch (NumberFormatException e) { - // ignore - } - return false; - } - - @Override - public void performCleanup() { - totpKeys.removeExpiredEntries(); - } - - public static final class TotpGenerationResult { - private final String totpKey; - private final String authenticatorQrCodeUrl; - - TotpGenerationResult(String totpKey, String authenticatorQrCodeUrl) { - this.totpKey = totpKey; - this.authenticatorQrCodeUrl = authenticatorQrCodeUrl; - } - - public String getTotpKey() { - return totpKey; - } - - public String getAuthenticatorQrCodeUrl() { - return authenticatorQrCodeUrl; - } - } -} diff --git a/src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java b/src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java new file mode 100644 index 000000000..14a6a6bbb --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java @@ -0,0 +1,69 @@ +package fr.xephi.authme.security.totp; + +import fr.xephi.authme.initialization.HasCleanup; +import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; +import fr.xephi.authme.util.expiring.ExpiringMap; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.util.concurrent.TimeUnit; + +/** + * Handles the generation of new TOTP secrets for players. + */ +public class GenerateTotpService implements HasCleanup { + + private static final int NEW_TOTP_KEY_EXPIRATION_MINUTES = 5; + + private final ExpiringMap totpKeys; + + @Inject + private TotpAuthenticator totpAuthenticator; + + GenerateTotpService() { + this.totpKeys = new ExpiringMap<>(NEW_TOTP_KEY_EXPIRATION_MINUTES, TimeUnit.MINUTES); + } + + /** + * Generates a new TOTP key and returns the corresponding QR code. + * + * @param player the player to save the TOTP key for + * @return TOTP generation result + */ + public TotpGenerationResult generateTotpKey(Player player) { + TotpGenerationResult credentials = totpAuthenticator.generateTotpKey(player); + totpKeys.put(player.getName().toLowerCase(), credentials); + return credentials; + } + + /** + * Returns the generated TOTP secret of a player, if available and not yet expired. + * + * @param player the player to retrieve the TOTP key for + * @return TOTP generation result + */ + public TotpGenerationResult getGeneratedTotpKey(Player player) { + return totpKeys.get(player.getName().toLowerCase()); + } + + public void removeGenerateTotpKey(Player player) { + totpKeys.remove(player.getName().toLowerCase()); + } + + /** + * Returns whether the given totp code is correct for the generated TOTP key of the player. + * + * @param player the player to verify the code for + * @param totpCode the totp code to verify with the generated secret + * @return true if the input code is correct, false if the code is invalid or no unexpired totp key is available + */ + public boolean isTotpCodeCorrectForGeneratedTotpKey(Player player, String totpCode) { + TotpGenerationResult totpDetails = totpKeys.get(player.getName().toLowerCase()); + return totpDetails != null && totpAuthenticator.checkCode(totpDetails.getTotpKey(), totpCode); + } + + @Override + public void performCleanup() { + totpKeys.removeExpiredEntries(); + } +} diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java new file mode 100644 index 000000000..ed31da3a7 --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java @@ -0,0 +1,67 @@ +package fr.xephi.authme.security.totp; + +import com.google.common.annotations.VisibleForTesting; +import com.warrenstrange.googleauth.GoogleAuthenticator; +import com.warrenstrange.googleauth.GoogleAuthenticatorKey; +import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; +import com.warrenstrange.googleauth.IGoogleAuthenticator; +import fr.xephi.authme.service.BukkitService; +import org.bukkit.entity.Player; + +import javax.inject.Inject; + +/** + * Provides rudimentary TOTP functions (wraps third-party TOTP implementation). + */ +public class TotpAuthenticator { + + private final IGoogleAuthenticator authenticator; + private final BukkitService bukkitService; + + + @Inject + TotpAuthenticator(BukkitService bukkitService) { + this(new GoogleAuthenticator(), bukkitService); + } + + @VisibleForTesting + TotpAuthenticator(IGoogleAuthenticator authenticator, BukkitService bukkitService) { + this.authenticator = authenticator; + this.bukkitService = bukkitService; + } + + public boolean checkCode(String totpKey, String inputCode) { + try { + Integer totpCode = Integer.valueOf(inputCode); + return authenticator.authorize(totpKey, totpCode); + } catch (NumberFormatException e) { + // ignore + } + return false; + } + + public TotpGenerationResult generateTotpKey(Player player) { + GoogleAuthenticatorKey credentials = authenticator.createCredentials(); + String qrCodeUrl = GoogleAuthenticatorQRGenerator.getOtpAuthURL( + bukkitService.getIp(), player.getName(), credentials); + return new TotpGenerationResult(credentials.getKey(), qrCodeUrl); + } + + public static final class TotpGenerationResult { + private final String totpKey; + private final String authenticatorQrCodeUrl; + + TotpGenerationResult(String totpKey, String authenticatorQrCodeUrl) { + this.totpKey = totpKey; + this.authenticatorQrCodeUrl = authenticatorQrCodeUrl; + } + + public String getTotpKey() { + return totpKey; + } + + public String getAuthenticatorQrCodeUrl() { + return authenticatorQrCodeUrl; + } + } +} diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpService.java b/src/main/java/fr/xephi/authme/security/totp/TotpService.java new file mode 100644 index 000000000..15e3381f0 --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/totp/TotpService.java @@ -0,0 +1,18 @@ +package fr.xephi.authme.security.totp; + +import fr.xephi.authme.data.auth.PlayerAuth; + +import javax.inject.Inject; + +/** + * Service for TOTP actions. + */ +public class TotpService { + + @Inject + private TotpAuthenticator totpAuthenticator; + + public boolean verifyCode(PlayerAuth auth, String totpCode) { + return totpAuthenticator.checkCode(auth.getTotpKey(), totpCode); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index e60721de2..bee650789 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -23,7 +23,7 @@ commands: usage: /email show|add|change|recover|code|setpassword login: description: Login command - usage: /login + usage: /login [2facode] aliases: - l - log @@ -48,7 +48,7 @@ commands: - cp totp: description: TOTP commands - usage: /totp add|remove + usage: /totp add|confirm|remove aliases: - 2fa captcha: @@ -238,7 +238,8 @@ permissions: authme.player.register: true authme.player.security.verificationcode: true authme.player.seeownaccounts: true - authme.player.totp: true + authme.player.totpadd: true + authme.player.totpremove: true authme.player.unregister: true authme.player.canbeforced: description: Permission for users a login can be forced to. @@ -283,8 +284,11 @@ permissions: authme.player.seeownaccounts: description: Permission to use to see own other accounts. default: true - authme.player.totp: - description: Permission to enable and disable TOTP. + authme.player.totpadd: + description: Permission to enable two-factor authentication. + default: true + authme.player.totpremove: + description: Permission to disable two-factor authentication. default: true authme.player.unregister: description: Command permission to unregister. diff --git a/src/test/java/fr/xephi/authme/security/TotpServiceTest.java b/src/test/java/fr/xephi/authme/security/TotpServiceTest.java deleted file mode 100644 index 6350b4c4c..000000000 --- a/src/test/java/fr/xephi/authme/security/TotpServiceTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package fr.xephi.authme.security; - -import fr.xephi.authme.security.TotpService.TotpGenerationResult; -import fr.xephi.authme.service.BukkitService; -import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static fr.xephi.authme.AuthMeMatchers.stringWithLength; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Test for {@link TotpService}. - */ -@RunWith(MockitoJUnitRunner.class) -public class TotpServiceTest { - - @InjectMocks - private TotpService totpService; - - @Mock - private BukkitService bukkitService; - - @Test - public void shouldGenerateTotpKey() { - // given - Player player = mock(Player.class); - given(player.getName()).willReturn("Bobby"); - given(bukkitService.getIp()).willReturn("127.48.44.4"); - - // when - TotpGenerationResult key1 = totpService.generateTotpKey(player); - TotpGenerationResult key2 = totpService.generateTotpKey(player); - - // then - assertThat(key1.getTotpKey(), stringWithLength(16)); - assertThat(key2.getTotpKey(), stringWithLength(16)); - assertThat(key1.getAuthenticatorQrCodeUrl(), startsWith("https://chart.googleapis.com/chart?chs=200x200")); - assertThat(key2.getAuthenticatorQrCodeUrl(), startsWith("https://chart.googleapis.com/chart?chs=200x200")); - assertThat(key1.getTotpKey(), not(equalTo(key2.getTotpKey()))); - } - -} diff --git a/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java b/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java new file mode 100644 index 000000000..1a22d26e7 --- /dev/null +++ b/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java @@ -0,0 +1,113 @@ +package fr.xephi.authme.security.totp; + +import fr.xephi.authme.ReflectionTestUtils; +import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; +import fr.xephi.authme.util.expiring.ExpiringMap; +import org.bukkit.entity.Player; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link GenerateTotpService}. + */ +@RunWith(MockitoJUnitRunner.class) +public class GenerateTotpServiceTest { + + @InjectMocks + private GenerateTotpService generateTotpService; + + @Mock + private TotpAuthenticator totpAuthenticator; + + @Test + public void shouldGenerateTotpKey() { + // given + TotpGenerationResult givenGenerationResult = new TotpGenerationResult("1234", "http://example.com/link/to/chart"); + Player player = mockPlayerWithName("Spencer"); + given(totpAuthenticator.generateTotpKey(player)).willReturn(givenGenerationResult); + + // when + TotpGenerationResult result = generateTotpService.generateTotpKey(player); + + // then + assertThat(result, equalTo(givenGenerationResult)); + assertThat(generateTotpService.getGeneratedTotpKey(player), equalTo(givenGenerationResult)); + } + + @Test + public void shouldRemoveGeneratedTotpKey() { + // given + TotpGenerationResult givenGenerationResult = new TotpGenerationResult("1234", "http://example.com/link/to/chart"); + Player player = mockPlayerWithName("Hanna"); + given(totpAuthenticator.generateTotpKey(player)).willReturn(givenGenerationResult); + generateTotpService.generateTotpKey(player); + + // when + generateTotpService.removeGenerateTotpKey(player); + + // then + assertThat(generateTotpService.getGeneratedTotpKey(player), nullValue()); + } + + @Test + public void shouldCheckGeneratedTotpKey() { + // given + String generatedKey = "ASLO43KDF2J"; + TotpGenerationResult givenGenerationResult = new TotpGenerationResult(generatedKey, "url"); + Player player = mockPlayerWithName("Aria"); + given(totpAuthenticator.generateTotpKey(player)).willReturn(givenGenerationResult); + generateTotpService.generateTotpKey(player); + String validCode = "928374"; + given(totpAuthenticator.checkCode(generatedKey, validCode)).willReturn(true); + + // when + boolean invalidCodeResult = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, "000000"); + boolean validCodeResult = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, validCode); + boolean unknownPlayerResult = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(mockPlayerWithName("other"), "299874"); + + // then + assertThat(invalidCodeResult, equalTo(false)); + assertThat(validCodeResult, equalTo(true)); + assertThat(unknownPlayerResult, equalTo(false)); + verify(totpAuthenticator).checkCode(generatedKey, "000000"); + verify(totpAuthenticator).checkCode(generatedKey, validCode); + } + + @Test + public void shouldRemoveExpiredEntries() throws InterruptedException { + // given + TotpGenerationResult generationResult = new TotpGenerationResult("key", "url"); + ExpiringMap generatedKeys = + ReflectionTestUtils.getFieldValue(GenerateTotpService.class, generateTotpService, "totpKeys"); + generatedKeys.setExpiration(1, TimeUnit.MILLISECONDS); + generatedKeys.put("ghost", generationResult); + generatedKeys.setExpiration(5, TimeUnit.MINUTES); + generatedKeys.put("ezra", generationResult); + + // when + Thread.sleep(2L); + generateTotpService.performCleanup(); + + // then + assertThat(generateTotpService.getGeneratedTotpKey(mockPlayerWithName("Ezra")), equalTo(generationResult)); + assertThat(generateTotpService.getGeneratedTotpKey(mockPlayerWithName("ghost")), nullValue()); + } + + private static Player mockPlayerWithName(String name) { + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + return player; + } +} diff --git a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java new file mode 100644 index 000000000..eb2746fa0 --- /dev/null +++ b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java @@ -0,0 +1,84 @@ +package fr.xephi.authme.security.totp; + +import com.warrenstrange.googleauth.IGoogleAuthenticator; +import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; +import fr.xephi.authme.service.BukkitService; +import org.bukkit.entity.Player; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static fr.xephi.authme.AuthMeMatchers.stringWithLength; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link TotpAuthenticator}. + */ +@RunWith(MockitoJUnitRunner.class) +public class TotpAuthenticatorTest { + + @InjectMocks + private TotpAuthenticator totpAuthenticator; + + @Mock + private BukkitService bukkitService; + + @Mock + private IGoogleAuthenticator googleAuthenticator; + + @Test + public void shouldGenerateTotpKey() { + // given + // Use the GoogleAuthenticator instance the TotpAuthenticator normally creates to test its parameters + totpAuthenticator = new TotpAuthenticator(bukkitService); + + Player player = mock(Player.class); + given(player.getName()).willReturn("Bobby"); + given(bukkitService.getIp()).willReturn("127.48.44.4"); + + // when + TotpGenerationResult key1 = totpAuthenticator.generateTotpKey(player); + TotpGenerationResult key2 = totpAuthenticator.generateTotpKey(player); + + // then + assertThat(key1.getTotpKey(), stringWithLength(16)); + assertThat(key2.getTotpKey(), stringWithLength(16)); + assertThat(key1.getAuthenticatorQrCodeUrl(), startsWith("https://chart.googleapis.com/chart?chs=200x200")); + assertThat(key2.getAuthenticatorQrCodeUrl(), startsWith("https://chart.googleapis.com/chart?chs=200x200")); + assertThat(key1.getTotpKey(), not(equalTo(key2.getTotpKey()))); + } + + @Test + public void shouldCheckCode() { + // given + String secret = "the_secret"; + int code = 21398; + given(googleAuthenticator.authorize(secret, code)).willReturn(true); + + // when + boolean result = totpAuthenticator.checkCode(secret, Integer.toString(code)); + + // then + assertThat(result, equalTo(true)); + verify(googleAuthenticator).authorize(secret, code); + } + + @Test + public void shouldHandleInvalidNumberInput() { + // given / when + boolean result = totpAuthenticator.checkCode("Some_Secret", "123ZZ"); + + // then + assertThat(result, equalTo(false)); + verifyZeroInteractions(googleAuthenticator); + } +} diff --git a/src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java b/src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java new file mode 100644 index 000000000..7d321cf19 --- /dev/null +++ b/src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java @@ -0,0 +1,46 @@ +package fr.xephi.authme.security.totp; + +import fr.xephi.authme.data.auth.PlayerAuth; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link TotpService}. + */ +@RunWith(MockitoJUnitRunner.class) +public class TotpServiceTest { + + @InjectMocks + private TotpService totpService; + + @Mock + private TotpAuthenticator totpAuthenticator; + + @Test + public void shouldVerifyCode() { + // given + String totpKey = "ASLO43KDF2J"; + PlayerAuth auth = PlayerAuth.builder() + .name("Maya") + .totpKey(totpKey) + .build(); + String inputCode = "408435"; + given(totpAuthenticator.checkCode(totpKey, inputCode)).willReturn(true); + + // when + boolean result = totpService.verifyCode(auth, inputCode); + + // then + assertThat(result, equalTo(true)); + verify(totpAuthenticator).checkCode(totpKey, inputCode); + } + +} diff --git a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java b/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java index 3e094cc2b..9159b4248 100644 --- a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java @@ -126,7 +126,7 @@ public class HelpTranslationGeneratorIntegrationTest { // Check /login checkDescription(configuration.get("commands.login"), "Login command", "/login detailed desc."); - checkArgs(configuration.get("commands.login"), arg("loginArg", "Login password")); + checkArgs(configuration.get("commands.login"), arg("loginArg", "Login password"), arg("2faArg", "TOTP code")); // Check /unregister checkDescription(configuration.get("commands.unregister"), "unreg_desc", "unreg_detail_desc"); diff --git a/src/test/resources/fr/xephi/authme/message/help_test.yml b/src/test/resources/fr/xephi/authme/message/help_test.yml index d6970daa7..9647ecd7a 100644 --- a/src/test/resources/fr/xephi/authme/message/help_test.yml +++ b/src/test/resources/fr/xephi/authme/message/help_test.yml @@ -65,6 +65,9 @@ commands: arg1: label: loginArg nonExistent: does not exist + arg2: + label: 2faArg + rhetorical: false someProp: label: '''someProp'' does not exist' description: idk From 1a53cd11b2caa770ebb47262ed804e575670156c Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 10 Mar 2018 16:58:30 +0100 Subject: [PATCH 066/155] #829 Move CommandSender / name to first argument to avoid confusion with varargs - Overloaded method with (String, String...) as args was problematic as it could be confusing on its own and also confusing with the (CommandSender, String...) flavor --- .../executable/authme/RegisterAdminCommand.java | 2 +- .../fr/xephi/authme/data/TempbanManager.java | 2 +- .../data/limbo/LimboPlayerTaskManager.java | 4 ++-- .../fr/xephi/authme/listener/OnJoinVerifier.java | 6 +++--- .../fr/xephi/authme/listener/PlayerListener.java | 4 ++-- .../java/fr/xephi/authme/message/Messages.java | 10 +++++----- .../authme/process/join/AsynchronousJoin.java | 4 ++-- .../authme/process/login/AsynchronousLogin.java | 2 +- .../register/ProcessSyncPasswordRegister.java | 2 +- .../fr/xephi/authme/service/CommonService.java | 6 +++--- .../authme/RegisterAdminCommandTest.java | 2 +- .../fr/xephi/authme/data/TempbanManagerTest.java | 4 ++-- .../data/limbo/LimboPlayerTaskManagerTest.java | 16 ++++++++-------- .../authme/listener/OnJoinVerifierTest.java | 6 +++--- .../authme/listener/PlayerListenerTest.java | 2 +- .../authme/message/MessagesIntegrationTest.java | 6 +++--- .../xephi/authme/service/CommonServiceTest.java | 6 +++--- 17 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index 5c28fdc69..e05089da8 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -74,7 +74,7 @@ public class RegisterAdminCommand implements ExecutableCommand { final Player player = bukkitService.getPlayerExact(playerName); if (player != null) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> - player.kickPlayer(commonService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER, player))); + player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER))); } }); } diff --git a/src/main/java/fr/xephi/authme/data/TempbanManager.java b/src/main/java/fr/xephi/authme/data/TempbanManager.java index 288864376..e6b685aca 100644 --- a/src/main/java/fr/xephi/authme/data/TempbanManager.java +++ b/src/main/java/fr/xephi/authme/data/TempbanManager.java @@ -97,7 +97,7 @@ public class TempbanManager implements SettingsDependent, HasCleanup { if (isEnabled) { final String name = player.getName(); final String ip = PlayerUtils.getPlayerIp(player); - final String reason = messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS, player); + final String reason = messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS); final Date expires = new Date(); long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE); diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java index f8e2e7baf..e5f4f65d0 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java @@ -51,7 +51,7 @@ class LimboPlayerTaskManager { int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL); MessageResult result = getMessageKey(player.getName(), isRegistered); if (interval > 0) { - String[] joinMessage = messages.retrieveSingle(result.messageKey, player, result.args).split("\n"); + String[] joinMessage = messages.retrieveSingle(player, result.messageKey, result.args).split("\n"); MessageTask messageTask = new MessageTask(player, joinMessage); bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND); limbo.setMessageTask(messageTask); @@ -67,7 +67,7 @@ class LimboPlayerTaskManager { void registerTimeoutTask(Player player, LimboPlayer limbo) { final int timeout = settings.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; if (timeout > 0) { - String message = messages.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR, player); + String message = messages.retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR); BukkitTask task = bukkitService.runTaskLater(new TimeoutTask(player, message, playerCache), timeout); limbo.setTimeoutTask(task); } diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index c10b8fcc5..c1c30cf94 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -123,7 +123,7 @@ public class OnJoinVerifier implements Reloadable { return false; } else if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { // Server is full and player is NOT VIP; set kick message and proceed with kick - event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)); + event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)); return true; } @@ -135,12 +135,12 @@ public class OnJoinVerifier implements Reloadable { } Player nonVipPlayer = generateKickPlayer(onlinePlayers); if (nonVipPlayer != null) { - nonVipPlayer.kickPlayer(messages.retrieveSingle(MessageKey.KICK_FOR_VIP, player)); + nonVipPlayer.kickPlayer(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP)); event.allow(); return false; } else { ConsoleLogger.info("VIP player " + player.getName() + " tried to join, but the server was full"); - event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)); + event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)); return true; } } diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 6482cd637..e48ff6be2 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -257,7 +257,7 @@ public class PlayerListener implements Listener { try { runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress()); } catch (FailedVerificationException e) { - event.setKickMessage(m.retrieveSingle(e.getReason(), name, e.getArgs())); + event.setKickMessage(m.retrieveSingle(name, e.getReason(), e.getArgs())); event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); } } @@ -285,7 +285,7 @@ public class PlayerListener implements Listener { try { runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress()); } catch (FailedVerificationException e) { - event.setKickMessage(m.retrieveSingle(e.getReason(), player, e.getArgs())); + event.setKickMessage(m.retrieveSingle(player, e.getReason(), e.getArgs())); event.setResult(PlayerLoginEvent.Result.KICK_OTHER); } } diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 69be2687e..be887276f 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -70,7 +70,7 @@ public class Messages { * @param replacements The replacements to apply for the tags */ public void send(CommandSender sender, MessageKey key, String... replacements) { - String message = retrieveSingle(key, sender, replacements); + String message = retrieveSingle(sender, key, replacements); for (String line : message.split("\n")) { sender.sendMessage(line); } @@ -149,12 +149,12 @@ public class Messages { * logs an error if the number of supplied replacements doesn't correspond to the number of tags * the message key contains. * - * @param key The key of the message to send * @param sender The entity to send the message to + * @param key The key of the message to send * @param replacements The replacements to apply for the tags * @return The message from the file with replacements */ - public String retrieveSingle(MessageKey key, CommandSender sender, String... replacements) { + public String retrieveSingle(CommandSender sender, MessageKey key, String... replacements) { String message = retrieveMessage(key, sender); String[] tags = key.getTags(); if (replacements.length == tags.length) { @@ -172,12 +172,12 @@ public class Messages { * logs an error if the number of supplied replacements doesn't correspond to the number of tags * the message key contains. * - * @param key The key of the message to send * @param name The name of the entity to send the message to + * @param key The key of the message to send * @param replacements The replacements to apply for the tags * @return The message from the file with replacements */ - public String retrieveSingle(MessageKey key, String name, String... replacements) { + public String retrieveSingle(String name, MessageKey key, String... replacements) { String message = retrieveMessage(key, name); String[] tags = key.getTags(); if (replacements.length == tags.length) { diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index da9e5127d..7fb13bac6 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -138,7 +138,7 @@ public class AsynchronousJoin implements AsynchronousProcess { private void handlePlayerWithUnmetNameRestriction(Player player, String ip) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { - player.kickPlayer(service.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR, player)); + player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR)); if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) { server.banIP(ip); } @@ -188,7 +188,7 @@ public class AsynchronousJoin implements AsynchronousProcess { && countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( - () -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE, player))); + () -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.SAME_IP_ONLINE))); return false; } return true; diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index 2731c59a1..193332501 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -197,7 +197,7 @@ public class AsynchronousLogin implements AsynchronousProcess { tempbanManager.tempbanPlayer(player); } else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( - () -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD, player))); + () -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.WRONG_PASSWORD))); } else { service.send(player, MessageKey.WRONG_PASSWORD); diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index f59bb28b1..ea5c028ef 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -58,7 +58,7 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { // Kick Player after Registration is enabled, kick the player if (service.getProperty(RegistrationSettings.FORCE_KICK_AFTER_REGISTER)) { - player.kickPlayer(service.retrieveSingleMessage(MessageKey.REGISTER_SUCCESS, player)); + player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.REGISTER_SUCCESS)); return; } diff --git a/src/main/java/fr/xephi/authme/service/CommonService.java b/src/main/java/fr/xephi/authme/service/CommonService.java index ea63c58e1..92a49267b 100644 --- a/src/main/java/fr/xephi/authme/service/CommonService.java +++ b/src/main/java/fr/xephi/authme/service/CommonService.java @@ -63,12 +63,12 @@ public class CommonService { /** * Retrieves a message in one piece. * - * @param key the key of the message * @param sender The entity to send the message to + * @param key the key of the message * @return the message */ - public String retrieveSingleMessage(MessageKey key, CommandSender sender) { - return messages.retrieveSingle(key, sender); + public String retrieveSingleMessage(CommandSender sender, MessageKey key) { + return messages.retrieveSingle(sender, key); } /** diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java index 162de38ac..92492b99f 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java @@ -160,7 +160,7 @@ public class RegisterAdminCommandTest { Player player = mock(Player.class); given(bukkitService.getPlayerExact(user)).willReturn(player); String kickForAdminRegister = "Admin registered you -- log in again"; - given(commandService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER, player)).willReturn(kickForAdminRegister); + given(commandService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister); CommandSender sender = mock(CommandSender.class); setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); setBukkitServiceToRunTaskOptionallyAsync(bukkitService); diff --git a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java b/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java index 5b1da8c7a..154098e88 100644 --- a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java @@ -149,7 +149,7 @@ public class TempbanManagerTest { String ip = "123.45.67.89"; TestHelper.mockPlayerIp(player, ip); String banReason = "IP ban too many logins"; - given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS, player)).willReturn(banReason); + given(messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); Settings settings = mockSettings(2, 100, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); setBukkitServiceToScheduleSyncDelayedTask(bukkitService); @@ -195,7 +195,7 @@ public class TempbanManagerTest { String ip = "22.44.66.88"; TestHelper.mockPlayerIp(player, ip); String banReason = "kick msg"; - given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS, player)).willReturn(banReason); + given(messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); Settings settings = mockSettings(10, 60, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); manager.increaseCount(ip, "user"); diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java index 79fa4f4dc..f1507a29e 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java @@ -71,7 +71,7 @@ public class LimboPlayerTaskManagerTest { Player player = mock(Player.class); LimboPlayer limboPlayer = mock(LimboPlayer.class); MessageKey key = MessageKey.REGISTER_MESSAGE; - given(messages.retrieveSingle(key, player)).willReturn("Please register!"); + given(messages.retrieveSingle(player, key)).willReturn("Please register!"); int interval = 12; given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(interval); @@ -80,7 +80,7 @@ public class LimboPlayerTaskManagerTest { // then verify(limboPlayer).setMessageTask(any(MessageTask.class)); - verify(messages).retrieveSingle(key, player); + verify(messages).retrieveSingle(player, key); verify(bukkitService).runTaskTimer( any(MessageTask.class), eq(2L * TICKS_PER_SECOND), eq((long) interval * TICKS_PER_SECOND)); } @@ -110,7 +110,7 @@ public class LimboPlayerTaskManagerTest { MessageTask existingMessageTask = mock(MessageTask.class); limboPlayer.setMessageTask(existingMessageTask); given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(8); - given(messages.retrieveSingle(MessageKey.REGISTER_MESSAGE, player)).willReturn("Please register!"); + given(messages.retrieveSingle(player, MessageKey.REGISTER_MESSAGE)).willReturn("Please register!"); // when limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); @@ -119,7 +119,7 @@ public class LimboPlayerTaskManagerTest { assertThat(limboPlayer.getMessageTask(), not(nullValue())); assertThat(limboPlayer.getMessageTask(), not(sameInstance(existingMessageTask))); verify(registrationCaptchaManager).isCaptchaRequired(name); - verify(messages).retrieveSingle(MessageKey.REGISTER_MESSAGE, player); + verify(messages).retrieveSingle(player, MessageKey.REGISTER_MESSAGE); verify(existingMessageTask).cancel(); } @@ -134,14 +134,14 @@ public class LimboPlayerTaskManagerTest { given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true); String captcha = "M032"; given(registrationCaptchaManager.getCaptchaCodeOrGenerateNew(name)).willReturn(captcha); - given(messages.retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, player, captcha)).willReturn("Need to use captcha"); + given(messages.retrieveSingle(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha)).willReturn("Need to use captcha"); // when limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); // then assertThat(limboPlayer.getMessageTask(), not(nullValue())); - verify(messages).retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, player, captcha); + verify(messages).retrieveSingle(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha); } @Test @@ -159,7 +159,7 @@ public class LimboPlayerTaskManagerTest { // then verify(limboPlayer).setTimeoutTask(bukkitTask); verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(600L)); // 30 * TICKS_PER_SECOND - verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR, player); + verify(messages).retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR); } @Test @@ -194,7 +194,7 @@ public class LimboPlayerTaskManagerTest { verify(existingTask).cancel(); assertThat(limboPlayer.getTimeoutTask(), equalTo(bukkitTask)); verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(360L)); // 18 * TICKS_PER_SECOND - verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR, player); + verify(messages).retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR); } } diff --git a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java index 7d8c95176..2f6121a90 100644 --- a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java +++ b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java @@ -101,7 +101,7 @@ public class OnJoinVerifierTest { event.setResult(PlayerLoginEvent.Result.KICK_FULL); given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(false); String serverFullMessage = "server is full"; - given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)).willReturn(serverFullMessage); + given(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)).willReturn(serverFullMessage); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); @@ -125,7 +125,7 @@ public class OnJoinVerifierTest { given(permissionsManager.hasPermission(onlinePlayers.get(1), PlayerStatePermission.IS_VIP)).willReturn(false); returnOnlineListFromBukkitServer(onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); - given(messages.retrieveSingle(MessageKey.KICK_FOR_VIP, player)).willReturn("kick for vip"); + given(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP)).willReturn("kick for vip"); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); @@ -149,7 +149,7 @@ public class OnJoinVerifierTest { given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true); returnOnlineListFromBukkitServer(onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); - given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER, player)).willReturn("kick full server"); + given(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)).willReturn("kick full server"); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index 9fd750ff7..3d75e2c52 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -596,7 +596,7 @@ public class PlayerListenerTest { MessageKey.INVALID_NAME_CHARACTERS, "[a-z]"); doThrow(exception).when(onJoinVerifier).checkIsValidName(name); String message = "Invalid characters!"; - given(messages.retrieveSingle(exception.getReason(), player, exception.getArgs())).willReturn(message); + given(messages.retrieveSingle(player, exception.getReason(), exception.getArgs())).willReturn(message); // when listener.onPlayerLogin(event); diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 125ad36af..d8a2bd00b 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -100,7 +100,7 @@ public class MessagesIntegrationTest { given(sender.getName()).willReturn("Tester"); // when - String message = messages.retrieveSingle(key, sender); + String message = messages.retrieveSingle(sender, key); // then assertThat(message, equalTo("We've got\nnew lines\nand ' apostrophes")); @@ -252,7 +252,7 @@ public class MessagesIntegrationTest { given(sender.getName()).willReturn("Tester"); // when - String message = messages.retrieveSingle(key, sender); + String message = messages.retrieveSingle(sender, key); // then assertThat(message, equalTo("§cWrong password!")); @@ -266,7 +266,7 @@ public class MessagesIntegrationTest { given(sender.getName()).willReturn("Tester"); // when - String result = messages.retrieveSingle(key, sender.getName(), "24680"); + String result = messages.retrieveSingle(sender.getName(), key, "24680"); // then assertThat(result, equalTo("Use /captcha 24680 to solve the captcha")); diff --git a/src/test/java/fr/xephi/authme/service/CommonServiceTest.java b/src/test/java/fr/xephi/authme/service/CommonServiceTest.java index e4ff2817b..b74762dc2 100644 --- a/src/test/java/fr/xephi/authme/service/CommonServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/CommonServiceTest.java @@ -85,14 +85,14 @@ public class CommonServiceTest { MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; Player player = mock(Player.class); String text = "Test text"; - given(messages.retrieveSingle(key, player)).willReturn(text); + given(messages.retrieveSingle(player, key)).willReturn(text); // when - String result = commonService.retrieveSingleMessage(key, player); + String result = commonService.retrieveSingleMessage(player, key); // then assertThat(result, equalTo(text)); - verify(messages).retrieveSingle(key, player); + verify(messages).retrieveSingle(player, key); } @Test From fe4d5e7c0df6864c7f060149bb04c0481f49221d Mon Sep 17 00:00:00 2001 From: HexelDev Date: Sat, 10 Mar 2018 19:13:23 +0100 Subject: [PATCH 067/155] Saving user join and check on command --- .../fr/xephi/authme/listener/PlayerListener.java | 13 +++++++++++++ .../java/fr/xephi/authme/message/MessageKey.java | 3 +++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index b4709a564..517b12ed0 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -1,5 +1,6 @@ package fr.xephi.authme.listener; +import fr.xephi.authme.data.QuickCommandsProtectionManager; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; @@ -86,6 +87,8 @@ public class PlayerListener implements Listener { @Inject private PermissionsManager permissionsManager; @Inject + private QuickCommandsProtectionManager quickCommandsProtectionManager; + @Inject private BungeeSender bungeeSender; private boolean isAsyncPlayerPreLoginEventCalled = false; @@ -100,6 +103,11 @@ public class PlayerListener implements Listener { return; } final Player player = event.getPlayer(); + if (!quickCommandsProtectionManager.isAllowed(player.getName())) { + event.setCancelled(true); + player.kickPlayer(m.retrieveSingle(MessageKey.QUICK_COMMAND_PROTECTION_KICK)); + return; + } if (listenerService.shouldCancelEvent(player)) { event.setCancelled(true); m.send(player, MessageKey.DENIED_COMMAND); @@ -207,6 +215,11 @@ public class PlayerListener implements Listener { if (!PlayerListener19Spigot.isPlayerSpawnLocationEventCalled()) { teleportationService.teleportOnJoin(player); } + + if (quickCommandsProtectionManager.shouldSaveLogin(player)) { + quickCommandsProtectionManager.setLogin(player.getName()); + } + management.performJoin(player); teleportationService.teleportNewPlayerToFirstSpawn(player); diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index b6367c5b1..8c40fcea8 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -275,6 +275,9 @@ public enum MessageKey { /** To verify your identity you need to link an email address with your account! */ VERIFICATION_CODE_EMAIL_NEEDED("verification.email_needed"), + /** You used a command too fast! Please, join the server again and wait more before using any command. */ + QUICK_COMMAND_PROTECTION_KICK("quick_command.kick"), + /** second */ SECOND("time.second"), From de6b9cbff0b227582b1d73bf56d46f2a0f93fdc5 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sun, 11 Mar 2018 01:20:29 +0100 Subject: [PATCH 068/155] Add LoC badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7d5a5f767..7705cb367 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) - Project status: + - Lines of code: [![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded)](https://github.com/AuthMe/AuthMeReloaded). - Test coverage: [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) - Code climate: [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) From 2219906e90c058a6ae580408919f21b5d3af2eb0 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sun, 11 Mar 2018 01:22:02 +0100 Subject: [PATCH 069/155] Whoops... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7705cb367..1232c46b1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) - Project status: - - Lines of code: [![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded)](https://github.com/AuthMe/AuthMeReloaded). + - Lines of code: [![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded)](https://github.com/AuthMe/AuthMeReloaded) - Test coverage: [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) - Code climate: [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) From ff0a7e1f89065dca0eeff0aecb0d3152fd863068 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 11 Mar 2018 16:34:25 +0100 Subject: [PATCH 070/155] Fix failing test --- src/test/java/fr/xephi/authme/service/SessionServiceTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/fr/xephi/authme/service/SessionServiceTest.java b/src/test/java/fr/xephi/authme/service/SessionServiceTest.java index 441b53561..b631c67e5 100644 --- a/src/test/java/fr/xephi/authme/service/SessionServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/SessionServiceTest.java @@ -218,6 +218,7 @@ public class SessionServiceTest { String name = "Charles"; Player player = mockPlayerWithNameAndIp(name, "144.117.118.145"); given(dataSource.hasSession(name)).willReturn(true); + given(commonService.getProperty(PluginSettings.SESSIONS_TIMEOUT)).willReturn(8); PlayerAuth auth = PlayerAuth.builder() .name(name) .lastIp(null) From f391abe2a702ff161eef789bd6af4f3855cf6dc8 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sun, 11 Mar 2018 18:44:52 +0100 Subject: [PATCH 071/155] New readme [WIP] (#1526) * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md --- README.md | 110 ++++++++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 1232c46b1..2360f2d1d 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,15 @@ -

-

The most used authentication plugin for the Spigot and derivates!

-
+# AuthMeReloaded +**"The best authentication plugin for the Bukkit modding API!"** +AuthMeLogo -##### Links and Contacts: +| Type | Badges | +|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **General:** | ![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded?category=code) ![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded?category=files) | +| **Code quality:** | [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) | +| **Jenkins CI:** | [![Jenkins Status](https://img.shields.io/website-up-down-green-red/http/shields.io.svg?label=ci.codemc.org)](https://ci.codemc.org/) [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded) ![Build Tests](https://img.shields.io/jenkins/t/https/ci.codemc.org/job/AuthMe/job/AuthMeReloaded.svg) | +| **Other CIs:** | [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) | - - Support: - - [GitHub issue tracker](https://github.com/AuthMe/AuthMeReloaded/issues) - - [BukkitDev page](https://dev.bukkit.org/projects/authme-reloaded) - - [Spigot page](https://www.spigotmc.org/resources/authmereloaded.6269/) - - [Discord](https://discord.gg/Vn9eCyE) - -- CI Services: - - Jenkins: [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/AuthMeReloaded) (**DEVELOPMENT BUILDS**) - - CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) - -- Project status: - - Lines of code: [![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded)](https://github.com/AuthMe/AuthMeReloaded) - - Test coverage: [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) - - Code climate: [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) - -- Development resources: - - JavaDocs - - Maven Repository - -- Statistics: - - bStats: [AuthMe on bstats.org](https://bstats.org/plugin/bukkit/AuthMe) - ![Graph](https://bstats.org/signatures/bukkit/AuthMe.svg) - -
- -##### Compiling requirements: ->- JDK 1.8 ->- Maven ->- Git/Github (Optional) - -##### How to compile the project: ->- Clone the project with Git/Github ->- Execute command "mvn clean package" - -##### Running requirements: ->- Java 1.8 ->- TacoSpigot, PaperSpigot or Spigot (1.7.10, 1.8.X, 1.9.X, 1.10.X, 1.11.X, 1.12.X)
- (In case you use Thermos, Cauldron or similar, you have to update the SpecialSource library to support Java 8 plugins. - HowTo: https://github.com/games647/FastLogin/issues/111#issuecomment-272331347) ->- ProtocolLib (optional, required by some features) - -
- -### Plugin Description: - -##### "The best authentication plugin for the Bukkit/Spigot API!" +## Description Prevent username stealing on your server!
Use it to secure your Offline mode server or to increase your Online mode server's protection! @@ -118,15 +78,51 @@ You can also create your own translation file and, if you want, you can share it - [How to convert from Rakamak](https://dev.bukkit.org/projects/authme-reloaded/pages/how-to-import-database-from-rakamak) - Convert between database types (e.g. SQLite to MySQL): /authme converter -
-##### Sponsor +## Links and Contacts + + - **Support:** + - [GitHub issue tracker](https://github.com/AuthMe/AuthMeReloaded/issues) + - [BukkitDev page](https://dev.bukkit.org/projects/authme-reloaded) + - [Spigot page](https://www.spigotmc.org/resources/authmereloaded.6269/) + - [Discord](https://discord.gg/Vn9eCyE) + +- **Dev resources:** + - JavaDocs + - Maven Repository + +- **Statistics:** + ![Graph](https://bstats.org/signatures/bukkit/AuthMe.svg) + +## Requirements + +##### Compiling requirements: +>- JDK 1.8 +>- Maven +>- Git/Github (Optional) + +##### How to compile the project: +>- Clone the project with Git/Github +>- Execute command "mvn clean package" + +##### Running requirements: +>- Java 1.8 +>- TacoSpigot, PaperSpigot or Spigot (1.7.10, 1.8.X, 1.9.X, 1.10.X, 1.11.X, 1.12.X)
+ (In case you use Thermos, Cauldron or similar, you have to update the SpecialSource library to support Java 8 plugins. + HowTo: https://github.com/games647/FastLogin/issues/111#issuecomment-272331347) +>- ProtocolLib (optional, required by some features) + +## Credits + +##### Sponsor: [GameHosting.it](http://www.gamehosting.it) is leader in Italy as Game Server Provider. With its own DataCenter offers Anti-DDoS solutions at affordable prices. Game Server of Minecraft based on Multicraft are equipped with the latest technology in hardware. -##### Credits -

Contributors: developers, translators -

Credit for old version of the plugin to: d4rkwarriors, fabe1337, Whoami2 and pomo4ka

-

Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex

+##### Contributors: +Team members: developers, translators -##### GeoIP License +Credits for the old version of the plugin: d4rkwarriors, fabe1337, Whoami2 and pomo4ka + +Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex + +##### GeoIP License: This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com From 98bd0f7a6b9204a1eb7bc9be47be3ce086a1359e Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sun, 11 Mar 2018 18:54:45 +0100 Subject: [PATCH 072/155] [ci-skip] Add gemnasium badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2360f2d1d..255f36cf9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ | **Code quality:** | [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) | | **Jenkins CI:** | [![Jenkins Status](https://img.shields.io/website-up-down-green-red/http/shields.io.svg?label=ci.codemc.org)](https://ci.codemc.org/) [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded) ![Build Tests](https://img.shields.io/jenkins/t/https/ci.codemc.org/job/AuthMe/job/AuthMeReloaded.svg) | | **Other CIs:** | [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) | +| **Dependencies:** | [![Dependency Status](https://gemnasium.com/badges/github.com/AuthMe/AuthMeReloaded.svg)](https://gemnasium.com/github.com/AuthMe/AuthMeReloaded) | ## Description From fddb3bf265dd0607e5e028bd20776e7670522ba3 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 11 Mar 2018 19:08:21 +0100 Subject: [PATCH 073/155] Fix minor Checkstyle violations - Mostly missing JavaDoc, some line lengths --- .../authme/debug/MySqlDefaultChanger.java | 5 +++ .../fr/xephi/authme/datasource/MySQL.java | 11 +++++++ .../fr/xephi/authme/datasource/SQLite.java | 1 + .../listener/PlayerListener19Spigot.java | 3 +- .../permission/handlers/LuckPermsHandler.java | 3 +- .../register/ProcessSyncEmailRegister.java | 9 +++++- .../register/ProcessSyncPasswordRegister.java | 6 ++++ .../xephi/authme/settings/SettingsWarner.java | 6 ++-- .../authme/task/purge/PurgeExecutor.java | 15 +++++++++ .../java/fr/xephi/authme/AuthMeMatchers.java | 1 + .../java/fr/xephi/authme/ClassCollector.java | 6 ++-- .../fr/xephi/authme/ReflectionTestUtils.java | 10 +++++- .../data/limbo/LimboPlayerMatchers.java | 4 ++- .../datasource/SqlDataSourceTestUtil.java | 9 ++++++ .../tools/dependencygraph/DrawDependency.java | 3 +- .../EncryptionMethodInfoGatherer.java | 6 ++++ .../permissions/PermissionNodesGatherer.java | 1 + .../permissions/PermissionsListWriter.java | 8 +++-- .../TranslationPageGenerator.java | 6 ++-- .../translations/TranslationsGatherer.java | 32 ++++++++++++------- .../filegeneration/GeneratePluginYml.java | 2 +- .../tools/messages/CheckMessageKeyUsages.java | 19 ++--------- src/test/java/tools/utils/FileIoUtils.java | 24 ++++++++++++++ 23 files changed, 142 insertions(+), 48 deletions(-) diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java index 5fbd6993f..4f201fa5c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java @@ -172,6 +172,11 @@ class MySqlDefaultChanger implements DebugSection { + sender.getName() + "'"); } + /** + * Outputs the current definitions of all {@link Columns} which can be migrated. + * + * @param sender command sender to output the data to + */ private void showColumnDetails(CommandSender sender) { sender.sendMessage(ChatColor.BLUE + "MySQL column details"); final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 01a240fe3..ce46baadd 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -28,6 +28,10 @@ import java.util.Set; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; +/** + * MySQL data source. + */ +@SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore public class MySQL implements DataSource { private boolean useSsl; @@ -728,6 +732,13 @@ public class MySQL implements DataSource { return players; } + /** + * Creates a {@link PlayerAuth} object with the data from the provided result set. + * + * @param row the result set to read from + * @return generated player auth object with the data from the result set + * @throws SQLException . + */ private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT); int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP); diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 422d57b17..ada94afbc 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -28,6 +28,7 @@ import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; /** * SQLite data source. */ +@SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore public class SQLite implements DataSource { private final Settings settings; diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener19Spigot.java b/src/main/java/fr/xephi/authme/listener/PlayerListener19Spigot.java index d5f46b305..1684887e6 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener19Spigot.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener19Spigot.java @@ -12,10 +12,11 @@ import javax.inject.Inject; public class PlayerListener19Spigot implements Listener { + private static boolean isPlayerSpawnLocationEventCalled = false; + @Inject private TeleportationService teleportationService; - private static boolean isPlayerSpawnLocationEventCalled = false; public static boolean isPlayerSpawnLocationEventCalled() { return isPlayerSpawnLocationEventCalled; diff --git a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java index ad46a35ca..9e52746fc 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java @@ -62,7 +62,8 @@ public class LuckPermsHandler implements PermissionHandler { return false; } - DataMutateResult result = user.setPermissionUnchecked(luckPermsApi.getNodeFactory().makeGroupNode(newGroup).build()); + DataMutateResult result = user.setPermissionUnchecked( + luckPermsApi.getNodeFactory().makeGroupNode(newGroup).build()); if (result == DataMutateResult.FAIL) { return false; } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java index b43a85639..6ab4a874a 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java @@ -10,7 +10,9 @@ import org.bukkit.entity.Player; import javax.inject.Inject; - +/** + * Performs synchronous tasks after a successful {@link RegistrationType#EMAIL email registration}. + */ public class ProcessSyncEmailRegister implements SynchronousProcess { @Inject @@ -22,6 +24,11 @@ public class ProcessSyncEmailRegister implements SynchronousProcess { ProcessSyncEmailRegister() { } + /** + * Performs sync tasks for a player which has just registered by email. + * + * @param player the recently registered player + */ public void processEmailRegister(Player player) { service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); limboService.replaceTasksAfterRegistration(player); diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index ea5c028ef..30e7f59ae 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -15,6 +15,7 @@ import org.bukkit.entity.Player; import javax.inject.Inject; /** + * Performs synchronous tasks after a successful {@link RegistrationType#PASSWORD password registration}. */ public class ProcessSyncPasswordRegister implements SynchronousProcess { @@ -46,6 +47,11 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { } } + /** + * Processes a player having registered with a password. + * + * @param player the newly registered player + */ public void processPasswordRegister(Player player) { service.send(player, MessageKey.REGISTER_SUCCESS); diff --git a/src/main/java/fr/xephi/authme/settings/SettingsWarner.java b/src/main/java/fr/xephi/authme/settings/SettingsWarner.java index ca049d56b..c94b0455c 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsWarner.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsWarner.java @@ -56,9 +56,9 @@ public class SettingsWarner { // Warn if spigot.yml has settings.bungeecord set to true but config.yml has Hooks.bungeecord set to false if (Utils.isSpigot() && Bukkit.spigot().getConfig().getBoolean("settings.bungeecord") && !settings.getProperty(HooksSettings.BUNGEECORD)) { - ConsoleLogger.warning("Note: Hooks.bungeecord is set to false but your server appears to be running in" + - " bungeecord mode (see your spigot.yml). In order to allow the datasource caching and the AuthMeBungee" + - " add-on to work properly you have to enable this option!"); + ConsoleLogger.warning("Note: Hooks.bungeecord is set to false but your server appears to be running in" + + " bungeecord mode (see your spigot.yml). In order to allow the datasource caching and the" + + " AuthMeBungee add-on to work properly you have to enable this option!"); } // Check if argon2 library is present and can be loaded diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java index 04b7f5cc6..a3e42f756 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java @@ -61,6 +61,11 @@ public class PurgeExecutor { purgePermissions(players); } + /** + * Purges data from the AntiXray plugin. + * + * @param cleared the players whose data should be cleared + */ synchronized void purgeAntiXray(Collection cleared) { if (!settings.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) { return; @@ -95,6 +100,11 @@ public class PurgeExecutor { ConsoleLogger.info(ChatColor.GOLD + "Deleted " + names.size() + " user accounts"); } + /** + * Purges data from the LimitedCreative plugin. + * + * @param cleared the players whose data should be cleared + */ synchronized void purgeLimitedCreative(Collection cleared) { if (!settings.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) { return; @@ -191,6 +201,11 @@ public class PurgeExecutor { ConsoleLogger.info("AutoPurge: Removed " + deletedFiles + " EssentialsFiles"); } + /** + * Removes permission data (groups a user belongs to) for the given players. + * + * @param cleared the players to remove data for + */ synchronized void purgePermissions(Collection cleared) { if (!settings.getProperty(PurgeSettings.REMOVE_PERMISSIONS)) { return; diff --git a/src/test/java/fr/xephi/authme/AuthMeMatchers.java b/src/test/java/fr/xephi/authme/AuthMeMatchers.java index 69f2ac27e..33768883d 100644 --- a/src/test/java/fr/xephi/authme/AuthMeMatchers.java +++ b/src/test/java/fr/xephi/authme/AuthMeMatchers.java @@ -11,6 +11,7 @@ import java.util.Objects; /** * Custom matchers for AuthMe entities. */ +@SuppressWarnings("checkstyle:JavadocMethod") // Justification: Javadoc would be huge because of the many parameters public final class AuthMeMatchers { private AuthMeMatchers() { diff --git a/src/test/java/fr/xephi/authme/ClassCollector.java b/src/test/java/fr/xephi/authme/ClassCollector.java index 886201b02..23c065e82 100644 --- a/src/test/java/fr/xephi/authme/ClassCollector.java +++ b/src/test/java/fr/xephi/authme/ClassCollector.java @@ -67,7 +67,7 @@ public class ClassCollector { public List> collectClasses(Predicate> filter) { File rootFolder = new File(root); List> collection = new ArrayList<>(); - collectClasses(rootFolder, filter, collection); + gatherClassesFromFile(rootFolder, filter, collection); return collection; } @@ -124,14 +124,14 @@ public class ClassCollector { * @param filter the class predicate * @param collection collection to add classes to */ - private void collectClasses(File folder, Predicate> filter, List> collection) { + private void gatherClassesFromFile(File folder, Predicate> filter, List> collection) { File[] files = folder.listFiles(); if (files == null) { throw new IllegalStateException("Could not read files from '" + folder + "'"); } for (File file : files) { if (file.isDirectory()) { - collectClasses(file, filter, collection); + gatherClassesFromFile(file, filter, collection); } else if (file.isFile()) { Class clazz = loadTaskClassFromFile(file); if (clazz != null && filter.test(clazz)) { diff --git a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java index 033bed276..963fbf71d 100644 --- a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java +++ b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java @@ -82,7 +82,6 @@ public final class ReflectionTestUtils { * @param clazz the class to retrieve a method from * @param methodName the name of the method * @param parameterTypes the parameter types the method to retrieve has - * * @return the method of the class, set to be accessible */ public static Method getMethod(Class clazz, String methodName, Class... parameterTypes) { @@ -96,6 +95,15 @@ public final class ReflectionTestUtils { } } + /** + * Invokes the given method on the provided instance with the given parameters. + * + * @param method the method to invoke + * @param instance the instance to invoke the method on (null for static methods) + * @param parameters the parameters to pass to the method + * @param return value of the method + * @return method return value + */ @SuppressWarnings("unchecked") public static V invokeMethod(Method method, Object instance, Object... parameters) { method.setAccessible(true); diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java index bb5a452e6..78386e2d3 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java +++ b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java @@ -14,6 +14,7 @@ import static org.hamcrest.collection.IsIterableContainingInOrder.contains; /** * Contains matchers for LimboPlayer. */ +@SuppressWarnings("checkstyle:JavadocMethod") // Justification: Javadoc would be huge because of the many parameters public final class LimboPlayerMatchers { private LimboPlayerMatchers() { @@ -45,7 +46,8 @@ public final class LimboPlayerMatchers { @Override public void describeMismatchSafely(LimboPlayer item, Description description) { description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s, walkSpeed=%f, flySpeed=%f", - item.isOperator(), String.join(" ,", item.getGroups()), item.isCanFly(), item.getWalkSpeed(), item.getFlySpeed())); + item.isOperator(), String.join(" ,", item.getGroups()), item.isCanFly(), + item.getWalkSpeed(), item.getFlySpeed())); } }; } diff --git a/src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java b/src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java index 79a938f6f..dd54e39a4 100644 --- a/src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java +++ b/src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java @@ -27,6 +27,15 @@ public final class SqlDataSourceTestUtil { return new MySQL(settings, hikariDataSource, extensionsFactory); } + /** + * Creates a SQLite implementation for testing purposes. Methods are overridden so the + * provided connection is never overridden. + * + * @param settings settings instance + * @param dataFolder data folder + * @param connection connection to use + * @return the created SQLite instance + */ public static SQLite createSqlite(Settings settings, File dataFolder, Connection connection) { return new SQLite(settings, dataFolder, connection) { // Override reload() so it doesn't run SQLite#connect, since we're given a specific Connection to use diff --git a/src/test/java/tools/dependencygraph/DrawDependency.java b/src/test/java/tools/dependencygraph/DrawDependency.java index 3f34197c4..40a3b6b23 100644 --- a/src/test/java/tools/dependencygraph/DrawDependency.java +++ b/src/test/java/tools/dependencygraph/DrawDependency.java @@ -131,7 +131,8 @@ public class DrawDependency implements ToolTask { private Class unwrapGenericClass(Type genericType) { if (genericType == Factory.class || genericType == SingletonStore.class) { Class parameterType = ReflectionUtils.getGenericType(genericType); - Objects.requireNonNull(parameterType, "Parameter type for '" + genericType + "' should be a concrete class"); + Objects.requireNonNull(parameterType, + "Parameter type for '" + genericType + "' should be a concrete class"); return parameterType; } return InjectorUtils.convertToClass(genericType); diff --git a/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java b/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java index 86f808386..0362e26ca 100644 --- a/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java +++ b/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java @@ -56,6 +56,12 @@ public class EncryptionMethodInfoGatherer { } } + /** + * Creates a description of the given hash algorithm based on its annotations. + * + * @param algorithm the algorithm to describe + * @return description of the hash algorithm + */ private static MethodDescription createDescription(HashAlgorithm algorithm) { Class clazz = algorithm.getClazz(); EncryptionMethod method = createEncryptionMethod(clazz); diff --git a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java index f177cbc67..0b17ebf61 100644 --- a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java +++ b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java @@ -37,6 +37,7 @@ public class PermissionNodesGatherer { /** * Return a sorted collection of all permission nodes, including its JavaDoc description. * + * @param permission node enum type * @return Ordered map whose keys are the permission nodes and the values the associated JavaDoc */ @SuppressWarnings("unchecked") diff --git a/src/test/java/tools/docs/permissions/PermissionsListWriter.java b/src/test/java/tools/docs/permissions/PermissionsListWriter.java index 797e26e79..1e0e8abd6 100644 --- a/src/test/java/tools/docs/permissions/PermissionsListWriter.java +++ b/src/test/java/tools/docs/permissions/PermissionsListWriter.java @@ -4,18 +4,20 @@ import tools.utils.AutoToolTask; import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; -import tools.utils.ToolsConstants; import java.util.Map; +import static tools.utils.ToolsConstants.DOCS_FOLDER; +import static tools.utils.ToolsConstants.TOOLS_SOURCE_ROOT; + /** * Task responsible for formatting a permissions node list and * for writing it to a file if desired. */ public class PermissionsListWriter implements AutoToolTask { - private static final String TEMPLATE_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/permissions/permission_nodes.tpl.md"; - private static final String PERMISSIONS_OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "permission_nodes.md"; + private static final String TEMPLATE_FILE = TOOLS_SOURCE_ROOT + "docs/permissions/permission_nodes.tpl.md"; + private static final String PERMISSIONS_OUTPUT_FILE = DOCS_FOLDER + "permission_nodes.md"; @Override public String getTaskName() { diff --git a/src/test/java/tools/docs/translations/TranslationPageGenerator.java b/src/test/java/tools/docs/translations/TranslationPageGenerator.java index dc555292c..dd87973d3 100644 --- a/src/test/java/tools/docs/translations/TranslationPageGenerator.java +++ b/src/test/java/tools/docs/translations/TranslationPageGenerator.java @@ -45,10 +45,10 @@ public class TranslationPageGenerator implements AutoToolTask { NestedTagValue translationValuesHolder = new NestedTagValue(); for (TranslationInfo translation : gatherer.getTranslationInfo()) { - int percentage = (int) Math.round(translation.percentTranslated * 100); - String name = firstNonNull(LANGUAGE_NAMES.get(translation.code), "?"); + int percentage = (int) Math.round(translation.getPercentTranslated() * 100); + String name = firstNonNull(LANGUAGE_NAMES.get(translation.getCode()), "?"); TagValueHolder valueHolder = TagValueHolder.create() - .put("code", translation.code) + .put("code", translation.getCode()) .put("name", name) .put("percentage", Integer.toString(percentage)) .put("color", computeColor(percentage)); diff --git a/src/test/java/tools/docs/translations/TranslationsGatherer.java b/src/test/java/tools/docs/translations/TranslationsGatherer.java index f54843a9c..e42b266d1 100644 --- a/src/test/java/tools/docs/translations/TranslationsGatherer.java +++ b/src/test/java/tools/docs/translations/TranslationsGatherer.java @@ -25,7 +25,7 @@ public class TranslationsGatherer { public TranslationsGatherer() { gatherTranslations(); - translationInfo.sort((e1, e2) -> getCode(e1).compareTo(getCode(e2))); + translationInfo.sort((e1, e2) -> getSortCode(e1).compareTo(getSortCode(e2))); } public List getTranslationInfo() { @@ -61,16 +61,6 @@ public class TranslationsGatherer { return null; } - public static final class TranslationInfo { - public final String code; - public final double percentTranslated; - - TranslationInfo(String code, double percentTranslated) { - this.code = code; - this.percentTranslated = percentTranslated; - } - } - /** * Returns the language code from the translation info for sorting purposes. * Returns "a" for "en" language code to sort English on top. @@ -78,8 +68,26 @@ public class TranslationsGatherer { * @param info the translation info * @return the language code for sorting */ - private static String getCode(TranslationInfo info) { + private static String getSortCode(TranslationInfo info) { return "en".equals(info.code) ? "a" : info.code; } + public static final class TranslationInfo { + private final String code; + private final double percentTranslated; + + TranslationInfo(String code, double percentTranslated) { + this.code = code; + this.percentTranslated = percentTranslated; + } + + public String getCode() { + return code; + } + + public double getPercentTranslated() { + return percentTranslated; + } + } + } diff --git a/src/test/java/tools/filegeneration/GeneratePluginYml.java b/src/test/java/tools/filegeneration/GeneratePluginYml.java index 04dba91ef..cf833b007 100644 --- a/src/test/java/tools/filegeneration/GeneratePluginYml.java +++ b/src/test/java/tools/filegeneration/GeneratePluginYml.java @@ -67,7 +67,7 @@ public class GeneratePluginYml implements AutoToolTask { List pluginYmlLines = FileIoUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE)); int lineNr = 0; for (String line : pluginYmlLines) { - if (line.equals("commands:")) { + if ("commands:".equals(line)) { break; } ++lineNr; diff --git a/src/test/java/tools/messages/CheckMessageKeyUsages.java b/src/test/java/tools/messages/CheckMessageKeyUsages.java index d43ad5d07..7387f0023 100644 --- a/src/test/java/tools/messages/CheckMessageKeyUsages.java +++ b/src/test/java/tools/messages/CheckMessageKeyUsages.java @@ -33,8 +33,8 @@ public class CheckMessageKeyUsages implements AutoToolTask { if (unusedKeys.isEmpty()) { System.out.println("No unused MessageKey entries found :)"); } else { - System.out.println("Did not find usages for keys:\n- " + - String.join("\n- ", Lists.transform(unusedKeys, MessageKey::name))); + System.out.println("Did not find usages for keys:\n- " + + String.join("\n- ", Lists.transform(unusedKeys, MessageKey::name))); } } @@ -51,21 +51,6 @@ public class CheckMessageKeyUsages implements AutoToolTask { return keys; } - private List findUsagesOfKey(MessageKey key) { - List filesUsingKey = new ArrayList<>(); - File sourceFolder = new File(ToolsConstants.MAIN_SOURCE_ROOT); - - Consumer usagesCollector = file -> { - String source = FileIoUtils.readFromFile(file.toPath()); - if (source.contains(key.name())) { - filesUsingKey.add(file); - } - }; - - walkJavaFileTree(sourceFolder, usagesCollector); - return filesUsingKey; - } - private static void walkJavaFileTree(File folder, Consumer javaFileConsumer) { for (File file : FileIoUtils.listFilesOrThrow(folder)) { if (file.isDirectory()) { diff --git a/src/test/java/tools/utils/FileIoUtils.java b/src/test/java/tools/utils/FileIoUtils.java index eadefd2d5..995c71a01 100644 --- a/src/test/java/tools/utils/FileIoUtils.java +++ b/src/test/java/tools/utils/FileIoUtils.java @@ -27,6 +27,12 @@ public final class FileIoUtils { writeToFile(Paths.get(outputFile), contents); } + /** + * Writes the given contents to the file, overriding any existing content. + * + * @param path the file to write to + * @param contents the contents to write + */ public static void writeToFile(Path path, String contents) { try { Files.write(path, contents.getBytes()); @@ -35,6 +41,12 @@ public final class FileIoUtils { } } + /** + * Adds the given contents to the file while keeping any existing content. + * + * @param outputFile the file to write to + * @param contents the contents to append + */ public static void appendToFile(String outputFile, String contents) { try { Files.write(Paths.get(outputFile), contents.getBytes(), StandardOpenOption.APPEND); @@ -47,6 +59,12 @@ public final class FileIoUtils { return readFromFile(Paths.get(file)); } + /** + * Returns the given file's contents as string. + * + * @param file the file to read + * @return the file's contents + */ public static String readFromFile(Path file) { try { return new String(Files.readAllBytes(file), StandardCharsets.UTF_8); @@ -55,6 +73,12 @@ public final class FileIoUtils { } } + /** + * Returns the lines of the given file. + * + * @param path the path of the file to read + * @return the lines of the file + */ public static List readLinesFromFile(Path path) { try { return Files.readAllLines(path, StandardCharsets.UTF_8); From 7f00e87e85e931a3c8af1b86983f657a5a47cb68 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 13 Mar 2018 11:42:39 +0100 Subject: [PATCH 074/155] [CI-SKIP] Create Jenkinsfile --- Jenkinsfile | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..b3dc23d99 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,125 @@ +pipeline { + agent any + tools { + maven 'Maven 3' + jdk 'OracleJDK 8' + } + stages { + stage ('Clean') { + steps { + echo 'Cleaning the maven workspace...' + sh 'mvn clean' + } + } + stage ('Dependencies') { + steps { + echo 'Downloading dependencies...' + sh 'mvn dependency:go-offline' + } + post { + success { + junit 'target/surefire-reports/**/*.xml' + archiveArtifacts artifacts: 'target/nukkit-*-SNAPSHOT.jar', fingerprint: true + } + } + } + stage ('Validate') { + steps { + echo 'Validating the maven project...' + sh 'mvn -o validate' + } + } + stage ('Compile') { + steps { + echo 'Compiling source classes...' + sh 'mvn -o compile' + } + } + stage ('Compile-Test') { + steps { + echo 'Compiling test classes...' + sh 'mvn -o test-compile' + } + } + stage ('Test') { + steps { + echo 'Performing unit testing...' + sh 'mvn -o test' + } + post { + success { + echo 'Archiving test results...' + junit 'target/surefire-reports/**/*.xml' + } + } + } + stage ('Package') { + steps { + echo 'Preparing the final package...' + sh 'mvn -o package' + } + post { + success { + echo 'Archiving the final package...' + archiveArtifacts artifacts: 'target/*.jar', fingerprint: true + } + } + } + stage ('Sources') { + when { + branch "master" + } + steps { + echo 'Generating sources...' + sh 'mvn -o source:jar' + } + post { + success { + echo 'Archiving sources...' + archiveArtifacts artifacts: 'target/*-souces.jar', fingerprint: true + } + } + } + stage ('Javadoc') { + when { + branch "master" + } + steps { + echo 'Generaing javadocs...' + sh 'mvn -o javadoc:javadoc javadoc:jar' + } + post { + success { + echo 'Archiving javadocs...' + step([ + $class: 'JavadocArchiver', + javadocDir: 'target/site/apidocs', + keepAll: true + ]) + archiveArtifacts artifacts: 'target/*-javadoc.jar', fingerprint: true + } + } + } + stage ('Verify') { + steps { + echo 'Performing integration testing...' + sh 'mvn -o verify' + } + } + stage ('Install') { + steps { + echo 'Installing artifacts to the local repository...' + sh 'mvn -o install' + } + } + stage ('Deploy') { + when { + branch "master" + } + steps { + echo 'Deploying to repository...' + sh 'mvn -o deploy' + } + } + } +} From 8a66a92f812544dd948aa6f4b3f583fd8c6ab1bb Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 13 Mar 2018 11:57:08 +0100 Subject: [PATCH 075/155] [CI-SKIP] Don't generate javadocs and sources by default --- pom.xml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/pom.xml b/pom.xml index 3f0f4610c..baf61d49b 100644 --- a/pom.xml +++ b/pom.xml @@ -187,15 +187,6 @@ org.apache.maven.plugins maven-javadoc-plugin 3.0.0 - - - attach-javadocs - - javadoc - jar - - - ${project.outputName}-${project.version} public @@ -206,14 +197,6 @@ org.apache.maven.plugins maven-source-plugin 3.0.1 - - - attach-sources - - jar - - - ${project.outputName}-${project.version} From d503d2c061080e9d967360050325ec8476a37833 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 13 Mar 2018 12:44:54 +0100 Subject: [PATCH 076/155] [CI-SKIP] implement pipeline ci-skip --- Jenkinsfile | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index b3dc23d99..57ff8cc95 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,43 +5,52 @@ pipeline { jdk 'OracleJDK 8' } stages { - stage ('Clean') { + stage ('prepare') { + steps { + env.CI_SKIP = "false" + result = sh (script: "git log -1 | grep '(?s).[CI[-\s]SKIP].*'", returnStatus: true) + if (result == 0) { + env.CI_SKIP = "true" + error "'[CI-SKIP]' found in git commit message. Aborting." + } + } + } + stage ('clean') { steps { echo 'Cleaning the maven workspace...' sh 'mvn clean' } } - stage ('Dependencies') { + stage ('dependencies') { steps { echo 'Downloading dependencies...' sh 'mvn dependency:go-offline' } post { success { - junit 'target/surefire-reports/**/*.xml' archiveArtifacts artifacts: 'target/nukkit-*-SNAPSHOT.jar', fingerprint: true } } } - stage ('Validate') { + stage ('validate') { steps { echo 'Validating the maven project...' sh 'mvn -o validate' } } - stage ('Compile') { + stage ('compile') { steps { echo 'Compiling source classes...' sh 'mvn -o compile' } } - stage ('Compile-Test') { + stage ('compile-test') { steps { echo 'Compiling test classes...' sh 'mvn -o test-compile' } } - stage ('Test') { + stage ('test') { steps { echo 'Performing unit testing...' sh 'mvn -o test' @@ -53,7 +62,7 @@ pipeline { } } } - stage ('Package') { + stage ('package') { steps { echo 'Preparing the final package...' sh 'mvn -o package' @@ -65,7 +74,7 @@ pipeline { } } } - stage ('Sources') { + stage ('sources') { when { branch "master" } @@ -80,7 +89,7 @@ pipeline { } } } - stage ('Javadoc') { + stage ('javadoc') { when { branch "master" } @@ -100,19 +109,19 @@ pipeline { } } } - stage ('Verify') { + stage ('verify') { steps { echo 'Performing integration testing...' sh 'mvn -o verify' } } - stage ('Install') { + stage ('install') { steps { echo 'Installing artifacts to the local repository...' sh 'mvn -o install' } } - stage ('Deploy') { + stage ('deploy') { when { branch "master" } @@ -121,5 +130,12 @@ pipeline { sh 'mvn -o deploy' } } + post { + always { + if (env.CI_SKIP == "true") { + currentBuild.result = 'NOT_BUILT' + } + } + } } } From 193e93e30ad38fed80b8891cd323375aa85b6c2e Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 13 Mar 2018 12:46:23 +0100 Subject: [PATCH 077/155] Whoops, damn escape character --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 57ff8cc95..f2c117d7e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,7 +8,7 @@ pipeline { stage ('prepare') { steps { env.CI_SKIP = "false" - result = sh (script: "git log -1 | grep '(?s).[CI[-\s]SKIP].*'", returnStatus: true) + result = sh (script: "git log -1 | grep '(?s).[CI[-\\s]SKIP].*'", returnStatus: true) if (result == 0) { env.CI_SKIP = "true" error "'[CI-SKIP]' found in git commit message. Aborting." From 18e4be8d5b6c9896924df9dd0456a026a55e7db6 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 13 Mar 2018 22:14:55 +0100 Subject: [PATCH 078/155] [CI-SKIP] Remove Jenkinsfile from main branch --- Jenkinsfile | 141 ---------------------------------------------------- 1 file changed, 141 deletions(-) delete mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index f2c117d7e..000000000 --- a/Jenkinsfile +++ /dev/null @@ -1,141 +0,0 @@ -pipeline { - agent any - tools { - maven 'Maven 3' - jdk 'OracleJDK 8' - } - stages { - stage ('prepare') { - steps { - env.CI_SKIP = "false" - result = sh (script: "git log -1 | grep '(?s).[CI[-\\s]SKIP].*'", returnStatus: true) - if (result == 0) { - env.CI_SKIP = "true" - error "'[CI-SKIP]' found in git commit message. Aborting." - } - } - } - stage ('clean') { - steps { - echo 'Cleaning the maven workspace...' - sh 'mvn clean' - } - } - stage ('dependencies') { - steps { - echo 'Downloading dependencies...' - sh 'mvn dependency:go-offline' - } - post { - success { - archiveArtifacts artifacts: 'target/nukkit-*-SNAPSHOT.jar', fingerprint: true - } - } - } - stage ('validate') { - steps { - echo 'Validating the maven project...' - sh 'mvn -o validate' - } - } - stage ('compile') { - steps { - echo 'Compiling source classes...' - sh 'mvn -o compile' - } - } - stage ('compile-test') { - steps { - echo 'Compiling test classes...' - sh 'mvn -o test-compile' - } - } - stage ('test') { - steps { - echo 'Performing unit testing...' - sh 'mvn -o test' - } - post { - success { - echo 'Archiving test results...' - junit 'target/surefire-reports/**/*.xml' - } - } - } - stage ('package') { - steps { - echo 'Preparing the final package...' - sh 'mvn -o package' - } - post { - success { - echo 'Archiving the final package...' - archiveArtifacts artifacts: 'target/*.jar', fingerprint: true - } - } - } - stage ('sources') { - when { - branch "master" - } - steps { - echo 'Generating sources...' - sh 'mvn -o source:jar' - } - post { - success { - echo 'Archiving sources...' - archiveArtifacts artifacts: 'target/*-souces.jar', fingerprint: true - } - } - } - stage ('javadoc') { - when { - branch "master" - } - steps { - echo 'Generaing javadocs...' - sh 'mvn -o javadoc:javadoc javadoc:jar' - } - post { - success { - echo 'Archiving javadocs...' - step([ - $class: 'JavadocArchiver', - javadocDir: 'target/site/apidocs', - keepAll: true - ]) - archiveArtifacts artifacts: 'target/*-javadoc.jar', fingerprint: true - } - } - } - stage ('verify') { - steps { - echo 'Performing integration testing...' - sh 'mvn -o verify' - } - } - stage ('install') { - steps { - echo 'Installing artifacts to the local repository...' - sh 'mvn -o install' - } - } - stage ('deploy') { - when { - branch "master" - } - steps { - echo 'Deploying to repository...' - sh 'mvn -o deploy' - } - } - post { - always { - if (env.CI_SKIP == "true") { - currentBuild.result = 'NOT_BUILT' - } - } - } - } -} From fe538a43410cbd1801ed4ae96380e4c3ed7705b1 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 13 Mar 2018 22:23:43 +0100 Subject: [PATCH 079/155] Revert "[CI-SKIP] Don't generate javadocs and sources by default" This reverts commit 8a66a92f812544dd948aa6f4b3f583fd8c6ab1bb. --- pom.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pom.xml b/pom.xml index baf61d49b..3f0f4610c 100644 --- a/pom.xml +++ b/pom.xml @@ -187,6 +187,15 @@ org.apache.maven.plugins maven-javadoc-plugin 3.0.0 + + + attach-javadocs + + javadoc + jar + + + ${project.outputName}-${project.version} public @@ -197,6 +206,14 @@ org.apache.maven.plugins maven-source-plugin 3.0.1 + + + attach-sources + + jar + + + ${project.outputName}-${project.version} From c93bfae90cb36bd58827550ef6a1c7da65f2eeca Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 13 Mar 2018 23:58:24 +0100 Subject: [PATCH 080/155] Try to fix jenkins javadocs archive --- pom.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3f0f4610c..6386fabd3 100644 --- a/pom.xml +++ b/pom.xml @@ -189,12 +189,17 @@ 3.0.0 - attach-javadocs + attach-javadoc - javadoc jar + + aggregate-javadoc + + aggregate + + ${project.outputName}-${project.version} From 914b76ad120ad797b8ec68e67adad2981473a685 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Wed, 14 Mar 2018 00:31:43 +0100 Subject: [PATCH 081/155] Update dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6386fabd3..b518d97af 100644 --- a/pom.xml +++ b/pom.xml @@ -461,7 +461,7 @@ de.mkammerer argon2-jvm-nolibs - 2.3 + 2.4 compile true @@ -544,7 +544,7 @@ me.lucko.luckperms luckperms-api - 4.0 + 4.1 provided From 55b6f675505eb05508e026dff338c1d7439e15a9 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Thu, 15 Mar 2018 20:20:40 +0100 Subject: [PATCH 082/155] JavaDocs --- .../data/QuickCommandsProtectionManager.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java index 692c1dbc5..6e697db16 100644 --- a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java +++ b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java @@ -26,14 +26,28 @@ public class QuickCommandsProtectionManager implements SettingsDependent, HasCle reload(settings); } + /** + * Save the player in the set + * @param name the player's name + */ public void setLogin(String name) { latestLogin.add(name); } + /** + * Returns whether the given player has the permission and should be saved in the set + * @param player the player to check + * @return true if the player has the permission, false otherwise + */ public boolean shouldSaveLogin(Player player) { return permissionsManager.hasPermission(player, PlayerPermission.QUICK_COMMANDS_PROTECTION); } + /** + * Returns whether the given player is able to perform the command + * @param name the name of the player to check + * @return true if the player is not in the set (so it's allowed to perform the command), false otherwise + */ public boolean isAllowed(String name) { return !latestLogin.contains(name); } From 6d60e72296abdb9b1897956d1e09c0ab1a25e436 Mon Sep 17 00:00:00 2001 From: games647 Date: Thu, 15 Mar 2018 20:42:50 +0100 Subject: [PATCH 083/155] Fix no auto login permission (Fixes #118) --- src/main/java/fr/xephi/authme/util/FileUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java index bc1bef41c..bdfd69045 100644 --- a/src/main/java/fr/xephi/authme/util/FileUtils.java +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -1,6 +1,8 @@ package fr.xephi.authme.util; import com.google.common.io.Files; +import com.google.common.io.MoreFiles; + import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; @@ -162,6 +164,8 @@ public final class FileUtils { * @return path to a file suitably named for storing a backup */ public static String createBackupFilePath(File file) { + MoreFiles.getNameWithoutExtension(file.toPath()); + String filename = "backup_" + Files.getNameWithoutExtension(file.getName()) + "_" + createCurrentTimeString() + "." + Files.getFileExtension(file.getName()); From 7ff5801cfe3f3e0e992a1fd36e8422c9b850d140 Mon Sep 17 00:00:00 2001 From: games647 Date: Thu, 15 Mar 2018 20:43:50 +0100 Subject: [PATCH 084/155] Revert "Fix no auto login permission (Fixes #118)" Wrong repo This reverts commit 6d60e72296abdb9b1897956d1e09c0ab1a25e436. --- src/main/java/fr/xephi/authme/util/FileUtils.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java index bdfd69045..bc1bef41c 100644 --- a/src/main/java/fr/xephi/authme/util/FileUtils.java +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -1,8 +1,6 @@ package fr.xephi.authme.util; import com.google.common.io.Files; -import com.google.common.io.MoreFiles; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; @@ -164,8 +162,6 @@ public final class FileUtils { * @return path to a file suitably named for storing a backup */ public static String createBackupFilePath(File file) { - MoreFiles.getNameWithoutExtension(file.toPath()); - String filename = "backup_" + Files.getNameWithoutExtension(file.getName()) + "_" + createCurrentTimeString() + "." + Files.getFileExtension(file.getName()); From 048a47ce6e05b496c298a6a4b704252a3308f186 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Thu, 15 Mar 2018 21:43:36 +0100 Subject: [PATCH 085/155] Fixing PLayerListener kick message/tests --- .../xephi/authme/listener/PlayerListener.java | 2 +- .../authme/listener/PlayerListenerTest.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 4b42628fb..6ceeb835c 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -105,7 +105,7 @@ public class PlayerListener implements Listener { final Player player = event.getPlayer(); if (!quickCommandsProtectionManager.isAllowed(player.getName())) { event.setCancelled(true); - player.kickPlayer(m.retrieveSingle(MessageKey.QUICK_COMMAND_PROTECTION_KICK)); + player.kickPlayer(m.retrieveSingle(player, MessageKey.QUICK_COMMAND_PROTECTION_KICK)); return; } if (listenerService.shouldCancelEvent(player)) { diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index 3d75e2c52..35c33a145 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -1,6 +1,7 @@ package fr.xephi.authme.listener; import fr.xephi.authme.TestHelper; +import fr.xephi.authme.data.QuickCommandsProtectionManager; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; @@ -110,6 +111,8 @@ public class PlayerListenerTest { private ValidationService validationService; @Mock private JoinMessageService joinMessageService; + @Mock + private QuickCommandsProtectionManager quickCommandsProtectionManager; /** * #831: If a player is kicked because of "logged in from another location", the kick @@ -219,6 +222,7 @@ public class PlayerListenerTest { // PlayerCommandPreprocessEvent#getPlayer is final, so create a spy instead of a mock PlayerCommandPreprocessEvent event = spy(new PlayerCommandPreprocessEvent(player, "/hub")); given(listenerService.shouldCancelEvent(player)).willReturn(false); + given(quickCommandsProtectionManager.isAllowed(player.getName())).willReturn(true); // when listener.onPlayerCommandPreprocess(event); @@ -238,6 +242,7 @@ public class PlayerListenerTest { Player player = playerWithMockedServer(); PlayerCommandPreprocessEvent event = spy(new PlayerCommandPreprocessEvent(player, "/hub")); given(listenerService.shouldCancelEvent(player)).willReturn(true); + given(quickCommandsProtectionManager.isAllowed(player.getName())).willReturn(true); // when listener.onPlayerCommandPreprocess(event); @@ -248,6 +253,23 @@ public class PlayerListenerTest { verify(messages).send(player, MessageKey.DENIED_COMMAND); } + @Test + public void shouldCancelCommandFastCommandEvent() { + // given + given(settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)).willReturn(false); + given(settings.getProperty(RestrictionSettings.ALLOW_COMMANDS)).willReturn(Arrays.asList("/spawn", "/help")); + Player player = playerWithMockedServer(); + PlayerCommandPreprocessEvent event = spy(new PlayerCommandPreprocessEvent(player, "/hub")); + given(quickCommandsProtectionManager.isAllowed(player.getName())).willReturn(false); + + // when + listener.onPlayerCommandPreprocess(event); + + // then + verify(event).setCancelled(true); + verify(player).kickPlayer(messages.retrieveSingle(player, MessageKey.QUICK_COMMAND_PROTECTION_KICK)); + } + @Test public void shouldAllowChat() { // given From 900e8f24151eeb987d99250321ddecc96542f7b3 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Thu, 15 Mar 2018 21:44:49 +0100 Subject: [PATCH 086/155] Updated messages --- .../fr/xephi/authme/message/MessageKey.java | 2 +- src/main/resources/messages/messages_bg.yml | 1 + src/main/resources/messages/messages_br.yml | 1 + src/main/resources/messages/messages_cz.yml | 1 + src/main/resources/messages/messages_de.yml | 1 + src/main/resources/messages/messages_en.yml | 1 + src/main/resources/messages/messages_eo.yml | 1 + src/main/resources/messages/messages_es.yml | 1 + src/main/resources/messages/messages_et.yml | 1 + src/main/resources/messages/messages_eu.yml | 1 + src/main/resources/messages/messages_fi.yml | 1 + src/main/resources/messages/messages_fr.yml | 1 + src/main/resources/messages/messages_gl.yml | 1 + src/main/resources/messages/messages_hu.yml | 1 + src/main/resources/messages/messages_id.yml | 1 + src/main/resources/messages/messages_it.yml | 23 +++++++++++-------- src/main/resources/messages/messages_ko.yml | 1 + src/main/resources/messages/messages_lt.yml | 1 + src/main/resources/messages/messages_nl.yml | 1 + src/main/resources/messages/messages_pl.yml | 1 + src/main/resources/messages/messages_pt.yml | 1 + src/main/resources/messages/messages_ro.yml | 1 + src/main/resources/messages/messages_ru.yml | 1 + src/main/resources/messages/messages_sk.yml | 1 + src/main/resources/messages/messages_tr.yml | 1 + src/main/resources/messages/messages_uk.yml | 1 + src/main/resources/messages/messages_vn.yml | 1 + src/main/resources/messages/messages_zhcn.yml | 1 + src/main/resources/messages/messages_zhhk.yml | 1 + src/main/resources/messages/messages_zhmc.yml | 1 + src/main/resources/messages/messages_zhtw.yml | 1 + 31 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index 8c40fcea8..355f14a9a 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -276,7 +276,7 @@ public enum MessageKey { VERIFICATION_CODE_EMAIL_NEEDED("verification.email_needed"), /** You used a command too fast! Please, join the server again and wait more before using any command. */ - QUICK_COMMAND_PROTECTION_KICK("quick_command.kick"), + QUICK_COMMAND_PROTECTION_KICK("on_join_validation.quick_command"), /** second */ SECOND("time.second"), diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index c49e2cb00..8b073bd96 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Твоята държава е забранена в този сървър!' not_owner_error: 'Ти не си собственика на този акаунт. Моля избери друго потребителско име!' invalid_name_case: 'Трябва да влезеш с %valid, а не с %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 3000840bb..69f5e3954 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -82,6 +82,7 @@ on_join_validation: country_banned: '&4O seu país está banido neste servidor!' not_owner_error: 'Você não é o proprietário da conta. Por favor, escolha outro nome!' invalid_name_case: 'Você deve se juntar usando nome de usuário %valid, não %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index 3b97a5aec..5ed2ceb41 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: 'Vaše země je na tomto serveru zakázána' not_owner_error: 'Nejsi majitelem tohoto účtu, prosím zvol si jiné jméno!' invalid_name_case: 'Měl by jsi použít jméno %valid, ne jméno %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 108d7dad8..84679c486 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Dein Land ist gesperrt!' not_owner_error: 'Du bist nicht der Besitzer dieses Accounts. Bitte wähle einen anderen Namen!' invalid_name_case: 'Dein registrierter Benutzername ist &2%valid&f - nicht &4%invalid&f.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index e5e20055a..6d9d28798 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -78,6 +78,7 @@ on_join_validation: same_nick_online: '&4The same username is already playing on the server!' invalid_name_case: 'You should join using username %valid, not %invalid.' same_ip_online: 'A player with the same IP is already in game!' + quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_eo.yml b/src/main/resources/messages/messages_eo.yml index dd1657728..c5783c831 100644 --- a/src/main/resources/messages/messages_eo.yml +++ b/src/main/resources/messages/messages_eo.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Via lando estas malpermesitaj de tiu servilo!' not_owner_error: 'Vi ne estas la posedanto de tiu konto. Bonvolu elekti alian nomon!' invalid_name_case: 'Vi devus aliĝi uzante uzantnomon %valid, ne %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index ce0d8a369..50363ead1 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -80,6 +80,7 @@ on_join_validation: country_banned: '¡Tu país ha sido baneado de este servidor!' not_owner_error: 'No eres el propietario de esta cuenta. ¡Por favor, elije otro nombre!' invalid_name_case: 'Solo puedes unirte mediante el nombre de usuario %valid, no %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_et.yml b/src/main/resources/messages/messages_et.yml index df6bd053c..68ce520f5 100644 --- a/src/main/resources/messages/messages_et.yml +++ b/src/main/resources/messages/messages_et.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Su riik on siin serveris keelatud!' not_owner_error: 'Sa ei ole selle konto omanik. Vali teine nimi.' invalid_name_case: 'Logi sisse kasutajanimega %valid, mitte kasutajanimega %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index 4eec94c34..0507d1042 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '[AuthMe] Zure herrialdea blokeatuta dago zerbitzari honetan' # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index 9a6578bdd..b0b4f3032 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -79,6 +79,7 @@ on_join_validation: # TODO country_banned: '&4Your country is banned from this server!' # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index 93358b2e8..52fd4c882 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -82,6 +82,7 @@ on_join_validation: country_banned: 'Votre pays est banni de ce serveur.' not_owner_error: 'Vous n''êtes pas le propriétaire de ce compte. Veuillez utiliser un autre pseudo !' invalid_name_case: 'Veuillez vous connecter avec "%valid" et non pas avec "%invalid".' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index 9c47a222d..d22caa7cb 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: 'O teu país está bloqueado neste servidor' # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index 609545976..1d420b746 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Az országod a tiltólistán van ezen a szerveren!' not_owner_error: 'Ez nem a te felhasználód. Kérlek, válassz másik nevet!' invalid_name_case: '%valid a felhasználó neved nem? Akkor ne %invalid névvel próbálj feljönni.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 9fda2668d..65e84cd9c 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -79,6 +79,7 @@ on_join_validation: # TODO country_banned: '&4Your country is banned from this server!' # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index 40b0059ec..22354cda8 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -5,6 +5,8 @@ # %displayname% - Sostituisce il nickname (e i colori) dell'utente che riceve il messaggio. # Registrazione + +# Registration registration: disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.' name_taken: '&cHai già eseguito la registrazione, non puoi eseguirla nuovamente.' @@ -14,7 +16,7 @@ registration: success: '&2Registrato correttamente!' kicked_admin_registered: 'Un amministratore ti ha appena registrato, per favore rientra nel server' -# Errori della password durante la registrazione +# Password errors on registration password: match_error: '&cLe password non corrispondono!' name_in_password: '&cNon puoi usare il tuo nome utente come password, per favore scegline un''altra...' @@ -22,7 +24,7 @@ password: forbidden_characters: '&4La tua password contiene caratteri non consentiti. I caratteri consentiti sono: %valid_chars' wrong_length: '&cLa password che hai inserito è troppo corta o troppo lunga, per favore scegline un''altra...' -# Autenticazione +# Login login: command_usage: '&cUtilizzo: /login ' wrong_password: '&cPassword non corretta!' @@ -30,7 +32,7 @@ login: login_request: '&cPer favore, esegui l''autenticazione con il comando: /login ' timeout_error: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!' -# Errori +# Errors error: denied_command: '&cPer poter usare questo comando devi essere autenticato!' denied_chat: '&cPer poter scrivere messaggi in chat devi essere autenticato!' @@ -49,12 +51,12 @@ antibot: auto_enabled: '&4Il servizio di AntiBot è stato automaticamente abilitato a seguito delle numerose connessioni!' auto_disabled: '&2Il servizio di AntiBot è stato automaticamente disabilitato dopo %m minuti!' -# Rimozione dal Database +# Unregister unregister: success: '&2Sei stato correttamente rimosso dal database!' command_usage: '&cUtilizzo: /unregister ' -# Altri messaggi +# Other messages misc: account_not_activated: '&cIl tuo account non è stato ancora verificato, controlla fra le tue email per scoprire come attivarlo!' password_changed: '&2Password cambiata correttamente!' @@ -65,12 +67,12 @@ misc: accounts_owned_self: 'Possiedi %count account:' accounts_owned_other: 'Il giocatore %name possiede %count account:' -# Messaggi della sessione +# Session messages session: valid_session: '&2Autenticato automaticamente attraverso la precedente sessione!' invalid_session: '&cIl tuo indirizzo IP è cambiato e la tua sessione è stata terminata!' -# Messaggi di errore durante l'accesso +# Error messages when joining on_join_validation: same_ip_online: 'Un giocatore con il tuo stesso IP è già connesso sul server!' same_nick_online: '&4Un giocatore con il tuo stesso nome utente è già connesso sul server!' @@ -80,6 +82,7 @@ on_join_validation: country_banned: '&4Il tuo paese è bandito da questo server!' not_owner_error: 'Non sei il proprietario di questo account. Per favore scegli un altro nome!' invalid_name_case: 'Dovresti entrare con questo nome utente "%valid", al posto di "%invalid".' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: @@ -100,7 +103,7 @@ email: change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' -# Recupero password via Email +# Password recovery by email recovery: forgot_password_hint: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery ' command_usage: '&cUtilizzo: /email recovery ' @@ -120,7 +123,7 @@ captcha: captcha_for_registration: 'Per poterti registrare devi prima risolvere un captcha, per favore scrivi: /captcha %captcha_code' register_captcha_valid: '&2Il captcha inserito è valido! Ora puoi eseguire la registrazione con: /register ' -# Codice di verifica +# Verification code verification: code_required: '&3Questo comando va a modificare dati sensibili e richiede una verifica tramite email! Controlla la tua posta in arrivo e segui le istruzioni nell''email.' command_usage: '&cUtilizzo: /verification ' @@ -130,7 +133,7 @@ verification: code_expired: '&3Il tuo codice è scaduto! Esegui nuovamente un comando che modifica dati sensibili per ricevere uno nuovo codice!' email_needed: '&3Per verificare la tua identità devi collegare un indirizzo email al tuo account!' -# Unità di tempo +# Time units time: second: 'secondo' seconds: 'secondi' diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index f934f4ccb..41049b884 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -81,6 +81,7 @@ on_join_validation: country_banned: '&4당신의 국가에서는 이 서버를 이용하실 수 없습니다!' not_owner_error: '이 계정의 소유자가 아닙니다. 다른 닉네임을 선택하세요!' invalid_name_case: '%invalid가 아닌, %valid 사용하여 접속해야 합니다.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index 2952d4497..15eea3048 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -79,6 +79,7 @@ on_join_validation: # TODO country_banned: '&4Your country is banned from this server!' # TODO not_owner_error: 'You are not the owner of this account. Please choose another name!' # TODO invalid_name_case: 'You should join using username %valid, not %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index 00b290de9..b8fc40950 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Jouw land is gebanned op deze server!' not_owner_error: 'Jij bent niet de eigenaar van dit account. Kies alsjeblieft een andere naam!' invalid_name_case: 'Verbind je alsjeblieft als %valid, niet als %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 427ad302b..4d9061dfd 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Ten kraj jest zbanowany na tym serwerze' not_owner_error: '&cNie jesteś właścicielem tego konta, wybierz inny nick!' invalid_name_case: '&cPowinieneś dołączyć do serwera z nicku %valid, a nie %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index fe41dd553..2fb0ad2c4 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: 'O seu país está banido deste servidor' not_owner_error: 'Não é o proprietário da conta. Por favor, escolha outro nome!' invalid_name_case: 'Deve se juntar usando nome de usuário %valid, não %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml index 16b144497..6fcacb052 100644 --- a/src/main/resources/messages/messages_ro.yml +++ b/src/main/resources/messages/messages_ro.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Tara ta este interzisa pe acest server!' not_owner_error: 'Tu nu esti detinatorul acestui cont. Te rugam alege alt nume!' invalid_name_case: 'Ar trebui sa intri cu numele %valid, nu %invalid' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 82d9d48e0..25a8ca032 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Вход с IP-адресов вашей страны запрещён на этом сервере.' not_owner_error: 'Вы не являетесь владельцем данной уч. записи. Выберите себе другое имя!' invalid_name_case: 'Неверное имя! Зайдите под именем %valid, а не %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index 030e721d7..5af6e5030 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -85,6 +85,7 @@ on_join_validation: country_banned: '&4Tvoja krajina je zabanovaná na tomto serveri!' not_owner_error: 'Niesi majiteľ tohto účtu. Prosím zvoľ si iné meno!' invalid_name_case: 'Mal by si sa pripojiť s nickom %valid, nie %invalid -pozor na veľké a malé písmená.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index 119f037e3..b9aff0ae9 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Senin bolgen sunucudan yasaklandi!' not_owner_error: 'Bu hesabin sahibi degilsin. Lutfen farkli bir isim sec!' invalid_name_case: 'Oyuna %valid isminde katilmalisin. %invalid ismini kullanarak katilamazsin.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index a1579c303..3ecf52905 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Your country is banned from this server!' not_owner_error: 'Цей акаунт вам не належить! Будь ласка, оберіть інакший нікнейм!' invalid_name_case: 'Регістр у вашому нікнеймі відрізняється від регістру при реєстрації.%nl%Поточний регістр: &c%invalid&f. Валідний регістр: &a%valid&f.%nl%Будь ласка, перезайдіть з валідним регістром!' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index 61bbda945..5e138f4f6 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4Quốc gia của bạn bị cấm tham gia máy chủ này!' not_owner_error: 'Bạn không phải là chủ sở hữu tài khoản này, hãy chọn tên khác!' invalid_name_case: 'Bạn nên vào máy chủ với tên đăng nhập là %valid, không phải là %invalid.' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index d53a44486..78c3c89b6 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '这个服务器禁止该国家登陆' not_owner_error: '&8[&6玩家系统&8] &4警告! &c你并不是此帐户持有人,请立即登出。 ' invalid_name_case: '&8[&6玩家系统&8] &c你应该使用「%valid」而并非「%invalid」登入游戏。 ' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index ae29c65b1..9d080748b 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -82,6 +82,7 @@ on_join_validation: country_banned: '&8[&6用戶系統&8] &4本伺服器已停止對你的國家提供遊戲服務。' not_owner_error: '&8[&6用戶系統&8] &4警告!&c你並不是此帳戶持有人,請立即登出。' invalid_name_case: '&8[&6用戶系統&8] &4警告!&c你應該使用「%valid」而並非「%invalid」登入遊戲。' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_zhmc.yml b/src/main/resources/messages/messages_zhmc.yml index 76fa6ccc4..a151a8cf8 100644 --- a/src/main/resources/messages/messages_zhmc.yml +++ b/src/main/resources/messages/messages_zhmc.yml @@ -79,6 +79,7 @@ on_join_validation: country_banned: '&4您的國家/地區已被禁止使用此伺服器!' not_owner_error: '您不是此帳戶的所有者。 請選擇其他名稱!' invalid_name_case: '您應該使用用戶名 %valid 而不是 %invalid 來加入。' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index 91696b5d8..43a7b5338 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -81,6 +81,7 @@ on_join_validation: country_banned: '&b【AuthMe】&6您所在的地區無法進入此伺服器' not_owner_error: '&b【AuthMe】&4警告!&c您並不是此帳戶持有人,請立即登出。' invalid_name_case: '&b【AuthMe】&4警告!&c您應該使用「%valid」而並非「%invalid」登入遊戲。' + # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' # Email email: From 66d1ee92c3273dacf2dcc15a2c8c462ef92949e2 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Thu, 15 Mar 2018 21:45:11 +0100 Subject: [PATCH 087/155] QuickCommandsProtectionManager Test class --- .../QuickCommandsProtectionManagerTest.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java diff --git a/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java b/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java new file mode 100644 index 000000000..10ef88eb8 --- /dev/null +++ b/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java @@ -0,0 +1,65 @@ +package fr.xephi.authme.data; + +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.ProtectionSettings; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; + +/** + * Test for {@link QuickCommandsProtectionManager}. + */ +@RunWith(MockitoJUnitRunner.class) +public class QuickCommandsProtectionManagerTest { + + @Mock + private Settings settings; + + @Mock + private PermissionsManager permissionsManager; + + @Test + public void shouldAllowCommand() { + // given + String name1 = "TestName1"; + String name2 = "TestName2"; + given(settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS)).willReturn(0); + + QuickCommandsProtectionManager qcpm = createQuickCommandsProtectioneManager(); + qcpm.setLogin(name2); + + // when + boolean test1 = qcpm.isAllowed(name1); + boolean test2 = qcpm.isAllowed(name2); + + // then + assertThat(test1, equalTo(true)); + assertThat(test2, equalTo(true)); + } + + @Test + public void shouldDenyCommand() { + // given + String name = "TestName1"; + given(settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS)).willReturn(5000); + + QuickCommandsProtectionManager qcpm = createQuickCommandsProtectioneManager(); + qcpm.setLogin(name); + + // when + boolean test = qcpm.isAllowed(name); + + // then + assertThat(test, equalTo(false)); + } + + private QuickCommandsProtectionManager createQuickCommandsProtectioneManager() { + return new QuickCommandsProtectionManager(settings, permissionsManager); + } +} From f5efd4bf23543093937bb42042a96404a4d24d42 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Thu, 15 Mar 2018 21:45:23 +0100 Subject: [PATCH 088/155] Updating plugin.yml --- src/main/resources/plugin.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2bae573d7..7ddb4b7e0 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -230,6 +230,7 @@ permissions: authme.player.email.see: true authme.player.login: true authme.player.logout: true + authme.player.protection.quickcommandsprotection: true authme.player.register: true authme.player.security.verificationcode: true authme.player.seeownaccounts: true @@ -268,6 +269,9 @@ permissions: authme.player.logout: description: Command permission to logout. default: true + authme.player.protection.quickcommandsprotection: + description: Permission that enables on join quick commands checks for the player. + default: true authme.player.register: description: Command permission to register. default: true From 7790fa5796e812dd80217e22ab97b8b665e7706a Mon Sep 17 00:00:00 2001 From: HexelDev Date: Thu, 15 Mar 2018 21:53:27 +0100 Subject: [PATCH 089/155] typo --- src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index 35c33a145..42d14e463 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -254,7 +254,7 @@ public class PlayerListenerTest { } @Test - public void shouldCancelCommandFastCommandEvent() { + public void shouldCancelFastCommandEvent() { // given given(settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)).willReturn(false); given(settings.getProperty(RestrictionSettings.ALLOW_COMMANDS)).willReturn(Arrays.asList("/spawn", "/help")); From f33446ee253f3da42c1ba1f9ea4ae0228e5cc110 Mon Sep 17 00:00:00 2001 From: games647 Date: Sat, 17 Mar 2018 03:00:24 +0100 Subject: [PATCH 090/155] Migrate to GEO IP 2 (Related #1471) (#1529) * Migrate to GEO IP 2, because support will drop in April * Change all links of maxmind to https * Update maxmind database dependency and add javatar to extract the database from the tar archive (now only a small difference in jar file size -> ~80KB smaller) * Verify downloaded archive using MD5 (There are no other checksums available) * Migrate to Java NIO instead of old java file I/O (Feedback?) * Internal Optional usage for nullable values (Feedback?) Minor: * Schedule a Bukkit async task instead of creating a thread manually * Validate ip input string * Extract validation into single method * Close all resources safely using try-resources * More https links * Add documentation * Set the same last modification as in the tar archive * Fix tests * Comment how the legacy API responded to unknown entries * Document missing function param * Document our maxmind dependency modifications * Include time unit into constant * More logging for downloading the database * Add missing return if the database cannot be found * Delete temporarily file after working with it --- README.md | 4 +- pom.xml | 28 +- src/main/java/fr/xephi/authme/AuthMe.java | 7 +- .../fr/xephi/authme/service/GeoIpService.java | 267 +++++++++++++----- .../properties/ProtectionSettings.java | 2 +- .../authme/service/GeoIpServiceTest.java | 61 ++-- 6 files changed, 262 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 255f36cf9..24514408c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ You can also create your own translation file and, if you want, you can share it
  • E-Mail Recovery System !!!
  • Username spoofing protection.
  • -
  • Countries Whitelist/Blacklist! (country codes)
  • +
  • Countries Whitelist/Blacklist! (country codes)
  • Built-in AntiBot System!
  • ForceLogin Feature: Admins can login with all account via console command!
  • Avoid the "Logged in from another location" message!
  • @@ -126,4 +126,4 @@ Credits for the old version of the plugin: d4rkwarriors, fabe1337, Whoami2 and p Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex ##### GeoIP License: -This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com +This product uses data from the GeoLite API created by MaxMind, available at https://www.maxmind.com diff --git a/pom.xml b/pom.xml index b518d97af..6f5b90609 100644 --- a/pom.xml +++ b/pom.xml @@ -267,8 +267,12 @@ fr.xephi.authme.libs.slf4j.slf4j - com.maxmind.geoip - fr.xephi.authme.libs.maxmind.geoip + com.maxmind.db + fr.xephi.authme.libs.maxmind + + + com.ice.tar + fr.xephi.authme.libs.tar net.ricecode.similarity @@ -399,13 +403,19 @@ true - + + - com.maxmind.geoip - geoip-api - 1.3.1 - compile - true + com.maxmind.db + maxmind-db-gson + 2.0.2-SNAPSHOT + + + + + javatar + javatar + 2.5 @@ -526,7 +536,7 @@ com.comphenix.protocol ProtocolLib-API - 4.4.0-SNAPSHOT + 4.3.0 provided diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index d547f3ae0..305aadc38 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -2,7 +2,9 @@ package fr.xephi.authme; import ch.jalu.injector.Injector; import ch.jalu.injector.InjectorBuilder; + import com.google.common.annotations.VisibleForTesting; + import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.datasource.DataSource; @@ -33,6 +35,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.CleanupTask; import fr.xephi.authme.task.purge.PurgeService; import fr.xephi.authme.util.ExceptionUtils; + +import java.io.File; + import org.apache.commons.lang.SystemUtils; import org.bukkit.Server; import org.bukkit.command.Command; @@ -43,8 +48,6 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.scheduler.BukkitScheduler; -import java.io.File; - import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; import static fr.xephi.authme.util.Utils.isClassLoaded; diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index 9c43ee177..b973b6337 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -1,46 +1,81 @@ package fr.xephi.authme.service; import com.google.common.annotations.VisibleForTesting; -import com.maxmind.geoip.LookupService; +import com.google.common.hash.HashCode; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; +import com.google.common.io.Resources; +import com.ice.tar.TarEntry; +import com.ice.tar.TarInputStream; +import com.maxmind.db.GeoIp2Provider; +import com.maxmind.db.Reader; +import com.maxmind.db.Reader.FileMode; +import com.maxmind.db.cache.CHMCache; +import com.maxmind.db.model.Country; +import com.maxmind.db.model.CountryResponse; + import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.InternetProtocolUtils; -import javax.inject.Inject; +import java.io.BufferedInputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; +import java.net.InetAddress; import java.net.URL; -import java.net.URLConnection; -import java.util.concurrent.TimeUnit; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.FileTime; +import java.time.Duration; +import java.time.Instant; +import java.util.Objects; +import java.util.Optional; import java.util.zip.GZIPInputStream; -import static com.maxmind.geoip.LookupService.GEOIP_MEMORY_CACHE; +import javax.inject.Inject; public class GeoIpService { - private static final String LICENSE = - "[LICENSE] This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com"; - private static final String GEOIP_URL = - "http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz"; - private LookupService lookupService; - private Thread downloadTask; - private final File dataFile; + private static final String LICENSE = + "[LICENSE] This product includes GeoLite2 data created by MaxMind, available at https://www.maxmind.com"; + + private static final String DATABASE_NAME = "GeoLite2-Country"; + private static final String DATABASE_EXT = ".mmdb"; + private static final String DATABASE_FILE = DATABASE_NAME + DATABASE_EXT; + + private static final String ARCHIVE_FILE = DATABASE_NAME + ".tar.gz"; + + private static final String ARCHIVE_URL = "https://geolite.maxmind.com/download/geoip/database/" + ARCHIVE_FILE; + private static final String CHECKSUM_URL = ARCHIVE_URL + ".md5"; + + private static final int UPDATE_INTERVAL_DAYS = 30; + + private final Path dataFile; + private final BukkitService bukkitService; + + private GeoIp2Provider databaseReader; + private volatile boolean downloading; @Inject - GeoIpService(@DataFolder File dataFolder) { - this.dataFile = new File(dataFolder, "GeoIP.dat"); + GeoIpService(@DataFolder File dataFolder, BukkitService bukkitService) { + this.bukkitService = bukkitService; + this.dataFile = dataFolder.toPath().resolve(DATABASE_FILE); + // Fires download of recent data or the initialization of the look up service isDataAvailable(); } @VisibleForTesting - GeoIpService(@DataFolder File dataFolder, LookupService lookupService) { - this.dataFile = dataFolder; - this.lookupService = lookupService; + GeoIpService(@DataFolder File dataFolder, BukkitService bukkitService, GeoIp2Provider reader) { + this.bukkitService = bukkitService; + this.dataFile = dataFolder.toPath().resolve(DATABASE_FILE); + + this.databaseReader = reader; } /** @@ -49,94 +84,186 @@ public class GeoIpService { * @return True if the data is available, false otherwise. */ private synchronized boolean isDataAvailable() { - if (downloadTask != null && downloadTask.isAlive()) { + if (downloading) { + // we are currently downloading the database return false; } - if (lookupService != null) { + + if (databaseReader != null) { + // everything is initialized return true; } - if (dataFile.exists()) { - boolean dataIsOld = (System.currentTimeMillis() - dataFile.lastModified()) > TimeUnit.DAYS.toMillis(30); - if (!dataIsOld) { - try { - lookupService = new LookupService(dataFile, GEOIP_MEMORY_CACHE); + if (Files.exists(dataFile)) { + try { + FileTime lastModifiedTime = Files.getLastModifiedTime(dataFile); + if (Duration.between(lastModifiedTime.toInstant(), Instant.now()).toDays() <= UPDATE_INTERVAL_DAYS) { + databaseReader = new Reader(dataFile.toFile(), FileMode.MEMORY, new CHMCache()); ConsoleLogger.info(LICENSE); + + // don't fire the update task - we are up to date return true; - } catch (IOException e) { - ConsoleLogger.logException("Failed to load GeoLiteAPI database", e); - return false; + } else { + ConsoleLogger.debug("GEO Ip database is older than " + UPDATE_INTERVAL_DAYS + " Days"); } - } else { - FileUtils.delete(dataFile); + } catch (IOException ioEx) { + ConsoleLogger.logException("Failed to load GeoLiteAPI database", ioEx); + return false; } } - // Ok, let's try to download the data file! - downloadTask = createDownloadTask(); - downloadTask.start(); + + // File is outdated or doesn't exist - let's try to download the data file! + startDownloadTask(); return false; } /** * Create a thread which will attempt to download new data from the GeoLite website. - * - * @return the generated download thread */ - private Thread createDownloadTask() { - return new Thread(new Runnable() { - @Override - public void run() { - try { - URL downloadUrl = new URL(GEOIP_URL); - URLConnection conn = downloadUrl.openConnection(); - conn.setConnectTimeout(10000); - conn.connect(); - InputStream input = conn.getInputStream(); - if (conn.getURL().toString().endsWith(".gz")) { - input = new GZIPInputStream(input); - } - OutputStream output = new FileOutputStream(dataFile); - byte[] buffer = new byte[2048]; - int length = input.read(buffer); - while (length >= 0) { - output.write(buffer, 0, length); - length = input.read(buffer); - } - output.close(); - input.close(); - } catch (IOException e) { - ConsoleLogger.logException("Could not download GeoLiteAPI database", e); + private void startDownloadTask() { + downloading = true; + + // use bukkit's cached threads + bukkitService.runTaskAsynchronously(() -> { + ConsoleLogger.info("Downloading GEO IP database, because the old database is outdated or doesn't exist"); + + Path tempFile = null; + try { + // download database to temporarily location + tempFile = Files.createTempFile(ARCHIVE_FILE, null); + try (OutputStream out = Files.newOutputStream(tempFile)) { + Resources.copy(new URL(ARCHIVE_URL), out); + } + + // MD5 checksum verification + String targetChecksum = Resources.toString(new URL(CHECKSUM_URL), StandardCharsets.UTF_8); + if (!verifyChecksum(Hashing.md5(), tempFile, targetChecksum)) { + return; + } + + // tar extract database and copy to target destination + if (!extractDatabase(tempFile, dataFile)) { + ConsoleLogger.warning("Cannot find database inside downloaded GEO IP file at " + tempFile); + return; + } + + ConsoleLogger.info("Successfully downloaded new GEO IP database to " + dataFile); + + //only set this value to false on success otherwise errors could lead to endless download triggers + downloading = false; + } catch (IOException ioEx) { + ConsoleLogger.logException("Could not download GeoLiteAPI database", ioEx); + } finally { + // clean up + if (tempFile != null) { + FileUtils.delete(tempFile.toFile()); } } }); } + /** + * Verify if the expected checksum is equal to the checksum of the given file. + * + * @param function the checksum function like MD5, SHA256 used to generate the checksum from the file + * @param file the file we want to calculate the checksum from + * @param expectedChecksum the expected checksum + * @return true if equal, false otherwise + * @throws IOException on I/O error reading the file + */ + private boolean verifyChecksum(HashFunction function, Path file, String expectedChecksum) throws IOException { + HashCode actualHash = function.hashBytes(Files.readAllBytes(file)); + HashCode expectedHash = HashCode.fromString(expectedChecksum); + if (Objects.equals(actualHash, expectedHash)) { + return true; + } + + ConsoleLogger.warning("GEO IP checksum verification failed"); + ConsoleLogger.warning("Expected: " + expectedHash + " Actual: " + actualHash); + return false; + } + + /** + * Extract the database from the tar archive. Existing outputFile will be replaced if it already exists. + * + * @param tarInputFile gzipped tar input file where the database is + * @param outputFile destination file for the database + * @return true if the database was found, false otherwise + * @throws IOException on I/O error reading the tar archive or writing the output + */ + private boolean extractDatabase(Path tarInputFile, Path outputFile) throws IOException { + // .gz -> gzipped file + try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(tarInputFile)); + TarInputStream tarIn = new TarInputStream(new GZIPInputStream(in))) { + TarEntry entry; + while ((entry = tarIn.getNextEntry()) != null) { + if (!entry.isDirectory()) { + // filename including folders (absolute path inside the archive) + String filename = entry.getName(); + if (filename.endsWith(DATABASE_EXT)) { + // found the database file + Files.copy(tarIn, outputFile, StandardCopyOption.REPLACE_EXISTING); + + // update the last modification date to be same as in the archive + Files.setLastModifiedTime(outputFile, FileTime.from(entry.getModTime().toInstant())); + return true; + } + } + } + } + + return false; + } + /** * Get the country code of the given IP address. * * @param ip textual IP address to lookup. - * * @return two-character ISO 3166-1 alpha code for the country. */ public String getCountryCode(String ip) { - if (!InternetProtocolUtils.isLocalAddress(ip) && isDataAvailable()) { - return lookupService.getCountry(ip).getCode(); - } - return "--"; + return getCountry(ip).map(Country::getIsoCode).orElse("--"); } /** * Get the country name of the given IP address. * * @param ip textual IP address to lookup. - * * @return The name of the country. */ public String getCountryName(String ip) { - if (!InternetProtocolUtils.isLocalAddress(ip) && isDataAvailable()) { - return lookupService.getCountry(ip).getName(); - } - return "N/A"; + return getCountry(ip).map(Country::getName).orElse("N/A"); } + /** + * Get the country of the given IP address + * + * @param ip textual IP address to lookup + * @return the wrapped Country model or {@link Optional#empty()} if + *
      + *
    • Database reader isn't initialized
    • + *
    • MaxMind has no record about this IP address
    • + *
    • IP address is local
    • + *
    • Textual representation is not a valid IP address
    • + *
    + */ + private Optional getCountry(String ip) { + if (ip == null || ip.isEmpty() || InternetProtocolUtils.isLocalAddress(ip) || !isDataAvailable()) { + return Optional.empty(); + } + + try { + InetAddress address = InetAddress.getByName(ip); + + //Reader.getCountry() can be null for unknown addresses + return Optional.ofNullable(databaseReader.getCountry(address)).map(CountryResponse::getCountry); + } catch (UnknownHostException e) { + // Ignore invalid ip addresses + // Legacy GEO IP Database returned a unknown country object with Country-Code: '--' and Country-Name: 'N/A' + } catch (IOException ioEx) { + ConsoleLogger.logException("Cannot lookup country for " + ip + " at GEO IP database", ioEx); + } + + return Optional.empty(); + } } diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index f0a6c2f74..469d983af 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -22,7 +22,7 @@ public final class ProtectionSettings implements SettingsHolder { @Comment({ "Countries allowed to join the server and register. For country codes, see", - "http://dev.maxmind.com/geoip/legacy/codes/iso3166/", + "https://dev.maxmind.com/geoip/legacy/codes/iso3166/", "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_WHITELIST = newListProperty("Protection.countries", "US", "GB"); diff --git a/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java b/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java index 60e3aa680..47dfb54e4 100644 --- a/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java @@ -1,7 +1,13 @@ package fr.xephi.authme.service; -import com.maxmind.geoip.Country; -import com.maxmind.geoip.LookupService; +import com.maxmind.db.GeoIp2Provider; +import com.maxmind.db.model.Country; +import com.maxmind.db.model.CountryResponse; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -10,13 +16,11 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import java.io.File; -import java.io.IOException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -29,8 +33,12 @@ public class GeoIpServiceTest { private GeoIpService geoIpService; private File dataFolder; + @Mock - private LookupService lookupService; + private GeoIp2Provider lookupService; + + @Mock + private BukkitService bukkitService; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -38,20 +46,24 @@ public class GeoIpServiceTest { @Before public void initializeGeoLiteApi() throws IOException { dataFolder = temporaryFolder.newFolder(); - geoIpService = new GeoIpService(dataFolder, lookupService); + geoIpService = new GeoIpService(dataFolder, bukkitService, lookupService); } @Test - public void shouldGetCountry() { + public void shouldGetCountry() throws Exception { // given - String ip = "123.45.67.89"; + InetAddress ip = InetAddress.getByName("123.45.67.89"); String countryCode = "XX"; + Country country = mock(Country.class); - given(country.getCode()).willReturn(countryCode); - given(lookupService.getCountry(ip)).willReturn(country); + given(country.getIsoCode()).willReturn(countryCode); + + CountryResponse response = mock(CountryResponse.class); + given(response.getCountry()).willReturn(country); + given(lookupService.getCountry(ip)).willReturn(response); // when - String result = geoIpService.getCountryCode(ip); + String result = geoIpService.getCountryCode(ip.getHostAddress()); // then assertThat(result, equalTo(countryCode)); @@ -59,7 +71,7 @@ public class GeoIpServiceTest { } @Test - public void shouldNotLookUpCountryForLocalhostIp() { + public void shouldNotLookUpCountryForLocalhostIp() throws Exception { // given String ip = "127.0.0.1"; @@ -68,20 +80,24 @@ public class GeoIpServiceTest { // then assertThat(result, equalTo("--")); - verify(lookupService, never()).getCountry(anyString()); + verify(lookupService, never()).getCountry(any()); } @Test - public void shouldLookUpCountryName() { + public void shouldLookUpCountryName() throws Exception { // given - String ip = "24.45.167.89"; + InetAddress ip = InetAddress.getByName("24.45.167.89"); String countryName = "Ecuador"; + Country country = mock(Country.class); given(country.getName()).willReturn(countryName); - given(lookupService.getCountry(ip)).willReturn(country); + + CountryResponse response = mock(CountryResponse.class); + given(response.getCountry()).willReturn(country); + given(lookupService.getCountry(ip)).willReturn(response); // when - String result = geoIpService.getCountryName(ip); + String result = geoIpService.getCountryName(ip.getHostAddress()); // then assertThat(result, equalTo(countryName)); @@ -89,16 +105,15 @@ public class GeoIpServiceTest { } @Test - public void shouldNotLookUpCountryNameForLocalhostIp() { + public void shouldNotLookUpCountryNameForLocalhostIp() throws Exception { // given - String ip = "127.0.0.1"; + InetAddress ip = InetAddress.getByName("127.0.0.1"); // when - String result = geoIpService.getCountryName(ip); + String result = geoIpService.getCountryName(ip.getHostAddress()); // then assertThat(result, equalTo("N/A")); verify(lookupService, never()).getCountry(ip); } - } From fed07714b5c53f9f4850f4ed2f96c8f9ebe94e3a Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sat, 17 Mar 2018 03:06:07 +0100 Subject: [PATCH 091/155] Pom cleanup --- pom.xml | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 6f5b90609..0ee1ecf65 100644 --- a/pom.xml +++ b/pom.xml @@ -248,7 +248,7 @@ com.google - fr.xephi.authme.libs.google + fr.xephi.authme.libs.com.google ch.jalu.injector @@ -256,27 +256,27 @@ ch.jalu.configme - fr.xephi.authme.libs.jalu.configme + fr.xephi.authme.libs.ch.jalu.configme com.zaxxer.hikari - fr.xephi.authme.libs.zaxxer.hikari + fr.xephi.authme.libs.com.zaxxer.hikari org.slf4j - fr.xephi.authme.libs.slf4j.slf4j + fr.xephi.authme.libs.org.slf4j com.maxmind.db - fr.xephi.authme.libs.maxmind + fr.xephi.authme.libs.com.maxmind.db com.ice.tar - fr.xephi.authme.libs.tar + fr.xephi.authme.libs.com.icetar.tar net.ricecode.similarity - fr.xephi.authme.libs.ricecode.similarity + fr.xephi.authme.libs.ricecode.net.ricecode.similarity de.rtner @@ -372,7 +372,6 @@ ch.jalu injector 1.0 - compile true
    @@ -381,7 +380,6 @@ net.ricecode string-similarity 1.0.0 - compile true @@ -390,7 +388,6 @@ com.google.code.gson gson 2.8.2 - compile true @@ -399,7 +396,6 @@ com.google.guava guava 24.0-jre - compile true @@ -409,6 +405,7 @@ com.maxmind.db maxmind-db-gson 2.0.2-SNAPSHOT + true @@ -416,6 +413,7 @@ javatar javatar 2.5 + true @@ -423,7 +421,6 @@ org.apache.commons commons-email 1.5 - compile true @@ -440,7 +437,6 @@ com.zaxxer HikariCP 2.7.8 - compile true @@ -454,7 +450,6 @@ org.slf4j slf4j-simple 1.7.25 - compile true @@ -463,7 +458,6 @@ de.rtner PBKDF2 1.1.2 - compile true @@ -472,7 +466,6 @@ de.mkammerer argon2-jvm-nolibs 2.4 - compile true @@ -513,7 +506,6 @@ ch.jalu configme 0.4.1 - compile true @@ -528,7 +520,6 @@ org.bstats bstats-bukkit 1.2 - compile true From aab5d71efdc0164a52d376f0263465e9fdcf322d Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Mon, 19 Mar 2018 16:53:11 +0100 Subject: [PATCH 092/155] Update sponsort details --- README.md | 2 +- src/main/java/fr/xephi/authme/AuthMe.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 24514408c..21910e4ef 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ You can also create your own translation file and, if you want, you can share it ## Credits ##### Sponsor: -[GameHosting.it](http://www.gamehosting.it) is leader in Italy as Game Server Provider. With its own DataCenter offers Anti-DDoS solutions at affordable prices. Game Server of Minecraft based on Multicraft are equipped with the latest technology in hardware. +[FastVM.io](https://fastvm.io) is leader in VPS hosting solutions. With its own DataCenter offers Anti-DDoS solutions at affordable prices. ##### Contributors: Team members: developers, translators diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 305aadc38..4aeb536c1 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -159,9 +159,9 @@ public class AuthMe extends JavaPlugin { OnStartupTasks.sendMetrics(this, settings); // Sponsor messages - ConsoleLogger.info("Development builds are available on our jenkins, thanks to f14stelt."); - ConsoleLogger.info("Do you want a good game server? Look at our sponsor GameHosting.it leader " - + "in Italy as Game Server Provider!"); + ConsoleLogger.info("Development builds are available on our jenkins, thanks to FastVM.io"); + ConsoleLogger.info("Do you want a good vps for your game server? Look at our sponsor FastVM.io leader " + + "as virtual server provider!"); // Successful message ConsoleLogger.info("AuthMe " + getPluginVersion() + " build n." + getPluginBuildNumber() From 3a690fd79aee8990391c07148b9f8a555f656356 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Mon, 19 Mar 2018 17:23:51 +0100 Subject: [PATCH 093/155] Change how we detect spigot #1531 --- src/main/java/fr/xephi/authme/util/Utils.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index b51eb9cfe..5a2443199 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -1,6 +1,7 @@ package fr.xephi.authme.util; import fr.xephi.authme.ConsoleLogger; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; @@ -28,7 +29,12 @@ public final class Utils { * @return true if the running server instance is spigot-based. */ public static boolean isSpigot() { - return isClassLoaded("org.spigotmc.SpigotConfig"); + try { + Bukkit.spigot(); + return true; + } catch (NoSuchMethodError e) { + return false; + } } /** From 8ee44e495b0ae62c2d915ad26782a28f6353efe3 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Mon, 19 Mar 2018 17:30:53 +0100 Subject: [PATCH 094/155] Decrease delay for connectPlayerOnLogin #1504 --- .../java/fr/xephi/authme/service/bungeecord/BungeeSender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java b/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java index 6c559f6b0..84fda6956 100644 --- a/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java +++ b/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java @@ -65,7 +65,7 @@ public class BungeeSender implements SettingsDependent { public void connectPlayerOnLogin(Player player) { if (isEnabled && !destinationServerOnLogin.isEmpty()) { bukkitService.scheduleSyncDelayedTask(() -> - sendBungeecordMessage("ConnectOther", player.getName(), destinationServerOnLogin), 20L); + sendBungeecordMessage("ConnectOther", player.getName(), destinationServerOnLogin), 5L); } } From 84f97ea1c2211d6025019b964409885f3e1c0d92 Mon Sep 17 00:00:00 2001 From: HexelDev Date: Mon, 19 Mar 2018 22:33:53 +0100 Subject: [PATCH 095/155] Add QuickCommandsProtectionManager#processJoin(player) --- .../data/QuickCommandsProtectionManager.java | 26 ++++++++++++------ .../xephi/authme/listener/PlayerListener.java | 4 +-- .../QuickCommandsProtectionManagerTest.java | 27 ++++++++++++++----- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java index 6e697db16..335944c82 100644 --- a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java +++ b/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java @@ -16,13 +16,13 @@ public class QuickCommandsProtectionManager implements SettingsDependent, HasCle private final PermissionsManager permissionsManager; - private final ExpiringSet latestLogin; + private final ExpiringSet latestJoin; @Inject public QuickCommandsProtectionManager(Settings settings, PermissionsManager permissionsManager) { this.permissionsManager = permissionsManager; long countTimeout = settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS); - latestLogin = new ExpiringSet<>(countTimeout, TimeUnit.MILLISECONDS); + latestJoin = new ExpiringSet<>(countTimeout, TimeUnit.MILLISECONDS); reload(settings); } @@ -30,8 +30,8 @@ public class QuickCommandsProtectionManager implements SettingsDependent, HasCle * Save the player in the set * @param name the player's name */ - public void setLogin(String name) { - latestLogin.add(name); + private void setJoin(String name) { + latestJoin.add(name); } /** @@ -39,27 +39,37 @@ public class QuickCommandsProtectionManager implements SettingsDependent, HasCle * @param player the player to check * @return true if the player has the permission, false otherwise */ - public boolean shouldSaveLogin(Player player) { + private boolean shouldSavePlayer(Player player) { return permissionsManager.hasPermission(player, PlayerPermission.QUICK_COMMANDS_PROTECTION); } + /** + * Process the player join + * @param player the player to process + */ + public void processJoin(Player player) { + if(shouldSavePlayer(player)) { + setJoin(player.getName()); + } + } + /** * Returns whether the given player is able to perform the command * @param name the name of the player to check * @return true if the player is not in the set (so it's allowed to perform the command), false otherwise */ public boolean isAllowed(String name) { - return !latestLogin.contains(name); + return !latestJoin.contains(name); } @Override public void reload(Settings settings) { long countTimeout = settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS); - latestLogin.setExpiration(countTimeout, TimeUnit.MILLISECONDS); + latestJoin.setExpiration(countTimeout, TimeUnit.MILLISECONDS); } @Override public void performCleanup() { - latestLogin.removeExpiredEntries(); + latestJoin.removeExpiredEntries(); } } diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 6ceeb835c..7857114e3 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -216,9 +216,7 @@ public class PlayerListener implements Listener { teleportationService.teleportOnJoin(player); } - if (quickCommandsProtectionManager.shouldSaveLogin(player)) { - quickCommandsProtectionManager.setLogin(player.getName()); - } + quickCommandsProtectionManager.processJoin(player); management.performJoin(player); diff --git a/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java b/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java index 10ef88eb8..704075674 100644 --- a/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java @@ -1,8 +1,10 @@ package fr.xephi.authme.data; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; +import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -11,6 +13,7 @@ import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; /** * Test for {@link QuickCommandsProtectionManager}. @@ -27,16 +30,19 @@ public class QuickCommandsProtectionManagerTest { @Test public void shouldAllowCommand() { // given - String name1 = "TestName1"; - String name2 = "TestName2"; + String playername = "PlayerName"; + Player player = mockPlayerWithName(playername); given(settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS)).willReturn(0); + given(permissionsManager.hasPermission(player, PlayerPermission.QUICK_COMMANDS_PROTECTION)).willReturn(true); + + String name = "TestName"; QuickCommandsProtectionManager qcpm = createQuickCommandsProtectioneManager(); - qcpm.setLogin(name2); + qcpm.processJoin(player); // when - boolean test1 = qcpm.isAllowed(name1); - boolean test2 = qcpm.isAllowed(name2); + boolean test1 = qcpm.isAllowed(name); + boolean test2 = qcpm.isAllowed(playername); // then assertThat(test1, equalTo(true)); @@ -47,10 +53,12 @@ public class QuickCommandsProtectionManagerTest { public void shouldDenyCommand() { // given String name = "TestName1"; + Player player = mockPlayerWithName(name); given(settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS)).willReturn(5000); QuickCommandsProtectionManager qcpm = createQuickCommandsProtectioneManager(); - qcpm.setLogin(name); + given(permissionsManager.hasPermission(player, PlayerPermission.QUICK_COMMANDS_PROTECTION)).willReturn(true); + qcpm.processJoin(player); // when boolean test = qcpm.isAllowed(name); @@ -62,4 +70,11 @@ public class QuickCommandsProtectionManagerTest { private QuickCommandsProtectionManager createQuickCommandsProtectioneManager() { return new QuickCommandsProtectionManager(settings, permissionsManager); } + + private static Player mockPlayerWithName(String name) { + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + return player; + } + } From a1a909c01d0b8739b209b388d50ca338da468bae Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 19 Mar 2018 23:08:48 +0100 Subject: [PATCH 096/155] #1531 Move spigot detection to BukkitService (#1534) --- .../xephi/authme/service/BukkitService.java | 17 ++++++++++++ .../xephi/authme/settings/SettingsWarner.java | 13 ++++++--- src/main/java/fr/xephi/authme/util/Utils.java | 15 ----------- .../authme/settings/SettingsWarnerTest.java | 27 +++++++++++-------- 4 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/BukkitService.java b/src/main/java/fr/xephi/authme/service/BukkitService.java index 6c9cd0cb8..09b4869fb 100644 --- a/src/main/java/fr/xephi/authme/service/BukkitService.java +++ b/src/main/java/fr/xephi/authme/service/BukkitService.java @@ -12,6 +12,7 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.scheduler.BukkitRunnable; @@ -25,6 +26,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -376,4 +378,19 @@ public class BukkitService implements SettingsDependent { public BanEntry banIp(String ip, String reason, Date expires, String source) { return Bukkit.getServer().getBanList(BanList.Type.IP).addBan(ip, reason, expires, source); } + + /** + * Returns an optional with a boolean indicating whether bungeecord is enabled or not if the + * server implementation is Spigot. Otherwise returns an empty optional. + * + * @return Optional with configuration value for Spigot, empty optional otherwise + */ + public Optional isBungeeCordConfiguredForSpigot() { + try { + YamlConfiguration spigotConfig = Bukkit.spigot().getConfig(); + return Optional.of(spigotConfig.getBoolean("settings.bungeecord")); + } catch (NoSuchMethodError e) { + return Optional.empty(); + } + } } diff --git a/src/main/java/fr/xephi/authme/settings/SettingsWarner.java b/src/main/java/fr/xephi/authme/settings/SettingsWarner.java index c94b0455c..d055dd0fd 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsWarner.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsWarner.java @@ -4,15 +4,15 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.crypts.Argon2; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.util.Utils; -import org.bukkit.Bukkit; import javax.inject.Inject; +import java.util.Optional; /** * Logs warning messages in cases where the configured values suggest a misconfiguration. @@ -29,6 +29,9 @@ public class SettingsWarner { @Inject private AuthMe authMe; + @Inject + private BukkitService bukkitService; + SettingsWarner() { } @@ -54,7 +57,7 @@ public class SettingsWarner { } // Warn if spigot.yml has settings.bungeecord set to true but config.yml has Hooks.bungeecord set to false - if (Utils.isSpigot() && Bukkit.spigot().getConfig().getBoolean("settings.bungeecord") + if (isTrue(bukkitService.isBungeeCordConfiguredForSpigot()) && !settings.getProperty(HooksSettings.BUNGEECORD)) { ConsoleLogger.warning("Note: Hooks.bungeecord is set to false but your server appears to be running in" + " bungeecord mode (see your spigot.yml). In order to allow the datasource caching and the" @@ -69,4 +72,8 @@ public class SettingsWarner { authMe.stopOrUnload(); } } + + private static boolean isTrue(Optional value) { + return value.isPresent() && value.get(); + } } diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 5a2443199..4db7f61bc 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -1,7 +1,6 @@ package fr.xephi.authme.util; import fr.xephi.authme.ConsoleLogger; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; @@ -23,20 +22,6 @@ public final class Utils { private Utils() { } - /** - * Returns if the running server instance is craftbukkit or spigot based. - * - * @return true if the running server instance is spigot-based. - */ - public static boolean isSpigot() { - try { - Bukkit.spigot(); - return true; - } catch (NoSuchMethodError e) { - return false; - } - } - /** * Compile Pattern sneaky without throwing Exception. * diff --git a/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java b/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java index d237268cc..16271bd94 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java +++ b/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java @@ -1,18 +1,21 @@ package fr.xephi.authme.settings; import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.properties.EmailSettings; +import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.Optional; import java.util.logging.Logger; import static org.mockito.ArgumentMatchers.anyString; @@ -27,12 +30,18 @@ import static org.mockito.internal.verification.VerificationModeFactory.times; @RunWith(MockitoJUnitRunner.class) public class SettingsWarnerTest { + @InjectMocks + private SettingsWarner settingsWarner; + @Mock private Settings settings; @Mock private AuthMe authMe; + @Mock + private BukkitService bukkitService; + @Test public void shouldLogWarnings() { // given @@ -43,12 +52,14 @@ public class SettingsWarnerTest { given(settings.getProperty(PluginSettings.SESSIONS_ENABLED)).willReturn(true); given(settings.getProperty(PluginSettings.SESSIONS_TIMEOUT)).willReturn(-5); given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.BCRYPT); + given(settings.getProperty(HooksSettings.BUNGEECORD)).willReturn(false); + given(bukkitService.isBungeeCordConfiguredForSpigot()).willReturn(Optional.of(true)); // when - createSettingsWarner().logWarningsForMisconfigurations(); + settingsWarner.logWarningsForMisconfigurations(); // then - verify(logger, times(3)).warning(anyString()); + verify(logger, times(4)).warning(anyString()); } @Test @@ -59,18 +70,12 @@ public class SettingsWarnerTest { given(settings.getProperty(EmailSettings.PORT25_USE_TLS)).willReturn(false); given(settings.getProperty(PluginSettings.SESSIONS_ENABLED)).willReturn(false); given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.MD5); + given(bukkitService.isBungeeCordConfiguredForSpigot()).willReturn(Optional.empty()); // when - createSettingsWarner().logWarningsForMisconfigurations(); + settingsWarner.logWarningsForMisconfigurations(); // then verifyZeroInteractions(logger); } - - private SettingsWarner createSettingsWarner() { - SettingsWarner warner = new SettingsWarner(); - ReflectionTestUtils.setField(warner, "settings", settings); - ReflectionTestUtils.setField(warner, "authMe", authMe); - return warner; - } } From 6251a69d3eda3bb62842965256519075e68ad367 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 20 Mar 2018 10:42:17 +0100 Subject: [PATCH 097/155] Use the latest LuckPerms api methods --- .../permission/handlers/LuckPermsHandler.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java index 9e52746fc..f01f14c34 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java @@ -1,5 +1,6 @@ package fr.xephi.authme.permission.handlers; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsSystemType; import me.lucko.luckperms.LuckPerms; @@ -41,13 +42,8 @@ public class LuckPermsHandler implements PermissionHandler { } private void saveUser(User user) { - luckPermsApi.getStorage().saveUser(user) - .thenAcceptAsync(wasSuccessful -> { - if (!wasSuccessful) { - return; - } - user.refreshPermissions(); - }, luckPermsApi.getStorage().getAsyncExecutor()); + luckPermsApi.getUserManager().saveUser(user) + .thenAcceptAsync(wasSuccessful -> user.refreshCachedData()); } @Override @@ -62,7 +58,7 @@ public class LuckPermsHandler implements PermissionHandler { return false; } - DataMutateResult result = user.setPermissionUnchecked( + DataMutateResult result = user.setPermission( luckPermsApi.getNodeFactory().makeGroupNode(newGroup).build()); if (result == DataMutateResult.FAIL) { return false; @@ -83,6 +79,8 @@ public class LuckPermsHandler implements PermissionHandler { public boolean hasPermissionOffline(String name, PermissionNode node) { User user = luckPermsApi.getUser(name); if (user == null) { + ConsoleLogger.warning("LuckPermsHandler: tried to check permission for offline user " + + name + " but it isn't loaded!"); return false; } @@ -98,6 +96,8 @@ public class LuckPermsHandler implements PermissionHandler { public boolean isInGroup(OfflinePlayer player, String group) { User user = luckPermsApi.getUser(player.getName()); if (user == null) { + ConsoleLogger.warning("LuckPermsHandler: tried to check group for offline user " + + player.getName() + " but it isn't loaded!"); return false; } @@ -112,6 +112,8 @@ public class LuckPermsHandler implements PermissionHandler { public boolean removeFromGroup(OfflinePlayer player, String group) { User user = luckPermsApi.getUser(player.getName()); if (user == null) { + ConsoleLogger.warning("LuckPermsHandler: tried to remove group for offline user " + + player.getName() + " but it isn't loaded!"); return false; } @@ -121,7 +123,7 @@ public class LuckPermsHandler implements PermissionHandler { } Node groupNode = luckPermsApi.getNodeFactory().makeGroupNode(permissionGroup).build(); - boolean result = user.unsetPermissionUnchecked(groupNode) != DataMutateResult.FAIL; + boolean result = user.unsetPermission(groupNode) != DataMutateResult.FAIL; luckPermsApi.cleanupUser(user); return result; @@ -131,6 +133,8 @@ public class LuckPermsHandler implements PermissionHandler { public boolean setGroup(OfflinePlayer player, String group) { User user = luckPermsApi.getUser(player.getName()); if (user == null) { + ConsoleLogger.warning("LuckPermsHandler: tried to set group for offline user " + + player.getName() + " but it isn't loaded!"); return false; } Group permissionGroup = luckPermsApi.getGroup(group); @@ -138,7 +142,7 @@ public class LuckPermsHandler implements PermissionHandler { return false; } Node groupNode = luckPermsApi.getNodeFactory().makeGroupNode(permissionGroup).build(); - DataMutateResult result = user.setPermissionUnchecked(groupNode); + DataMutateResult result = user.setPermission(groupNode); if (result == DataMutateResult.FAIL) { return false; } @@ -153,6 +157,8 @@ public class LuckPermsHandler implements PermissionHandler { public List getGroups(OfflinePlayer player) { User user = luckPermsApi.getUser(player.getName()); if (user == null) { + ConsoleLogger.warning("LuckPermsHandler: tried to get groups for offline user " + + player.getName() + " but it isn't loaded!"); return Collections.emptyList(); } @@ -185,7 +191,7 @@ public class LuckPermsHandler implements PermissionHandler { @Override public void loadUserData(UUID uuid) { try { - luckPermsApi.getStorage().loadUser(uuid).get(5, TimeUnit.SECONDS); + luckPermsApi.getUserManager().loadUser(uuid).get(5, TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { e.printStackTrace(); } From 495cfc69a951a3b6b913d0b5c642a963069bc8ff Mon Sep 17 00:00:00 2001 From: ljacqu Date: Tue, 20 Mar 2018 23:06:08 +0100 Subject: [PATCH 098/155] #1141 Move TOTP code during login as separate step: /2fa code Rough version. - Introduces a limbo player state on the LimboPlayer, allowing us to add further mandatory actions between successful (password) authentication and the ability to play on the server --- .../authme/command/CommandInitializer.java | 11 ++- .../executable/captcha/CaptchaCommand.java | 3 +- .../executable/login/LoginCommand.java | 3 +- .../executable/totp/TotpCodeCommand.java | 72 +++++++++++++++++++ .../authme/data/limbo/LimboMessageType.java | 11 +++ .../xephi/authme/data/limbo/LimboPlayer.java | 9 +++ .../authme/data/limbo/LimboPlayerState.java | 11 +++ .../data/limbo/LimboPlayerTaskManager.java | 14 ++-- .../xephi/authme/data/limbo/LimboService.java | 11 +-- .../fr/xephi/authme/message/MessageKey.java | 3 + .../message/updater/MessageUpdater.java | 1 + .../fr/xephi/authme/process/Management.java | 4 +- .../process/login/AsynchronousLogin.java | 37 ++++------ .../properties/RestrictionSettings.java | 2 +- src/main/resources/messages/messages_en.yml | 3 + .../executable/login/LoginCommandTest.java | 15 +--- .../limbo/LimboPlayerTaskManagerTest.java | 8 +-- .../authme/data/limbo/LimboServiceTest.java | 6 +- 18 files changed, 162 insertions(+), 62 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java create mode 100644 src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java create mode 100644 src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index e1890626b..e2450ba80 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -44,6 +44,7 @@ import fr.xephi.authme.command.executable.totp.AddTotpCommand; import fr.xephi.authme.command.executable.totp.ConfirmTotpCommand; import fr.xephi.authme.command.executable.totp.RemoveTotpCommand; import fr.xephi.authme.command.executable.totp.TotpBaseCommand; +import fr.xephi.authme.command.executable.totp.TotpCodeCommand; import fr.xephi.authme.command.executable.unregister.UnregisterCommand; import fr.xephi.authme.command.executable.verification.VerificationCommand; import fr.xephi.authme.permission.AdminPermission; @@ -92,7 +93,6 @@ public class CommandInitializer { .description("Login command") .detailedDescription("Command to log in using AuthMeReloaded.") .withArgument("password", "Login password", MANDATORY) - .withArgument("2facode", "TOTP code", OPTIONAL) .permission(PlayerPermission.LOGIN) .executableCommand(LoginCommand.class) .register(); @@ -561,6 +561,15 @@ public class CommandInitializer { .executableCommand(TotpBaseCommand.class) .register(); + // Register the base totp code + CommandDescription.builder() + .parent(null) + .labels("code", "c") + .description("Command for logging in") + .detailedDescription("Processes the two-factor authentication code during login.") + .executableCommand(TotpCodeCommand.class) + .register(); + // Register totp add CommandDescription.builder() .parent(totpBase) diff --git a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java index b0da817f6..c486dd042 100644 --- a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java @@ -4,6 +4,7 @@ import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.captcha.LoginCaptchaManager; import fr.xephi.authme.data.captcha.RegistrationCaptchaManager; +import fr.xephi.authme.data.limbo.LimboMessageType; import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; @@ -80,6 +81,6 @@ public class CaptchaCommand extends PlayerCommand { String newCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName()); commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode); } - limboService.resetMessageTask(player, false); + limboService.resetMessageTask(player, LimboMessageType.REGISTER); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java b/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java index e2878271d..57ae86393 100644 --- a/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java @@ -19,8 +19,7 @@ public class LoginCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments) { String password = arguments.get(0); - String totpCode = arguments.size() > 1 ? arguments.get(1) : null; - management.performLogin(player, password, totpCode); + management.performLogin(player, password); } @Override diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java new file mode 100644 index 000000000..74d85746f --- /dev/null +++ b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java @@ -0,0 +1,72 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.data.auth.PlayerCache; +import fr.xephi.authme.data.limbo.LimboPlayer; +import fr.xephi.authme.data.limbo.LimboPlayerState; +import fr.xephi.authme.data.limbo.LimboService; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.process.login.AsynchronousLogin; +import fr.xephi.authme.security.totp.TotpService; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.util.List; + +/** + * TOTP code command for processing the 2FA code during the login process. + */ +public class TotpCodeCommand extends PlayerCommand { + + @Inject + private LimboService limboService; + + @Inject + private PlayerCache playerCache; + + @Inject + private Messages messages; + + @Inject + private TotpService totpService; + + @Inject + private DataSource dataSource; + + @Inject + private AsynchronousLogin asynchronousLogin; + + @Override + protected void runCommand(Player player, List arguments) { + if (playerCache.isAuthenticated(player.getName())) { + messages.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + return; + } + + PlayerAuth auth = dataSource.getAuth(player.getName()); + if (auth == null) { + messages.send(player, MessageKey.REGISTER_MESSAGE); + return; + } + + LimboPlayer limbo = limboService.getLimboPlayer(player.getName()); + if (limbo.getState() == LimboPlayerState.TOTP_REQUIRED) { + processCode(player, limbo, auth, arguments.get(0)); + } else { + messages.send(player, MessageKey.LOGIN_MESSAGE); + } + } + + private void processCode(Player player, LimboPlayer limbo, PlayerAuth auth, String inputCode) { + boolean isCodeValid = totpService.verifyCode(auth, inputCode); + if (isCodeValid) { + limbo.setState(LimboPlayerState.FINISHED); + asynchronousLogin.performLogin(player, auth); + } else { + player.sendMessage("Invalid code!"); + } + } +} diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java b/src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java new file mode 100644 index 000000000..4d0af3e5a --- /dev/null +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java @@ -0,0 +1,11 @@ +package fr.xephi.authme.data.limbo; + +public enum LimboMessageType { + + REGISTER, + + LOG_IN, + + TOTP_CODE + +} diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java index b7ea415c8..03c3ea138 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java @@ -23,6 +23,7 @@ public class LimboPlayer { private final float flySpeed; private BukkitTask timeoutTask = null; private MessageTask messageTask = null; + private LimboPlayerState state = LimboPlayerState.PASSWORD_REQUIRED; public LimboPlayer(Location loc, boolean operator, Collection groups, boolean fly, float walkSpeed, float flySpeed) { @@ -124,4 +125,12 @@ public class LimboPlayer { setMessageTask(null); setTimeoutTask(null); } + + public LimboPlayerState getState() { + return state; + } + + public void setState(LimboPlayerState state) { + this.state = state; + } } diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java new file mode 100644 index 000000000..24d6cb0f8 --- /dev/null +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java @@ -0,0 +1,11 @@ +package fr.xephi.authme.data.limbo; + +public enum LimboPlayerState { + + PASSWORD_REQUIRED, + + TOTP_REQUIRED, + + FINISHED + +} diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java index e5f4f65d0..3612e6797 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java @@ -45,11 +45,11 @@ class LimboPlayerTaskManager { * * @param player the player * @param limbo the associated limbo player of the player - * @param isRegistered whether the player is registered or not (needed to determine the message in the task) + * @param messageType message type */ - void registerMessageTask(Player player, LimboPlayer limbo, boolean isRegistered) { + void registerMessageTask(Player player, LimboPlayer limbo, LimboMessageType messageType) { int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL); - MessageResult result = getMessageKey(player.getName(), isRegistered); + MessageResult result = getMessageKey(player.getName(), messageType); if (interval > 0) { String[] joinMessage = messages.retrieveSingle(player, result.messageKey, result.args).split("\n"); MessageTask messageTask = new MessageTask(player, joinMessage); @@ -89,12 +89,14 @@ class LimboPlayerTaskManager { * Returns the appropriate message key according to the registration status and settings. * * @param name the player's name - * @param isRegistered whether or not the username is registered + * @param messageType the message to show * @return the message key to display to the user */ - private MessageResult getMessageKey(String name, boolean isRegistered) { - if (isRegistered) { + private MessageResult getMessageKey(String name, LimboMessageType messageType) { + if (messageType == LimboMessageType.LOG_IN) { return new MessageResult(MessageKey.LOGIN_MESSAGE); + } else if (messageType == LimboMessageType.TOTP_CODE) { + return new MessageResult(MessageKey.TWO_FACTOR_CODE_REQUIRED); } else if (registrationCaptchaManager.isCaptchaRequired(name)) { final String captchaCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(name); return new MessageResult(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captchaCode); diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboService.java b/src/main/java/fr/xephi/authme/data/limbo/LimboService.java index 982f359a8..fae9d44ed 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboService.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboService.java @@ -69,7 +69,8 @@ public class LimboService { LimboPlayer limboPlayer = helper.merge(existingLimbo, limboFromDisk); limboPlayer = helper.merge(helper.createLimboPlayer(player, isRegistered, location), limboPlayer); - taskManager.registerMessageTask(player, limboPlayer, isRegistered); + taskManager.registerMessageTask(player, limboPlayer, + isRegistered ? LimboMessageType.LOG_IN : LimboMessageType.REGISTER); taskManager.registerTimeoutTask(player, limboPlayer); helper.revokeLimboStates(player); authGroupHandler.setGroup(player, limboPlayer, @@ -134,7 +135,7 @@ public class LimboService { Optional limboPlayer = getLimboOrLogError(player, "reset tasks"); limboPlayer.ifPresent(limbo -> { taskManager.registerTimeoutTask(player, limbo); - taskManager.registerMessageTask(player, limbo, true); + taskManager.registerMessageTask(player, limbo, LimboMessageType.LOG_IN); }); authGroupHandler.setGroup(player, limboPlayer.orElse(null), AuthGroupType.REGISTERED_UNAUTHENTICATED); } @@ -143,11 +144,11 @@ public class LimboService { * Resets the message task associated with the player's LimboPlayer. * * @param player the player to set a new message task for - * @param isRegistered whether or not the player is registered + * @param messageType the message to show for the limbo player */ - public void resetMessageTask(Player player, boolean isRegistered) { + public void resetMessageTask(Player player, LimboMessageType messageType) { getLimboOrLogError(player, "reset message task") - .ifPresent(limbo -> taskManager.registerMessageTask(player, limbo, isRegistered)); + .ifPresent(limbo -> taskManager.registerMessageTask(player, limbo, messageType)); } /** diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index b6367c5b1..c9f59934c 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -197,6 +197,9 @@ public enum MessageKey { /** Your secret code is %code. You can scan it from here %url */ TWO_FACTOR_CREATE("misc.two_factor_create", "%code", "%url"), + /** Please submit your two-factor authentication code with /2fa code <code>. */ + TWO_FACTOR_CODE_REQUIRED("two_factor.code_required"), + /** You are not the owner of this account. Please choose another name! */ NOT_OWNER_ERROR("on_join_validation.not_owner_error"), diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index 3b429106c..579968eec 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -131,6 +131,7 @@ public class MessageUpdater { .put("captcha", new String[]{"Captcha"}) .put("verification", new String[]{"Verification code"}) .put("time", new String[]{"Time units"}) + .put("two_factor", new String[]{"Two-factor authentication"}) .build(); Set addedKeys = new HashSet<>(); diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index a5a879e05..5260fed13 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -49,8 +49,8 @@ public class Management { } - public void performLogin(Player player, String password, String totpCode) { - runTask(() -> asynchronousLogin.login(player, password, totpCode)); + public void performLogin(Player player, String password) { + runTask(() -> asynchronousLogin.login(player, password)); } public void forceLogin(Player player) { diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index 0f8bec3f3..22c46dd95 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -6,6 +6,8 @@ import fr.xephi.authme.data.TempbanManager; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.captcha.LoginCaptchaManager; +import fr.xephi.authme.data.limbo.LimboMessageType; +import fr.xephi.authme.data.limbo.LimboPlayerState; import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.AuthMeAsyncPreLoginEvent; @@ -18,7 +20,6 @@ import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.security.totp.TotpService; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.SessionService; @@ -79,9 +80,6 @@ public class AsynchronousLogin implements AsynchronousProcess { @Inject private BungeeSender bungeeSender; - @Inject - private TotpService totpService; - AsynchronousLogin() { } @@ -90,12 +88,17 @@ public class AsynchronousLogin implements AsynchronousProcess { * * @param player the player to log in * @param password the password to log in with - * @param totpCode the totp code (nullable) */ - public void login(Player player, String password, String totpCode) { + public void login(Player player, String password) { PlayerAuth auth = getPlayerAuth(player); - if (auth != null && checkPlayerInfo(player, auth, password, totpCode)) { - performLogin(player, auth); + if (auth != null && checkPlayerInfo(player, auth, password)) { + if (auth.getTotpKey() != null) { + limboService.resetMessageTask(player, LimboMessageType.TOTP_CODE); + limboService.getLimboPlayer(player.getName()).setState(LimboPlayerState.TOTP_REQUIRED); + // TODO #1141: Check if we should check limbo state before processing password + } else { + performLogin(player, auth); + } } } @@ -130,7 +133,7 @@ public class AsynchronousLogin implements AsynchronousProcess { if (auth == null) { service.send(player, MessageKey.UNKNOWN_USER); // Recreate the message task to immediately send the message again as response - limboService.resetMessageTask(player, false); + limboService.resetMessageTask(player, LimboMessageType.REGISTER); return null; } @@ -161,11 +164,10 @@ public class AsynchronousLogin implements AsynchronousProcess { * @param player the player requesting to log in * @param auth the PlayerAuth object of the player * @param password the password supplied by the player - * @param totpCode the input totp code (nullable) * @return true if the password matches and all other conditions are met (e.g. no captcha required), * false otherwise */ - private boolean checkPlayerInfo(Player player, PlayerAuth auth, String password, String totpCode) { + private boolean checkPlayerInfo(Player player, PlayerAuth auth, String password) { final String name = player.getName().toLowerCase(); // If captcha is required send a message to the player and deny to log in @@ -180,17 +182,6 @@ public class AsynchronousLogin implements AsynchronousProcess { loginCaptchaManager.increaseLoginFailureCount(name); tempbanManager.increaseCount(ip, name); - if (auth.getTotpKey() != null) { - if (totpCode == null) { - player.sendMessage( - "You have two-factor authentication enabled. Please provide it: /login <2faCode>"); - return false; - } else if (!totpService.verifyCode(auth, totpCode)) { - player.sendMessage("Invalid code for two-factor authentication. Please try again"); - return false; - } - } - if (passwordSecurity.comparePassword(password, auth.getPassword(), player.getName())) { return true; } else { @@ -235,7 +226,7 @@ public class AsynchronousLogin implements AsynchronousProcess { * @param player the player to log in * @param auth the associated PlayerAuth object */ - private void performLogin(Player player, PlayerAuth auth) { + public void performLogin(Player player, PlayerAuth auth) { if (player.isOnline()) { final boolean isFirstLogin = (auth.getLastLogin() == null); diff --git a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java index 492af4e6d..e769dd9b0 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -26,7 +26,7 @@ public final class RestrictionSettings implements SettingsHolder { @Comment("Allowed commands for unauthenticated players") public static final Property> ALLOW_COMMANDS = newLowercaseListProperty("settings.restrictions.allowCommands", - "/login", "/register", "/l", "/reg", "/email", "/captcha"); + "/login", "/register", "/l", "/reg", "/email", "/captcha", "/2fa", "/totp"); @Comment({ "Max number of allowed registrations per IP", diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index e5e20055a..9b586bb16 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -128,6 +128,9 @@ verification: code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' email_needed: '&3To verify your identity you need to link an email address with your account!!' +two_factor: + code_required: 'Please submit your two-factor authentication code with /2fa code ' + # Time units time: second: 'second' diff --git a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java index 1335ca6f6..b5ce86a53 100644 --- a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java @@ -11,7 +11,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import java.util.Arrays; import java.util.Collections; import static org.hamcrest.Matchers.containsString; @@ -57,19 +56,7 @@ public class LoginCommandTest { command.executeCommand(sender, Collections.singletonList("password")); // then - verify(management).performLogin(sender, "password", null); - } - - @Test - public void shouldCallManagementForPasswordAndTotpCode() { - // given - Player sender = mock(Player.class); - - // when - command.executeCommand(sender, Arrays.asList("pwd", "12345")); - - // then - verify(management).performLogin(sender, "pwd", "12345"); + verify(management).performLogin(sender, "password" ); } @Test diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java index f1507a29e..ff0743001 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java @@ -76,7 +76,7 @@ public class LimboPlayerTaskManagerTest { given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(interval); // when - limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); + limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER); // then verify(limboPlayer).setMessageTask(any(MessageTask.class)); @@ -94,7 +94,7 @@ public class LimboPlayerTaskManagerTest { given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(0); // when - limboPlayerTaskManager.registerMessageTask(player, limboPlayer, true); + limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.LOG_IN); // then verifyZeroInteractions(limboPlayer, bukkitService); @@ -113,7 +113,7 @@ public class LimboPlayerTaskManagerTest { given(messages.retrieveSingle(player, MessageKey.REGISTER_MESSAGE)).willReturn("Please register!"); // when - limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); + limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER); // then assertThat(limboPlayer.getMessageTask(), not(nullValue())); @@ -137,7 +137,7 @@ public class LimboPlayerTaskManagerTest { given(messages.retrieveSingle(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha)).willReturn("Need to use captcha"); // when - limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); + limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER); // then assertThat(limboPlayer.getMessageTask(), not(nullValue())); diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java b/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java index 81feaa8ed..8bcfaff79 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java +++ b/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java @@ -90,7 +90,7 @@ public class LimboServiceTest { limboService.createLimboPlayer(player, true); // then - verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(true)); + verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.LOG_IN)); verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class)); verify(player).setAllowFlight(false); verify(player).setFlySpeed(0.0f); @@ -121,7 +121,7 @@ public class LimboServiceTest { limboService.createLimboPlayer(player, false); // then - verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(false)); + verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.REGISTER)); verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class)); verify(permissionsManager, only()).hasGroupSupport(); verify(player).setAllowFlight(false); @@ -209,7 +209,7 @@ public class LimboServiceTest { // then verify(taskManager).registerTimeoutTask(player, limbo); - verify(taskManager).registerMessageTask(player, limbo, true); + verify(taskManager).registerMessageTask(player, limbo, LimboMessageType.LOG_IN); verify(authGroupHandler).setGroup(player, limbo, AuthGroupType.REGISTERED_UNAUTHENTICATED); } From 584a0bebbfb92c9e3496b9ea7dbe08f2bb9625c2 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Tue, 20 Mar 2018 23:13:48 +0100 Subject: [PATCH 099/155] Minor: Fix failing test after command change --- .../authme/service/HelpTranslationGeneratorIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java b/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java index 9159b4248..3e094cc2b 100644 --- a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java @@ -126,7 +126,7 @@ public class HelpTranslationGeneratorIntegrationTest { // Check /login checkDescription(configuration.get("commands.login"), "Login command", "/login detailed desc."); - checkArgs(configuration.get("commands.login"), arg("loginArg", "Login password"), arg("2faArg", "TOTP code")); + checkArgs(configuration.get("commands.login"), arg("loginArg", "Login password")); // Check /unregister checkDescription(configuration.get("commands.unregister"), "unreg_desc", "unreg_detail_desc"); From e9ab82db6bea3f4c59095227208f4e426592bc0f Mon Sep 17 00:00:00 2001 From: ljacqu Date: Wed, 21 Mar 2018 23:56:13 +0100 Subject: [PATCH 100/155] #1141 Make 2fa messages translatable, various cleanups (null safety, ...) --- .../executable/totp/AddTotpCommand.java | 13 ++++++----- .../executable/totp/ConfirmTotpCommand.java | 19 +++++++++++---- .../executable/totp/RemoveTotpCommand.java | 11 ++++++--- .../executable/totp/TotpCodeCommand.java | 9 ++++---- .../authme/data/limbo/LimboPlayerState.java | 4 +--- .../fr/xephi/authme/message/MessageKey.java | 23 ++++++++++++++++++- .../security/totp/TotpAuthenticator.java | 7 ++++++ src/main/resources/messages/messages_en.yml | 9 +++++++- .../security/totp/TotpAuthenticatorTest.java | 8 +++++-- .../fr/xephi/authme/message/help_test.yml | 3 --- 10 files changed, 78 insertions(+), 28 deletions(-) diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java index 8fc9aaf10..9238c3b07 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java @@ -4,10 +4,9 @@ import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; import fr.xephi.authme.security.totp.GenerateTotpService; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; -import fr.xephi.authme.service.CommonService; -import org.bukkit.ChatColor; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -25,17 +24,19 @@ public class AddTotpCommand extends PlayerCommand { private DataSource dataSource; @Inject - private CommonService commonService; + private Messages messages; @Override protected void runCommand(Player player, List arguments) { PlayerAuth auth = dataSource.getAuth(player.getName()); - if (auth.getTotpKey() == null) { + if (auth == null) { + messages.send(player, MessageKey.REGISTER_MESSAGE); + } else if (auth.getTotpKey() == null) { TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player); - commonService.send(player, MessageKey.TWO_FACTOR_CREATE, + messages.send(player, MessageKey.TWO_FACTOR_CREATE, createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl()); } else { - player.sendMessage(ChatColor.RED + "Two-factor authentication is already enabled for your account!"); + messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java index 34bdf56d2..52d1a9802 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java @@ -1,7 +1,10 @@ package fr.xephi.authme.command.executable.totp; import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; import fr.xephi.authme.security.totp.GenerateTotpService; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import org.bukkit.entity.Player; @@ -20,21 +23,29 @@ public class ConfirmTotpCommand extends PlayerCommand { @Inject private DataSource dataSource; + @Inject + private Messages messages; + @Override protected void runCommand(Player player, List arguments) { - // TODO #1141: Check if player already has TOTP + PlayerAuth auth = dataSource.getAuth(player.getName()); + if (auth == null) { + messages.send(player, MessageKey.REGISTER_MESSAGE); + } else if (auth.getTotpKey() != null) { + messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); + } final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player); if (totpDetails == null) { - player.sendMessage("No TOTP key has been generated for you or it has expired. Please run /totp add"); + messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE); } else { boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, arguments.get(0)); if (isCodeValid) { generateTotpService.removeGenerateTotpKey(player); dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey()); - player.sendMessage("Successfully enabled two-factor authentication for your account"); + messages.send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS); } else { - player.sendMessage("Wrong code or code has expired. Please use /totp add again"); + messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java index 0a2a371d6..20ef4a1bf 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java @@ -3,6 +3,8 @@ package fr.xephi.authme.command.executable.totp; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; import fr.xephi.authme.security.totp.TotpService; import org.bukkit.entity.Player; @@ -20,17 +22,20 @@ public class RemoveTotpCommand extends PlayerCommand { @Inject private TotpService totpService; + @Inject + private Messages messages; + @Override protected void runCommand(Player player, List arguments) { PlayerAuth auth = dataSource.getAuth(player.getName()); if (auth.getTotpKey() == null) { - player.sendMessage("Two-factor authentication is not enabled for your account!"); + messages.send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR); } else { if (totpService.verifyCode(auth, arguments.get(0))) { dataSource.removeTotpKey(auth.getNickname()); - player.sendMessage("Successfully removed two-factor authentication from your account"); + messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS); } else { - player.sendMessage("Invalid code!"); + messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java index 74d85746f..d8b7a28f9 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java @@ -53,20 +53,19 @@ public class TotpCodeCommand extends PlayerCommand { } LimboPlayer limbo = limboService.getLimboPlayer(player.getName()); - if (limbo.getState() == LimboPlayerState.TOTP_REQUIRED) { - processCode(player, limbo, auth, arguments.get(0)); + if (limbo != null && limbo.getState() == LimboPlayerState.TOTP_REQUIRED) { + processCode(player, auth, arguments.get(0)); } else { messages.send(player, MessageKey.LOGIN_MESSAGE); } } - private void processCode(Player player, LimboPlayer limbo, PlayerAuth auth, String inputCode) { + private void processCode(Player player, PlayerAuth auth, String inputCode) { boolean isCodeValid = totpService.verifyCode(auth, inputCode); if (isCodeValid) { - limbo.setState(LimboPlayerState.FINISHED); asynchronousLogin.performLogin(player, auth); } else { - player.sendMessage("Invalid code!"); + messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE); } } } diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java index 24d6cb0f8..5940ab206 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java @@ -4,8 +4,6 @@ public enum LimboPlayerState { PASSWORD_REQUIRED, - TOTP_REQUIRED, - - FINISHED + TOTP_REQUIRED } diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index 90206ce7c..5d340964b 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -195,11 +195,32 @@ public enum MessageKey { EMAIL_ALREADY_USED_ERROR("email.already_used"), /** Your secret code is %code. You can scan it from here %url */ - TWO_FACTOR_CREATE("misc.two_factor_create", "%code", "%url"), + TWO_FACTOR_CREATE("two_factor.code_created", "%code", "%url"), /** Please submit your two-factor authentication code with /2fa code <code>. */ TWO_FACTOR_CODE_REQUIRED("two_factor.code_required"), + /** Two-factor authentication is already enabled for your account! */ + TWO_FACTOR_ALREADY_ENABLED("two_factor.already_enabled"), + + /** No 2fa key has been generated for you or it has expired. Please run /2fa add */ + TWO_FACTOR_ENABLE_ERROR_NO_CODE("two_factor.enable_error_no_code"), + + /** Successfully enabled two-factor authentication for your account */ + TWO_FACTOR_ENABLE_SUCCESS("two_factor.enable_success"), + + /** Wrong code or code has expired. Please run /2fa add */ + TWO_FACTOR_ENABLE_ERROR_WRONG_CODE("two_factor.enable_error_wrong_code"), + + /** Two-factor authentication is not enabled for your account. Run /2fa add */ + TWO_FACTOR_NOT_ENABLED_ERROR("two_factor.not_enabled_error"), + + /** Successfully removed two-factor auth from your account */ + TWO_FACTOR_REMOVED_SUCCESS("two_factor.removed_success"), + + /** Invalid code! */ + TWO_FACTOR_INVALID_CODE("two_factor.invalid_code"), + /** You are not the owner of this account. Please choose another name! */ NOT_OWNER_ERROR("on_join_validation.not_owner_error"), diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java index ed31da3a7..d546df071 100644 --- a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java +++ b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java @@ -30,6 +30,13 @@ public class TotpAuthenticator { this.bukkitService = bukkitService; } + /** + * Returns whether the given input code matches for the provided TOTP key. + * + * @param totpKey the key to check with + * @param inputCode the input code to verify + * @return true if code is valid, false otherwise + */ public boolean checkCode(String totpKey, String inputCode) { try { Integer totpCode = Integer.valueOf(inputCode); diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index 1b5e09280..aca51fa75 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -56,7 +56,6 @@ unregister: misc: accounts_owned_self: 'You own %count accounts:' accounts_owned_other: 'The player %name has %count accounts:' - two_factor_create: '&2Your secret code is %code. You can scan it from here %url' account_not_activated: '&cYour account isn''t activated yet, please check your emails!' password_changed: '&2Password changed successfully!' logout: '&2Logged out successfully!' @@ -130,7 +129,15 @@ verification: email_needed: '&3To verify your identity you need to link an email address with your account!!' two_factor: + code_created: '&2Your secret code is %code. You can scan it from here %url' code_required: 'Please submit your two-factor authentication code with /2fa code ' + already_enabled: 'Two-factor authentication is already enabled for your account!' + enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + enable_success: 'Successfully enabled two-factor authentication for your account' + enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + removed_success: 'Successfully removed two-factor auth from your account' + invalid_code: 'Invalid code!' # Time units time: diff --git a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java index eb2746fa0..b675ef8a0 100644 --- a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java +++ b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java @@ -4,9 +4,9 @@ import com.warrenstrange.googleauth.IGoogleAuthenticator; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verifyZeroInteractions; @RunWith(MockitoJUnitRunner.class) public class TotpAuthenticatorTest { - @InjectMocks private TotpAuthenticator totpAuthenticator; @Mock @@ -35,6 +34,11 @@ public class TotpAuthenticatorTest { @Mock private IGoogleAuthenticator googleAuthenticator; + @Before + public void initializeTotpAuthenticator() { + totpAuthenticator = new TotpAuthenticator(googleAuthenticator, bukkitService); + } + @Test public void shouldGenerateTotpKey() { // given diff --git a/src/test/resources/fr/xephi/authme/message/help_test.yml b/src/test/resources/fr/xephi/authme/message/help_test.yml index 9647ecd7a..d6970daa7 100644 --- a/src/test/resources/fr/xephi/authme/message/help_test.yml +++ b/src/test/resources/fr/xephi/authme/message/help_test.yml @@ -65,9 +65,6 @@ commands: arg1: label: loginArg nonExistent: does not exist - arg2: - label: 2faArg - rhetorical: false someProp: label: '''someProp'' does not exist' description: idk From 5a58f2c44f918b1378a6de4b5862f496b1a68219 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 24 Mar 2018 12:24:25 +0100 Subject: [PATCH 101/155] #1539 Integrate data source columns library - Create wrapper around SqlColumnsHandler for AuthMe-specific behavior - Integrate columns handler into first SQLite and MySQL method implementations --- pom.xml | 8 + .../authme/datasource/AuthMeColumns.java | 125 ++++++++++++++++ .../authme/datasource/ColumnContext.java | 20 +++ .../fr/xephi/authme/datasource/MySQL.java | 98 +++--------- .../fr/xephi/authme/datasource/SQLite.java | 89 +++-------- .../columnshandler/AuthMeColumnsHandler.java | 141 ++++++++++++++++++ .../xephi/authme/ClassesConsistencyTest.java | 3 +- .../AbstractDataSourceIntegrationTest.java | 17 ++- 8 files changed, 346 insertions(+), 155 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/datasource/AuthMeColumns.java create mode 100644 src/main/java/fr/xephi/authme/datasource/ColumnContext.java create mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java diff --git a/pom.xml b/pom.xml index 0ee1ecf65..f109d7411 100644 --- a/pom.xml +++ b/pom.xml @@ -788,6 +788,14 @@ + + ch.jalu + datasourcecolumns + 0.1-SNAPSHOT + compile + true + + diff --git a/src/main/java/fr/xephi/authme/datasource/AuthMeColumns.java b/src/main/java/fr/xephi/authme/datasource/AuthMeColumns.java new file mode 100644 index 000000000..657084aea --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/AuthMeColumns.java @@ -0,0 +1,125 @@ +package fr.xephi.authme.datasource; + +import ch.jalu.configme.properties.Property; +import ch.jalu.datasourcecolumns.ColumnType; +import ch.jalu.datasourcecolumns.DependentColumn; +import ch.jalu.datasourcecolumns.StandardTypes; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.settings.properties.DatabaseSettings; + +import java.util.function.Function; + +public final class AuthMeColumns implements DependentColumn { + + public static final AuthMeColumns NAME = createString( + DatabaseSettings.MYSQL_COL_NAME, PlayerAuth::getNickname); + + public static final AuthMeColumns NICK_NAME = createString( + DatabaseSettings.MYSQL_COL_REALNAME, PlayerAuth::getRealName); + + public static final AuthMeColumns PASSWORD = createString( + DatabaseSettings.MYSQL_COL_PASSWORD, auth -> auth.getPassword().getHash()); + + public static final AuthMeColumns SALT = new AuthMeColumns<>( + StandardTypes.STRING, DatabaseSettings.MYSQL_COL_SALT, auth -> auth.getPassword().getSalt(), true); + + public static final AuthMeColumns EMAIL = createString( + DatabaseSettings.MYSQL_COL_EMAIL, PlayerAuth::getEmail); + + public static final AuthMeColumns LAST_IP = createString( + DatabaseSettings.MYSQL_COL_LAST_IP, PlayerAuth::getLastIp); + + public static final AuthMeColumns LOCATION_X = createDouble( + DatabaseSettings.MYSQL_COL_LASTLOC_X, PlayerAuth::getQuitLocX); + + public static final AuthMeColumns LOCATION_Y = createDouble( + DatabaseSettings.MYSQL_COL_LASTLOC_Y, PlayerAuth::getQuitLocY); + + public static final AuthMeColumns LOCATION_Z = createDouble( + DatabaseSettings.MYSQL_COL_LASTLOC_Z, PlayerAuth::getQuitLocZ); + + public static final AuthMeColumns LOCATION_WORLD = createString( + DatabaseSettings.MYSQL_COL_LASTLOC_WORLD, PlayerAuth::getWorld); + + public static final AuthMeColumns LOCATION_YAW = createFloat( + DatabaseSettings.MYSQL_COL_LASTLOC_YAW, PlayerAuth::getYaw); + + public static final AuthMeColumns LOCATION_PITCH = createFloat( + DatabaseSettings.MYSQL_COL_LASTLOC_PITCH, PlayerAuth::getPitch); + + + private final ColumnType columnType; + private final Property nameProperty; + private final Function playerAuthGetter; + private final boolean isOptional; + + private AuthMeColumns(ColumnType type, Property nameProperty, Function playerAuthGetter, + boolean isOptional) { + this.columnType = type; + this.nameProperty = nameProperty; + this.playerAuthGetter = playerAuthGetter; + this.isOptional = isOptional; + } + + private static AuthMeColumns createString(Property nameProperty, + Function getter) { + return new AuthMeColumns<>(StandardTypes.STRING, nameProperty, getter, false); + } + + private static AuthMeColumns createDouble(Property nameProperty, + Function getter) { + return new AuthMeColumns<>(new DoubleType(), nameProperty, getter, false); + } + + private static AuthMeColumns createFloat(Property nameProperty, + Function getter) { + return new AuthMeColumns<>(new FloatType(), nameProperty, getter, false); + } + + + public Property getNameProperty() { + return nameProperty; + } + + @Override + public T getValueFromDependent(PlayerAuth playerAuth) { + return playerAuthGetter.apply(playerAuth); + } + + @Override + public String resolveName(ColumnContext columnContext) { + return columnContext.getName(this); + } + + @Override + public ColumnType getType() { + return columnType; + } + + @Override + public boolean isColumnUsed(ColumnContext columnContext) { + return !isOptional || !resolveName(columnContext).isEmpty(); + } + + @Override + public boolean useDefaultForNullValue(ColumnContext columnContext) { + return false; + } + + // TODO: Move this to the project... + private static final class DoubleType implements ColumnType { + + @Override + public Class getClazz() { + return Double.class; + } + } + + private static final class FloatType implements ColumnType { + + @Override + public Class getClazz() { + return Float.class; + } + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/ColumnContext.java b/src/main/java/fr/xephi/authme/datasource/ColumnContext.java new file mode 100644 index 000000000..cbc743fad --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/ColumnContext.java @@ -0,0 +1,20 @@ +package fr.xephi.authme.datasource; + +import fr.xephi.authme.settings.Settings; + +import java.util.HashMap; +import java.util.Map; + +public class ColumnContext { + + private final Settings settings; + private final Map, String> columnNames = new HashMap<>(); + + public ColumnContext(Settings settings) { + this.settings = settings; + } + + public String getName(AuthMeColumns column) { + return columnNames.computeIfAbsent(column, k -> settings.getProperty(k.getNameProperty())); + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index ce46baadd..f098d4683 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -1,10 +1,12 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValues; import com.google.common.annotations.VisibleForTesting; import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory; import fr.xephi.authme.security.crypts.HashedPassword; @@ -25,6 +27,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static ch.jalu.datasourcecolumns.data.UpdateValues.with; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; @@ -45,6 +48,7 @@ public class MySQL implements DataSource { private int maxLifetime; private List columnOthers; private Columns col; + private AuthMeColumnsHandler columnsHandler; private MySqlExtension sqlExtension; private HikariDataSource ds; @@ -99,6 +103,8 @@ public class MySQL implements DataSource { this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS); this.col = new Columns(settings); + this.columnsHandler = + AuthMeColumnsHandler.createForMySql(sql -> getConnection().prepareStatement(sql), settings); this.sqlExtension = extensionsFactory.buildExtension(col); this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE); this.maxLifetime = settings.getProperty(DatabaseSettings.MYSQL_CONNECTION_MAX_LIFETIME); @@ -278,20 +284,13 @@ public class MySQL implements DataSource { @Override public HashedPassword getPassword(String user) { - boolean useSalt = !col.SALT.isEmpty(); - String sql = "SELECT " + col.PASSWORD - + (useSalt ? ", " + col.SALT : "") - + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user.toLowerCase()); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return new HashedPassword(rs.getString(col.PASSWORD), - useSalt ? rs.getString(col.SALT) : null); - } + try { + DataSourceValues passwordResult = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); + if (passwordResult.rowExists()) { + return new HashedPassword(passwordResult.get(AuthMeColumns.PASSWORD), passwordResult.get(AuthMeColumns.SALT)); } - } catch (SQLException ex) { - logSqlException(ex); + } catch (SQLException e) { + logSqlException(e); } return null; } @@ -371,33 +370,9 @@ public class MySQL implements DataSource { @Override public boolean updatePassword(String user, HashedPassword password) { - user = user.toLowerCase(); - try (Connection con = getConnection()) { - boolean useSalt = !col.SALT.isEmpty(); - if (useSalt) { - String sql = String.format("UPDATE %s SET %s = ?, %s = ? WHERE %s = ?;", - tableName, col.PASSWORD, col.SALT, col.NAME); - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, password.getHash()); - pst.setString(2, password.getSalt()); - pst.setString(3, user); - pst.executeUpdate(); - } - } else { - String sql = String.format("UPDATE %s SET %s = ? WHERE %s = ?;", - tableName, col.PASSWORD, col.NAME); - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, password.getHash()); - pst.setString(2, user); - pst.executeUpdate(); - } - } - sqlExtension.changePassword(user, password, con); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(user, + with(AuthMeColumns.PASSWORD, password.getHash()) + .and(AuthMeColumns.SALT, password.getSalt()).build()); } @Override @@ -456,38 +431,14 @@ public class MySQL implements DataSource { @Override public boolean updateQuitLoc(PlayerAuth auth) { - String sql = "UPDATE " + tableName - + " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " - + col.LASTLOC_WORLD + "=?, " + col.LASTLOC_YAW + "=?, " + col.LASTLOC_PITCH + "=?" - + " WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setDouble(1, auth.getQuitLocX()); - pst.setDouble(2, auth.getQuitLocY()); - pst.setDouble(3, auth.getQuitLocZ()); - pst.setString(4, auth.getWorld()); - pst.setFloat(5, auth.getYaw()); - pst.setFloat(6, auth.getPitch()); - pst.setString(7, auth.getNickname()); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(auth, + AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, + AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); } @Override public boolean updateEmail(PlayerAuth auth) { - String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, auth.getEmail()); - pst.setString(2, auth.getNickname()); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(auth, AuthMeColumns.EMAIL); } @Override @@ -654,16 +605,7 @@ public class MySQL implements DataSource { @Override public boolean updateRealName(String user, String realName) { - String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, realName); - pst.setString(2, user); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index ada94afbc..57d12a6b4 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -1,8 +1,10 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValues; import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -22,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static ch.jalu.datasourcecolumns.data.UpdateValues.with; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; @@ -37,6 +40,7 @@ public class SQLite implements DataSource { private final String tableName; private final Columns col; private Connection con; + private AuthMeColumnsHandler columnsHandler; /** * Constructor for SQLite. @@ -71,6 +75,7 @@ public class SQLite implements DataSource { this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); this.col = new Columns(settings); this.con = connection; + this.columnsHandler = AuthMeColumnsHandler.createForSqlite(con, settings); } /** @@ -220,20 +225,13 @@ public class SQLite implements DataSource { @Override public HashedPassword getPassword(String user) { - boolean useSalt = !col.SALT.isEmpty(); - String sql = "SELECT " + col.PASSWORD - + (useSalt ? ", " + col.SALT : "") - + " FROM " + tableName + " WHERE " + col.NAME + "=?"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return new HashedPassword(rs.getString(col.PASSWORD), - useSalt ? rs.getString(col.SALT) : null); - } + try { + DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); + if (values.rowExists()) { + return new HashedPassword(values.get(AuthMeColumns.PASSWORD), values.get(AuthMeColumns.SALT)); } - } catch (SQLException ex) { - logSqlException(ex); + } catch (SQLException e) { + logSqlException(e); } return null; } @@ -305,25 +303,9 @@ public class SQLite implements DataSource { @Override public boolean updatePassword(String user, HashedPassword password) { - user = user.toLowerCase(); - boolean useSalt = !col.SALT.isEmpty(); - String sql = "UPDATE " + tableName + " SET " + col.PASSWORD + " = ?" - + (useSalt ? ", " + col.SALT + " = ?" : "") - + " WHERE " + col.NAME + " = ?"; - try (PreparedStatement pst = con.prepareStatement(sql)){ - pst.setString(1, password.getHash()); - if (useSalt) { - pst.setString(2, password.getSalt()); - pst.setString(3, user); - } else { - pst.setString(2, user); - } - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(user, + with(AuthMeColumns.PASSWORD, password.getHash()) + .and(AuthMeColumns.SALT, password.getSalt()).build()); } @Override @@ -392,38 +374,14 @@ public class SQLite implements DataSource { @Override public boolean updateQuitLoc(PlayerAuth auth) { - String sql = "UPDATE " + tableName + " SET " - + col.LASTLOC_X + "=?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " - + col.LASTLOC_WORLD + "=?, " + col.LASTLOC_YAW + "=?, " + col.LASTLOC_PITCH + "=? " - + "WHERE " + col.NAME + "=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setDouble(1, auth.getQuitLocX()); - pst.setDouble(2, auth.getQuitLocY()); - pst.setDouble(3, auth.getQuitLocZ()); - pst.setString(4, auth.getWorld()); - pst.setFloat(5, auth.getYaw()); - pst.setFloat(6, auth.getPitch()); - pst.setString(7, auth.getNickname()); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(auth, + AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, + AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); } @Override public boolean updateEmail(PlayerAuth auth) { - String sql = "UPDATE " + tableName + " SET " + col.EMAIL + "=? WHERE " + col.NAME + "=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, auth.getEmail()); - pst.setString(2, auth.getNickname()); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(auth, AuthMeColumns.EMAIL); } @Override @@ -583,16 +541,7 @@ public class SQLite implements DataSource { @Override public boolean updateRealName(String user, String realName) { - String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, realName); - pst.setString(2, user); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java new file mode 100644 index 000000000..b4c02dc93 --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java @@ -0,0 +1,141 @@ +package fr.xephi.authme.datasource.columnshandler; + +import ch.jalu.datasourcecolumns.data.DataSourceValue; +import ch.jalu.datasourcecolumns.data.DataSourceValues; +import ch.jalu.datasourcecolumns.data.UpdateValues; +import ch.jalu.datasourcecolumns.sqlimplementation.PredicateSqlGenerator; +import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementGenerator; +import ch.jalu.datasourcecolumns.sqlimplementation.ResultSetValueRetriever; +import ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandler; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.AuthMeColumns; +import fr.xephi.authme.datasource.ColumnContext; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.DatabaseSettings; + +import java.sql.Connection; +import java.sql.SQLException; + +import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; + +/** + * Wrapper of {@link SqlColumnsHandler} for the AuthMe data table. + * Wraps exceptions and provides better support for operations based on a {@link PlayerAuth} object. + */ +public final class AuthMeColumnsHandler { + + private final SqlColumnsHandler internalHandler; + + private AuthMeColumnsHandler(SqlColumnsHandler internalHandler) { + this.internalHandler = internalHandler; + } + + /** + * Creates a column handler for SQLite. + * + * @param connection the connection to the database + * @param settings plugin settings + * @return created column handler + */ + public static AuthMeColumnsHandler createForSqlite(Connection connection, Settings settings) { + ColumnContext columnContext = new ColumnContext(settings); + String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); + String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); + + SqlColumnsHandler sqlColHandler = + new SqlColumnsHandler<>(connection, columnContext, tableName, nameColumn); + return new AuthMeColumnsHandler(sqlColHandler); + } + + /** + * Creates a column handler for MySQL. + * + * @param preparedStatementGenerator supplier of SQL prepared statements with a connection to the database + * @param settings plugin settings + * @return created column handler + */ + public static AuthMeColumnsHandler createForMySql(PreparedStatementGenerator preparedStatementGenerator, + Settings settings) { + ColumnContext columnContext = new ColumnContext(settings); + String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); + String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); + + SqlColumnsHandler sqlColHandler = new SqlColumnsHandler<>(preparedStatementGenerator, + columnContext, tableName, nameColumn, new ResultSetValueRetriever<>(columnContext), + new PredicateSqlGenerator<>(columnContext)); + return new AuthMeColumnsHandler(sqlColHandler); + } + + /** + * Changes a column from a specific row to the given value. + * + * @param name name of the account to modify + * @param column the column to modify + * @param value the value to set the column to + * @param the column type + * @return true upon success, false otherwise + */ + public boolean update(String name, AuthMeColumns column, T value) { + try { + return internalHandler.update(name, column, value); + } catch (SQLException e) { + logSqlException(e); + return false; + } + } + + /** + * Updates a row to have the values as retrieved from the PlayerAuth object. + * + * @param auth the player auth object to modify and to get values from + * @param columns the columns to update in the row + * @return true upon success, false otherwise + */ + public boolean update(PlayerAuth auth, AuthMeColumns... columns) { + try { + return internalHandler.update(auth.getNickname(), auth, columns); + } catch (SQLException e) { + logSqlException(e); + return false; + } + } + + /** + * Updates a row to have the given values. + * + * @param name the name of the account to modify + * @param updateValues the values to set on the row + * @return true upon success, false otherwise + */ + public boolean update(String name, UpdateValues updateValues) { + try { + return internalHandler.update(name.toLowerCase(), updateValues); + } catch (SQLException e) { + logSqlException(e); + return false; + } + } + + /** + * Retrieves the given column from a given row. + * + * @param name the account name to look up + * @param column the column whose value should be retrieved + * @param the column type + * @return the result of the lookup + */ + public DataSourceValue retrieve(String name, AuthMeColumns column) throws SQLException { + return internalHandler.retrieve(name.toLowerCase(), column); + } + + /** + * Retrieves multiple values from a given row. + * + * @param name the account name to look up + * @param columns the columns to retrieve + * @return map-like object with the requested values + */ + public DataSourceValues retrieve(String name, AuthMeColumns... columns) throws SQLException { + return internalHandler.retrieve(name.toLowerCase(), columns); + } +} diff --git a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java index 267f7d7cb..4005dc798 100644 --- a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import fr.xephi.authme.data.captcha.CaptchaCodeStorage; +import fr.xephi.authme.datasource.AuthMeColumns; import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension; import fr.xephi.authme.initialization.HasCleanup; @@ -52,7 +53,7 @@ public class ClassesConsistencyTest { int.class, long.class, float.class, String.class, File.class, Enum.class, collectionsUnmodifiableList(), Charset.class, /* AuthMe */ - Property.class, RegistrationMethod.class, + Property.class, RegistrationMethod.class, AuthMeColumns.class, /* Guava */ ImmutableMap.class, ImmutableList.class); diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index 530ab56be..70df2d48b 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -61,7 +61,7 @@ public abstract class AbstractDataSourceIntegrationTest { // when HashedPassword bobbyPassword = dataSource.getPassword("bobby"); HashedPassword invalidPassword = dataSource.getPassword("doesNotExist"); - HashedPassword userPassword = dataSource.getPassword("user"); + HashedPassword userPassword = dataSource.getPassword("User"); // then assertThat(bobbyPassword, equalToHash("$SHA$11aa0706173d7272$dbba966")); @@ -160,7 +160,8 @@ public abstract class AbstractDataSourceIntegrationTest { boolean response2 = dataSource.updatePassword("non-existent-name", new HashedPassword("sd")); // then - assertThat(response1 && response2, equalTo(true)); + assertThat(response1, equalTo(true)); + assertThat(response2, equalTo(false)); // no record modified assertThat(dataSource.getPassword("user"), equalToHash(newHash)); } @@ -175,7 +176,8 @@ public abstract class AbstractDataSourceIntegrationTest { boolean response2 = dataSource.updatePassword("non-existent-name", new HashedPassword("asdfasdf", "a1f34ec")); // then - assertThat(response1 && response2, equalTo(true)); + assertThat(response1, equalTo(true)); + assertThat(response2, equalTo(false)); // no record modified assertThat(dataSource.getPassword("user"), equalToHash("new_hash")); } @@ -191,7 +193,8 @@ public abstract class AbstractDataSourceIntegrationTest { boolean response2 = dataSource.updatePassword(invalidAuth); // then - assertThat(response1 && response2, equalTo(true)); + assertThat(response1, equalTo(true)); + assertThat(response2, equalTo(false)); // no record modified assertThat(dataSource.getPassword("bobby"), equalToHash("tt", "cc")); } @@ -273,7 +276,8 @@ public abstract class AbstractDataSourceIntegrationTest { boolean response2 = dataSource.updateEmail(invalidAuth); // then - assertThat(response1 && response2, equalTo(true)); + assertThat(response1, equalTo(true)); + assertThat(response2, equalTo(false)); // no record modified assertThat(dataSource.getAllAuths(), hasItem(hasAuthBasicData("user", "user", email, "34.56.78.90"))); } @@ -328,7 +332,8 @@ public abstract class AbstractDataSourceIntegrationTest { boolean response2 = dataSource.updateRealName("notExists", "NOTEXISTS"); // then - assertThat(response1 && response2, equalTo(true)); + assertThat(response1, equalTo(true)); + assertThat(response2, equalTo(false)); // no record modified assertThat(dataSource.getAuth("bobby"), hasAuthBasicData("bobby", "BOBBY", null, "123.45.67.89")); } From 837bfb7935e069e53f9ba8a3dffc4e09c8b724e1 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sat, 24 Mar 2018 15:47:40 +0100 Subject: [PATCH 102/155] Update dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0ee1ecf65..b6b1d4ec2 100644 --- a/pom.xml +++ b/pom.xml @@ -395,7 +395,7 @@ com.google.guava guava - 24.0-jre + 24.1-jre true @@ -808,7 +808,7 @@ org.mockito mockito-core test - 2.15.0 + 2.16.0 hamcrest-core From 881ef6a640c86f27ac63da65ffc897124ef2f7ce Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 24 Mar 2018 21:16:43 +0100 Subject: [PATCH 103/155] #1539 DataSource columns: close MySQL connections, add missing columns, use newly built-in types, improve column initialization --- .../authme/datasource/ColumnContext.java | 20 ----- .../fr/xephi/authme/datasource/MySQL.java | 35 ++------- .../fr/xephi/authme/datasource/SQLite.java | 45 +---------- .../{ => columnshandler}/AuthMeColumns.java | 72 ++++++++---------- .../columnshandler/AuthMeColumnsFactory.java | 76 +++++++++++++++++++ .../columnshandler/AuthMeColumnsHandler.java | 34 ++++++--- .../columnshandler/ColumnContext.java | 35 +++++++++ .../columnshandler/ConnectionSupplier.java | 17 +++++ .../MySqlPreparedStatementGenerator.java | 44 +++++++++++ .../xephi/authme/ClassesConsistencyTest.java | 2 +- 10 files changed, 238 insertions(+), 142 deletions(-) delete mode 100644 src/main/java/fr/xephi/authme/datasource/ColumnContext.java rename src/main/java/fr/xephi/authme/datasource/{ => columnshandler}/AuthMeColumns.java (61%) create mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java create mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java create mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java create mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java diff --git a/src/main/java/fr/xephi/authme/datasource/ColumnContext.java b/src/main/java/fr/xephi/authme/datasource/ColumnContext.java deleted file mode 100644 index cbc743fad..000000000 --- a/src/main/java/fr/xephi/authme/datasource/ColumnContext.java +++ /dev/null @@ -1,20 +0,0 @@ -package fr.xephi.authme.datasource; - -import fr.xephi.authme.settings.Settings; - -import java.util.HashMap; -import java.util.Map; - -public class ColumnContext { - - private final Settings settings; - private final Map, String> columnNames = new HashMap<>(); - - public ColumnContext(Settings settings) { - this.settings = settings; - } - - public String getName(AuthMeColumns column) { - return columnNames.computeIfAbsent(column, k -> settings.getProperty(k.getNameProperty())); - } -} diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index f098d4683..8e501ae0c 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -6,6 +6,7 @@ import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory; @@ -13,7 +14,6 @@ import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.HooksSettings; -import fr.xephi.authme.util.StringUtils; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -103,8 +103,7 @@ public class MySQL implements DataSource { this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS); this.col = new Columns(settings); - this.columnsHandler = - AuthMeColumnsHandler.createForMySql(sql -> getConnection().prepareStatement(sql), settings); + this.columnsHandler = AuthMeColumnsHandler.createForMySql(this::getConnection, settings); this.sqlExtension = extensionsFactory.buildExtension(col); this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE); this.maxLifetime = settings.getProperty(DatabaseSettings.MYSQL_CONNECTION_MAX_LIFETIME); @@ -317,33 +316,11 @@ public class MySQL implements DataSource { @Override public boolean saveAuth(PlayerAuth auth) { + columnsHandler.insert(auth, + AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, + AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP); + try (Connection con = getConnection()) { - // TODO ljacqu 20171104: Replace with generic columns util to clean this up - boolean useSalt = !col.SALT.isEmpty() || !StringUtils.isEmpty(auth.getPassword().getSalt()); - boolean hasEmail = auth.getEmail() != null; - String emailPlaceholder = hasEmail ? "?" : "DEFAULT"; - - String sql = "INSERT INTO " + tableName + "(" - + col.NAME + "," + col.PASSWORD + "," + col.REAL_NAME - + "," + col.EMAIL + "," + col.REGISTRATION_DATE + "," + col.REGISTRATION_IP - + (useSalt ? "," + col.SALT : "") - + ") VALUES (?,?,?," + emailPlaceholder + ",?,?" + (useSalt ? ",?" : "") + ");"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - int index = 1; - pst.setString(index++, auth.getNickname()); - pst.setString(index++, auth.getPassword().getHash()); - pst.setString(index++, auth.getRealName()); - if (hasEmail) { - pst.setString(index++, auth.getEmail()); - } - pst.setObject(index++, auth.getRegistrationDate()); - pst.setString(index++, auth.getRegistrationIp()); - if (useSalt) { - pst.setString(index++, auth.getPassword().getSalt()); - } - pst.executeUpdate(); - } - if (!columnOthers.isEmpty()) { for (String column : columnOthers) { try (PreparedStatement pst = con.prepareStatement( diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 57d12a6b4..0b2361aa8 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -4,11 +4,11 @@ import ch.jalu.datasourcecolumns.data.DataSourceValues; import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; -import fr.xephi.authme.util.StringUtils; import java.io.File; import java.sql.Connection; @@ -254,46 +254,9 @@ public class SQLite implements DataSource { @Override public boolean saveAuth(PlayerAuth auth) { - PreparedStatement pst = null; - try { - HashedPassword password = auth.getPassword(); - if (col.SALT.isEmpty()) { - if (!StringUtils.isEmpty(auth.getPassword().getSalt())) { - ConsoleLogger.warning("Warning! Detected hashed password with separate salt but the salt column " - + "is not set in the config!"); - } - - pst = con.prepareStatement("INSERT INTO " + tableName + "(" + col.NAME + "," + col.PASSWORD - + "," + col.REAL_NAME + "," + col.EMAIL - + "," + col.REGISTRATION_DATE + "," + col.REGISTRATION_IP - + ") VALUES (?,?,?,?,?,?);"); - pst.setString(1, auth.getNickname()); - pst.setString(2, password.getHash()); - pst.setString(3, auth.getRealName()); - pst.setString(4, auth.getEmail()); - pst.setLong(5, auth.getRegistrationDate()); - pst.setString(6, auth.getRegistrationIp()); - pst.executeUpdate(); - } else { - pst = con.prepareStatement("INSERT INTO " + tableName + "(" + col.NAME + "," + col.PASSWORD - + "," + col.REAL_NAME + "," + col.EMAIL - + "," + col.REGISTRATION_DATE + "," + col.REGISTRATION_IP + "," + col.SALT - + ") VALUES (?,?,?,?,?,?,?);"); - pst.setString(1, auth.getNickname()); - pst.setString(2, password.getHash()); - pst.setString(3, auth.getRealName()); - pst.setString(4, auth.getEmail()); - pst.setLong(5, auth.getRegistrationDate()); - pst.setString(6, auth.getRegistrationIp()); - pst.setString(7, password.getSalt()); - pst.executeUpdate(); - } - } catch (SQLException ex) { - logSqlException(ex); - } finally { - close(pst); - } - return true; + return columnsHandler.insert(auth, + AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, + AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/AuthMeColumns.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java similarity index 61% rename from src/main/java/fr/xephi/authme/datasource/AuthMeColumns.java rename to src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java index 657084aea..246bf5581 100644 --- a/src/main/java/fr/xephi/authme/datasource/AuthMeColumns.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java @@ -1,14 +1,27 @@ -package fr.xephi.authme.datasource; +package fr.xephi.authme.datasource.columnshandler; import ch.jalu.configme.properties.Property; import ch.jalu.datasourcecolumns.ColumnType; import ch.jalu.datasourcecolumns.DependentColumn; -import ch.jalu.datasourcecolumns.StandardTypes; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.settings.properties.DatabaseSettings; import java.util.function.Function; +import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.ColumnOptions.DEFAULT_FOR_NULL; +import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.ColumnOptions.OPTIONAL; +import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createDouble; +import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createFloat; +import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createInteger; +import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createLong; +import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createString; + +/** + * Column definitions for the AuthMe table. + * + * @param the column type + * @see PlayerAuth + */ public final class AuthMeColumns implements DependentColumn { public static final AuthMeColumns NAME = createString( @@ -20,15 +33,24 @@ public final class AuthMeColumns implements DependentColumn PASSWORD = createString( DatabaseSettings.MYSQL_COL_PASSWORD, auth -> auth.getPassword().getHash()); - public static final AuthMeColumns SALT = new AuthMeColumns<>( - StandardTypes.STRING, DatabaseSettings.MYSQL_COL_SALT, auth -> auth.getPassword().getSalt(), true); + public static final AuthMeColumns SALT = createString( + DatabaseSettings.MYSQL_COL_SALT, auth -> auth.getPassword().getSalt(), OPTIONAL); public static final AuthMeColumns EMAIL = createString( - DatabaseSettings.MYSQL_COL_EMAIL, PlayerAuth::getEmail); + DatabaseSettings.MYSQL_COL_EMAIL, PlayerAuth::getEmail, DEFAULT_FOR_NULL); public static final AuthMeColumns LAST_IP = createString( DatabaseSettings.MYSQL_COL_LAST_IP, PlayerAuth::getLastIp); + public static final AuthMeColumns GROUP_ID = createInteger( + DatabaseSettings.MYSQL_COL_GROUP, PlayerAuth::getGroupId, OPTIONAL); + + public static final AuthMeColumns REGISTRATION_IP = createString( + DatabaseSettings.MYSQL_COL_REGISTER_IP, PlayerAuth::getRegistrationIp); + + public static final AuthMeColumns REGISTRATION_DATE = createLong( + DatabaseSettings.MYSQL_COL_REGISTER_DATE, PlayerAuth::getRegistrationDate); + public static final AuthMeColumns LOCATION_X = createDouble( DatabaseSettings.MYSQL_COL_LASTLOC_X, PlayerAuth::getQuitLocX); @@ -52,28 +74,15 @@ public final class AuthMeColumns implements DependentColumn nameProperty; private final Function playerAuthGetter; private final boolean isOptional; + private final boolean useDefaultForNull; - private AuthMeColumns(ColumnType type, Property nameProperty, Function playerAuthGetter, - boolean isOptional) { + AuthMeColumns(ColumnType type, Property nameProperty, Function playerAuthGetter, + boolean isOptional, boolean useDefaultForNull) { this.columnType = type; this.nameProperty = nameProperty; this.playerAuthGetter = playerAuthGetter; this.isOptional = isOptional; - } - - private static AuthMeColumns createString(Property nameProperty, - Function getter) { - return new AuthMeColumns<>(StandardTypes.STRING, nameProperty, getter, false); - } - - private static AuthMeColumns createDouble(Property nameProperty, - Function getter) { - return new AuthMeColumns<>(new DoubleType(), nameProperty, getter, false); - } - - private static AuthMeColumns createFloat(Property nameProperty, - Function getter) { - return new AuthMeColumns<>(new FloatType(), nameProperty, getter, false); + this.useDefaultForNull = useDefaultForNull; } @@ -103,23 +112,6 @@ public final class AuthMeColumns implements DependentColumn { - - @Override - public Class getClazz() { - return Double.class; - } - } - - private static final class FloatType implements ColumnType { - - @Override - public Class getClazz() { - return Float.class; - } + return useDefaultForNull && columnContext.hasDefaultSupport(); } } diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java new file mode 100644 index 000000000..420a26bc0 --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java @@ -0,0 +1,76 @@ +package fr.xephi.authme.datasource.columnshandler; + +import ch.jalu.configme.properties.Property; +import ch.jalu.datasourcecolumns.ColumnType; +import ch.jalu.datasourcecolumns.StandardTypes; +import fr.xephi.authme.data.auth.PlayerAuth; + +import java.util.function.Function; + +/** + * Util class for initializing {@link AuthMeColumns} constants. + */ +final class AuthMeColumnsFactory { + + private AuthMeColumnsFactory() { + } + + static AuthMeColumns createInteger(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { + return createInternal(StandardTypes.INTEGER, nameProperty, playerAuthGetter, options); + } + + static AuthMeColumns createLong(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { + return createInternal(StandardTypes.LONG, nameProperty, playerAuthGetter, options); + } + + static AuthMeColumns createString(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { + return createInternal(StandardTypes.STRING, nameProperty, playerAuthGetter, options); + } + + static AuthMeColumns createDouble(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { + return createInternal(StandardTypes.DOUBLE, nameProperty, playerAuthGetter, options); + } + + static AuthMeColumns createFloat(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { + return createInternal(StandardTypes.FLOAT, nameProperty, playerAuthGetter, options); + } + + private static AuthMeColumns createInternal(ColumnType type, Property nameProperty, + Function authGetter, ColumnOptions... options) { + return new AuthMeColumns<>(type, nameProperty, authGetter, isOptional(options), hasDefaultForNull(options)); + } + + private static boolean isOptional(ColumnOptions[] options) { + return containsInArray(ColumnOptions.OPTIONAL, options); + } + + private static boolean hasDefaultForNull(ColumnOptions[] options) { + return containsInArray(ColumnOptions.DEFAULT_FOR_NULL, options); + } + + private static boolean containsInArray(ColumnOptions needle, ColumnOptions[] haystack) { + for (ColumnOptions option : haystack) { + if (option == needle) { + return true; + } + } + return false; + } + + enum ColumnOptions { + + OPTIONAL, + + DEFAULT_FOR_NULL + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java index b4c02dc93..a35c8c97c 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java @@ -4,12 +4,9 @@ import ch.jalu.datasourcecolumns.data.DataSourceValue; import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.data.UpdateValues; import ch.jalu.datasourcecolumns.sqlimplementation.PredicateSqlGenerator; -import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementGenerator; import ch.jalu.datasourcecolumns.sqlimplementation.ResultSetValueRetriever; import ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandler; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.AuthMeColumns; -import fr.xephi.authme.datasource.ColumnContext; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -38,7 +35,7 @@ public final class AuthMeColumnsHandler { * @return created column handler */ public static AuthMeColumnsHandler createForSqlite(Connection connection, Settings settings) { - ColumnContext columnContext = new ColumnContext(settings); + ColumnContext columnContext = new ColumnContext(settings, false); String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); @@ -50,19 +47,18 @@ public final class AuthMeColumnsHandler { /** * Creates a column handler for MySQL. * - * @param preparedStatementGenerator supplier of SQL prepared statements with a connection to the database + * @param connectionSupplier supplier of connections from the connection pool * @param settings plugin settings * @return created column handler */ - public static AuthMeColumnsHandler createForMySql(PreparedStatementGenerator preparedStatementGenerator, - Settings settings) { - ColumnContext columnContext = new ColumnContext(settings); + public static AuthMeColumnsHandler createForMySql(ConnectionSupplier connectionSupplier, Settings settings) { + ColumnContext columnContext = new ColumnContext(settings, true); String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); - SqlColumnsHandler sqlColHandler = new SqlColumnsHandler<>(preparedStatementGenerator, - columnContext, tableName, nameColumn, new ResultSetValueRetriever<>(columnContext), - new PredicateSqlGenerator<>(columnContext)); + SqlColumnsHandler sqlColHandler = new SqlColumnsHandler<>( + new MySqlPreparedStatementGenerator(connectionSupplier), columnContext, tableName, nameColumn, + new ResultSetValueRetriever<>(columnContext), new PredicateSqlGenerator<>(columnContext)); return new AuthMeColumnsHandler(sqlColHandler); } @@ -138,4 +134,20 @@ public final class AuthMeColumnsHandler { public DataSourceValues retrieve(String name, AuthMeColumns... columns) throws SQLException { return internalHandler.retrieve(name.toLowerCase(), columns); } + + /** + * Inserts the given values into a new row, as taken from the player auth. + * + * @param auth the player auth to get values from + * @param columns the columns to insert + * @return true upon success, false otherwise + */ + public boolean insert(PlayerAuth auth, AuthMeColumns... columns) { + try { + return internalHandler.insert(auth, columns); + } catch (SQLException e) { + logSqlException(e); + return false; + } + } } diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java new file mode 100644 index 000000000..8759935db --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.datasource.columnshandler; + +import fr.xephi.authme.settings.Settings; + +import java.util.HashMap; +import java.util.Map; + +/** + * Context for resolving the properties of {@link AuthMeColumns} entries. + */ +public class ColumnContext { + + private final Settings settings; + private final Map, String> columnNames = new HashMap<>(); + private final boolean hasDefaultSupport; + + /** + * Constructor. + * + * @param settings plugin settings + * @param hasDefaultSupport whether or not the underlying database has support for the {@code DEFAULT} keyword + */ + public ColumnContext(Settings settings, boolean hasDefaultSupport) { + this.settings = settings; + this.hasDefaultSupport = hasDefaultSupport; + } + + public String getName(AuthMeColumns column) { + return columnNames.computeIfAbsent(column, k -> settings.getProperty(k.getNameProperty())); + } + + public boolean hasDefaultSupport() { + return hasDefaultSupport; + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java new file mode 100644 index 000000000..4d419a219 --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java @@ -0,0 +1,17 @@ +package fr.xephi.authme.datasource.columnshandler; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Supplier of connections to a database. + */ +@FunctionalInterface +public interface ConnectionSupplier { + + /** + * @return connection object to the database + */ + Connection get() throws SQLException; + +} diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java new file mode 100644 index 000000000..c20357ae4 --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java @@ -0,0 +1,44 @@ +package fr.xephi.authme.datasource.columnshandler; + +import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementGenerator; +import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementResult; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * Implementation of {@link PreparedStatementGenerator} for MySQL which ensures that the connection + * taken from the connection pool is also closed after the prepared statement has been executed. + */ +class MySqlPreparedStatementGenerator implements PreparedStatementGenerator { + + private final ConnectionSupplier connectionSupplier; + + MySqlPreparedStatementGenerator(ConnectionSupplier connectionSupplier) { + this.connectionSupplier = connectionSupplier; + } + + @Override + public PreparedStatementResult create(String sql) throws SQLException { + Connection connection = connectionSupplier.get(); + return new MySqlPreparedStatementResult(connection, connection.prepareStatement(sql)); + } + + /** Prepared statement result which also closes the associated connection. */ + private static final class MySqlPreparedStatementResult extends PreparedStatementResult { + + private final Connection connection; + + MySqlPreparedStatementResult(Connection connection, PreparedStatement preparedStatement) { + super(preparedStatement); + this.connection = connection; + } + + @Override + public void close() throws SQLException { + super.close(); + connection.close(); + } + } +} diff --git a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java index 4005dc798..c7f093982 100644 --- a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java @@ -5,8 +5,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import fr.xephi.authme.data.captcha.CaptchaCodeStorage; -import fr.xephi.authme.datasource.AuthMeColumns; import fr.xephi.authme.datasource.Columns; +import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension; import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.process.register.executors.RegistrationMethod; From 137fc3d50579cfc9c2db29dd86f19fc4cc202325 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 24 Mar 2018 22:53:30 +0100 Subject: [PATCH 104/155] #1539 Use columns handler in more datasource methods; fix case-insensitivity for SQLite --- .../fr/xephi/authme/datasource/MySQL.java | 81 +++++-------------- .../fr/xephi/authme/datasource/SQLite.java | 81 ++++--------------- .../columnshandler/AuthMeColumns.java | 3 + .../columnshandler/AuthMeColumnsHandler.java | 35 +++++++- .../datasource/MySqlIntegrationTest.java | 4 +- 5 files changed, 75 insertions(+), 129 deletions(-) diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 8e501ae0c..e7739fb18 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -1,6 +1,7 @@ package fr.xephi.authme.datasource; import ch.jalu.datasourcecolumns.data.DataSourceValues; +import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; import com.google.common.annotations.VisibleForTesting; import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; @@ -23,11 +24,14 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import static ch.jalu.datasourcecolumns.data.UpdateValues.with; +import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eq; +import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eqIgnoreCase; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; @@ -269,24 +273,20 @@ public class MySQL implements DataSource { @Override public boolean isAuthAvailable(String user) { - String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user.toLowerCase()); - try (ResultSet rs = pst.executeQuery()) { - return rs.next(); - } - } catch (SQLException ex) { - logSqlException(ex); + try { + return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); + } catch (SQLException e) { + logSqlException(e); + return false; } - return false; } @Override public HashedPassword getPassword(String user) { try { - DataSourceValues passwordResult = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); - if (passwordResult.rowExists()) { - return new HashedPassword(passwordResult.get(AuthMeColumns.PASSWORD), passwordResult.get(AuthMeColumns.SALT)); + DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); + if (values.rowExists()) { + return new HashedPassword(values.get(AuthMeColumns.PASSWORD), values.get(AuthMeColumns.SALT)); } } catch (SQLException e) { logSqlException(e); @@ -354,19 +354,7 @@ public class MySQL implements DataSource { @Override public boolean updateSession(PlayerAuth auth) { - String sql = "UPDATE " + tableName + " SET " - + col.LAST_IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, auth.getLastIp()); - pst.setObject(2, auth.getLastLogin()); - pst.setString(3, auth.getRealName()); - pst.setString(4, auth.getNickname()); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); } @Override @@ -427,35 +415,17 @@ public class MySQL implements DataSource { @Override public List getAllAuthsByIp(String ip) { - List result = new ArrayList<>(); - String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_IP + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, ip); - try (ResultSet rs = pst.executeQuery()) { - while (rs.next()) { - result.add(rs.getString(col.NAME)); - } - } - } catch (SQLException ex) { - logSqlException(ex); + try { + return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); + } catch (SQLException e) { + logSqlException(e); + return Collections.emptyList(); } - return result; } @Override public int countAuthsByEmail(String email) { - String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, email); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return rs.getInt(1); - } - } - } catch (SQLException ex) { - logSqlException(ex); - } - return 0; + return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); } @Override @@ -566,18 +536,7 @@ public class MySQL implements DataSource { @Override public int getAccountsRegistered() { - int result = 0; - String sql = "SELECT COUNT(*) FROM " + tableName; - try (Connection con = getConnection(); - Statement st = con.createStatement(); - ResultSet rs = st.executeQuery(sql)) { - if (rs.next()) { - result = rs.getInt(1); - } - } catch (SQLException ex) { - logSqlException(ex); - } - return result; + return columnsHandler.count(new AlwaysTruePredicate<>()); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 0b2361aa8..01f13a50e 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -1,6 +1,7 @@ package fr.xephi.authme.datasource; import ch.jalu.datasourcecolumns.data.DataSourceValues; +import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; @@ -20,11 +21,14 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import static ch.jalu.datasourcecolumns.data.UpdateValues.with; +import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eq; +import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eqIgnoreCase; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; @@ -211,14 +215,10 @@ public class SQLite implements DataSource { @Override public boolean isAuthAvailable(String user) { - String sql = "SELECT 1 FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user); - try (ResultSet rs = pst.executeQuery()) { - return rs.next(); - } - } catch (SQLException ex) { - ConsoleLogger.warning(ex.getMessage()); + try { + return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); + } catch (SQLException e) { + logSqlException(e); return false; } } @@ -273,19 +273,7 @@ public class SQLite implements DataSource { @Override public boolean updateSession(PlayerAuth auth) { - String sql = "UPDATE " + tableName + " SET " + col.LAST_IP + "=?, " + col.LAST_LOGIN + "=?, " - + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)){ - pst.setString(1, auth.getLastIp()); - pst.setObject(2, auth.getLastLogin()); - pst.setString(3, auth.getRealName()); - pst.setString(4, auth.getNickname()); - pst.executeUpdate(); - return true; - } catch (SQLException ex) { - logSqlException(ex); - } - return false; + return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); } @Override @@ -360,36 +348,17 @@ public class SQLite implements DataSource { @Override public List getAllAuthsByIp(String ip) { - List countIp = new ArrayList<>(); - String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_IP + "=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, ip); - try (ResultSet rs = pst.executeQuery()) { - while (rs.next()) { - countIp.add(rs.getString(col.NAME)); - } - return countIp; - } - } catch (SQLException ex) { - logSqlException(ex); + try { + return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); + } catch (SQLException e) { + logSqlException(e); + return Collections.emptyList(); } - return new ArrayList<>(); } @Override public int countAuthsByEmail(String email) { - String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE " + col.EMAIL + " = ? COLLATE NOCASE;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, email); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return rs.getInt(1); - } - } - } catch (SQLException ex) { - logSqlException(ex); - } - return 0; + return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); } @Override @@ -491,15 +460,7 @@ public class SQLite implements DataSource { @Override public int getAccountsRegistered() { - String sql = "SELECT COUNT(*) FROM " + tableName + ";"; - try (PreparedStatement pst = con.prepareStatement(sql); ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return rs.getInt(1); - } - } catch (SQLException ex) { - logSqlException(ex); - } - return 0; + return columnsHandler.count(new AlwaysTruePredicate<>()); } @Override @@ -607,16 +568,6 @@ public class SQLite implements DataSource { + currentTimestamp + ", to all " + updatedRows + " rows"); } - private static void close(Statement st) { - if (st != null) { - try { - st.close(); - } catch (SQLException ex) { - logSqlException(ex); - } - } - } - private static void close(Connection con) { if (con != null) { try { diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java index 246bf5581..109ea5a63 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java @@ -45,6 +45,9 @@ public final class AuthMeColumns implements DependentColumn GROUP_ID = createInteger( DatabaseSettings.MYSQL_COL_GROUP, PlayerAuth::getGroupId, OPTIONAL); + public static final AuthMeColumns LAST_LOGIN = createLong( + DatabaseSettings.MYSQL_COL_LASTLOGIN, PlayerAuth::getLastLogin); + public static final AuthMeColumns REGISTRATION_IP = createString( DatabaseSettings.MYSQL_COL_REGISTER_IP, PlayerAuth::getRegistrationIp); diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java index a35c8c97c..08202e396 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java @@ -3,7 +3,9 @@ package fr.xephi.authme.datasource.columnshandler; import ch.jalu.datasourcecolumns.data.DataSourceValue; import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.data.UpdateValues; +import ch.jalu.datasourcecolumns.predicate.Predicate; import ch.jalu.datasourcecolumns.sqlimplementation.PredicateSqlGenerator; +import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementGenerator; import ch.jalu.datasourcecolumns.sqlimplementation.ResultSetValueRetriever; import ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandler; import fr.xephi.authme.data.auth.PlayerAuth; @@ -12,6 +14,7 @@ import fr.xephi.authme.settings.properties.DatabaseSettings; import java.sql.Connection; import java.sql.SQLException; +import java.util.List; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; @@ -39,8 +42,9 @@ public final class AuthMeColumnsHandler { String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); - SqlColumnsHandler sqlColHandler = - new SqlColumnsHandler<>(connection, columnContext, tableName, nameColumn); + SqlColumnsHandler sqlColHandler = new SqlColumnsHandler<>( + PreparedStatementGenerator.fromConnection(connection), columnContext, tableName, nameColumn, + new ResultSetValueRetriever<>(columnContext), new PredicateSqlGenerator<>(columnContext, true)); return new AuthMeColumnsHandler(sqlColHandler); } @@ -135,6 +139,18 @@ public final class AuthMeColumnsHandler { return internalHandler.retrieve(name.toLowerCase(), columns); } + /** + * Retrieves a column's value for all rows that satisfy the given predicate. + * + * @param predicate the predicate to fulfill + * @param column the column to retrieve from the matching rows + * @param the column's value type + * @return the values of the matching rows + */ + public List retrieve(Predicate predicate, AuthMeColumns column) throws SQLException { + return internalHandler.retrieve(predicate, column); + } + /** * Inserts the given values into a new row, as taken from the player auth. * @@ -150,4 +166,19 @@ public final class AuthMeColumnsHandler { return false; } } + + /** + * Returns the number of rows that match the provided predicate. + * + * @param predicate the predicate to test the rows for + * @return number of rows fulfilling the predicate + */ + public int count(Predicate predicate) { + try { + return internalHandler.count(predicate); + } catch (SQLException e) { + logSqlException(e); + return 0; + } + } } diff --git a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java index eab6a4cc4..027608230 100644 --- a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java @@ -55,7 +55,9 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest { HikariConfig config = new HikariConfig(); config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource"); config.setConnectionTestQuery("VALUES 1"); - config.addDataSourceProperty("URL", "jdbc:h2:mem:test"); + // Note "ignorecase=true": H2 does not support `COLLATE NOCASE` for case-insensitive equals queries. + // MySQL is by default case-insensitive so this is OK to make as an assumption. + config.addDataSourceProperty("URL", "jdbc:h2:mem:test;ignorecase=true"); config.addDataSourceProperty("user", "sa"); config.addDataSourceProperty("password", "sa"); HikariDataSource ds = new HikariDataSource(config); From 4595a141914817b6764d8fa16847af9d4eaae5cd Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 25 Mar 2018 11:52:40 +0200 Subject: [PATCH 105/155] #1539 Add support for columns that are not on player auth (is_logged, has_session) --- .../executable/authme/GetEmailCommand.java | 6 +- .../authme/debug/TestEmailSender.java | 6 +- .../executable/email/RecoverEmailCommand.java | 6 +- .../authme/data/VerificationCodeManager.java | 10 +- .../authme/datasource/CacheDataSource.java | 8 +- .../xephi/authme/datasource/DataSource.java | 3 +- .../authme/datasource/DataSourceResult.java | 53 ---------- .../fr/xephi/authme/datasource/FlatFile.java | 3 +- .../fr/xephi/authme/datasource/MySQL.java | 95 +++++------------- .../fr/xephi/authme/datasource/SQLite.java | 64 ++++--------- .../columnshandler/AuthMeColumns.java | 96 ++++++------------- .../columnshandler/AuthMeColumnsFactory.java | 45 +++++---- .../columnshandler/AuthMeColumnsHandler.java | 30 ++++-- .../columnshandler/ColumnContext.java | 4 +- .../columnshandler/DataSourceColumn.java | 57 +++++++++++ .../columnshandler/PlayerAuthColumn.java | 32 +++++++ .../xephi/authme/ClassesConsistencyTest.java | 5 +- .../authme/GetEmailCommandTest.java | 6 +- .../email/RecoverEmailCommandTest.java | 12 +-- .../data/VerificationCodeManagerTest.java | 10 +- .../AbstractDataSourceIntegrationTest.java | 8 +- 21 files changed, 257 insertions(+), 302 deletions(-) delete mode 100644 src/main/java/fr/xephi/authme/datasource/DataSourceResult.java create mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java create mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java index a7f327a10..b66914387 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command.executable.authme; +import ch.jalu.datasourcecolumns.data.DataSourceValue; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; @@ -25,8 +25,8 @@ public class GetEmailCommand implements ExecutableCommand { public void executeCommand(CommandSender sender, List arguments) { String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0); - DataSourceResult email = dataSource.getEmail(playerName); - if (email.playerExists()) { + DataSourceValue email = dataSource.getEmail(playerName); + if (email.rowExists()) { sender.sendMessage("[AuthMe] " + playerName + "'s email: " + email.getValue()); } else { commonService.send(sender, MessageKey.UNKNOWN_USER); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java index f3580d326..02bd4a213 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command.executable.authme.debug; +import ch.jalu.datasourcecolumns.data.DataSourceValue; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.mail.SendMailSsl; import fr.xephi.authme.permission.DebugSectionPermissions; import fr.xephi.authme.permission.PermissionNode; @@ -82,8 +82,8 @@ class TestEmailSender implements DebugSection { */ private String getEmail(CommandSender sender, List arguments) { if (arguments.isEmpty()) { - DataSourceResult emailResult = dataSource.getEmail(sender.getName()); - if (!emailResult.playerExists()) { + DataSourceValue emailResult = dataSource.getEmail(sender.getName()); + if (!emailResult.rowExists()) { sender.sendMessage(ChatColor.RED + "Please provide an email address, " + "e.g. /authme debug mail test@example.com"); return null; diff --git a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java index 339980a34..0a3a96945 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java @@ -1,10 +1,10 @@ package fr.xephi.authme.command.executable.email; +import ch.jalu.datasourcecolumns.data.DataSourceValue; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; @@ -58,8 +58,8 @@ public class RecoverEmailCommand extends PlayerCommand { return; } - DataSourceResult emailResult = dataSource.getEmail(playerName); - if (!emailResult.playerExists()) { + DataSourceValue emailResult = dataSource.getEmail(playerName); + if (!emailResult.rowExists()) { commonService.send(player, MessageKey.USAGE_REGISTER); return; } diff --git a/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java b/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java index a6ba75c6e..c5c2d7257 100644 --- a/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java +++ b/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java @@ -1,7 +1,7 @@ package fr.xephi.authme.data; +import ch.jalu.datasourcecolumns.data.DataSourceValue; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.mail.EmailService; @@ -103,8 +103,8 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup { */ public boolean hasEmail(String name) { boolean result = false; - DataSourceResult emailResult = dataSource.getEmail(name); - if (emailResult.playerExists()) { + DataSourceValue emailResult = dataSource.getEmail(name); + if (emailResult.rowExists()) { final String email = emailResult.getValue(); if (!Utils.isEmailEmpty(email)) { result = true; @@ -130,8 +130,8 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup { * @param name the name of the player to generate a code for */ private void generateCode(String name) { - DataSourceResult emailResult = dataSource.getEmail(name); - if (emailResult.playerExists()) { + DataSourceValue emailResult = dataSource.getEmail(name); + if (emailResult.rowExists()) { final String email = emailResult.getValue(); if (!Utils.isEmailEmpty(email)) { String code = RandomStringUtils.generateNum(6); // 6 digits code diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 39f04a53c..5081cf09c 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -1,5 +1,7 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValue; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -244,10 +246,10 @@ public class CacheDataSource implements DataSource { } @Override - public DataSourceResult getEmail(String user) { + public DataSourceValue getEmail(String user) { return cachedAuths.getUnchecked(user) - .map(auth -> DataSourceResult.of(auth.getEmail())) - .orElse(DataSourceResult.unknownPlayer()); + .map(auth -> DataSourceValueImpl.of(auth.getEmail())) + .orElse(DataSourceValueImpl.unknownRow()); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index 6f97951db..a0a47adea 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -1,5 +1,6 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValue; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.security.crypts.HashedPassword; @@ -216,7 +217,7 @@ public interface DataSource extends Reloadable { * @param user the user to retrieve an email for * @return the email saved for the user, or null if user or email is not present */ - DataSourceResult getEmail(String user); + DataSourceValue getEmail(String user); /** * Return all players of the database. diff --git a/src/main/java/fr/xephi/authme/datasource/DataSourceResult.java b/src/main/java/fr/xephi/authme/datasource/DataSourceResult.java deleted file mode 100644 index c005874e2..000000000 --- a/src/main/java/fr/xephi/authme/datasource/DataSourceResult.java +++ /dev/null @@ -1,53 +0,0 @@ -package fr.xephi.authme.datasource; - -/** - * Wraps a value and allows to specify whether a value is missing or the player is not registered. - */ -public final class DataSourceResult { - - /** Instance used when a player does not exist. */ - private static final DataSourceResult UNKNOWN_PLAYER = new DataSourceResult<>(null); - private final T value; - - private DataSourceResult(T value) { - this.value = value; - } - - /** - * Returns a {@link DataSourceResult} for the given value. - * - * @param value the value to wrap - * @param the value's type - * @return DataSourceResult object for the given value - */ - public static DataSourceResult of(T value) { - return new DataSourceResult<>(value); - } - - /** - * Returns a {@link DataSourceResult} specifying that the player does not exist. - * - * @param the value type - * @return data source result for unknown player - */ - public static DataSourceResult unknownPlayer() { - return UNKNOWN_PLAYER; - } - - /** - * @return whether the player of the associated value exists - */ - public boolean playerExists() { - return this != UNKNOWN_PLAYER; - } - - /** - * Returns the value. It is {@code null} if the player is unknown. It is also {@code null} - * if the player exists but does not have the value defined. - * - * @return the value, or null - */ - public T getValue() { - return value; - } -} diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index d234da556..56db1e6e7 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -1,5 +1,6 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValue; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; @@ -366,7 +367,7 @@ public class FlatFile implements DataSource { } @Override - public DataSourceResult getEmail(String user) { + public DataSourceValue getEmail(String user) { throw new UnsupportedOperationException("Flat file no longer supported"); } diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index e7739fb18..b8e487e2b 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -1,5 +1,7 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValue; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; import com.google.common.annotations.VisibleForTesting; @@ -448,90 +450,49 @@ public class MySQL implements DataSource { @Override public boolean isLogged(String user) { - String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user); - try (ResultSet rs = pst.executeQuery()) { - return rs.next() && (rs.getInt(col.IS_LOGGED) == 1); - } - } catch (SQLException ex) { - logSqlException(ex); + try { + DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.IS_LOGGED); + return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); + } catch (SQLException e) { + logSqlException(e); + return false; } - return false; } @Override public void setLogged(String user) { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 1); - pst.setString(2, user.toLowerCase()); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 1); } @Override public void setUnlogged(String user) { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 0); - pst.setString(2, user.toLowerCase()); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 0); } @Override public boolean hasSession(String user) { - String sql = "SELECT " + col.HAS_SESSION + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user.toLowerCase()); - try (ResultSet rs = pst.executeQuery()) { - return rs.next() && (rs.getInt(col.HAS_SESSION) == 1); - } - } catch (SQLException ex) { - logSqlException(ex); + try { + DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); + return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); + } catch (SQLException e) { + logSqlException(e); + return false; } - return false; } @Override public void grantSession(String user) { - String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 1); - pst.setString(2, user.toLowerCase()); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); } @Override public void revokeSession(String user) { - String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 0); - pst.setString(2, user.toLowerCase()); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); } @Override public void purgeLogged() { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 0); - pst.setInt(2, 1); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); } @Override @@ -545,19 +506,13 @@ public class MySQL implements DataSource { } @Override - public DataSourceResult getEmail(String user) { - String sql = "SELECT " + col.EMAIL + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return DataSourceResult.of(rs.getString(1)); - } - } - } catch (SQLException ex) { - logSqlException(ex); + public DataSourceValue getEmail(String user) { + try { + return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); + } catch (SQLException e) { + logSqlException(e); + return DataSourceValueImpl.unknownRow(); } - return DataSourceResult.unknownPlayer(); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 01f13a50e..5c78e0bbe 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -1,5 +1,7 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValue; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; import com.google.common.annotations.VisibleForTesting; @@ -408,54 +410,28 @@ public class SQLite implements DataSource { @Override public boolean hasSession(String user) { - String sql = "SELECT " + col.HAS_SESSION + " FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user.toLowerCase()); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return rs.getInt(col.HAS_SESSION) == 1; - } - } - } catch (SQLException ex) { - logSqlException(ex); + try { + DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); + return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); + } catch (SQLException e) { + logSqlException(e); + return false; } - return false; } @Override public void grantSession(String user) { - String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE LOWER(" + col.NAME + ")=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 1); - pst.setString(2, user.toLowerCase()); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); } @Override public void revokeSession(String user) { - String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE LOWER(" + col.NAME + ")=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 0); - pst.setString(2, user.toLowerCase()); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); } @Override public void purgeLogged() { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 0); - pst.setInt(2, 1); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } + columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); } @Override @@ -469,19 +445,13 @@ public class SQLite implements DataSource { } @Override - public DataSourceResult getEmail(String user) { - String sql = "SELECT " + col.EMAIL + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return DataSourceResult.of(rs.getString(1)); - } - } - } catch (SQLException ex) { - logSqlException(ex); + public DataSourceValue getEmail(String user) { + try { + return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); + } catch (SQLException e) { + logSqlException(e); + return DataSourceValueImpl.unknownRow(); } - return DataSourceResult.unknownPlayer(); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java index 109ea5a63..5c235095f 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java @@ -1,13 +1,8 @@ package fr.xephi.authme.datasource.columnshandler; -import ch.jalu.configme.properties.Property; -import ch.jalu.datasourcecolumns.ColumnType; -import ch.jalu.datasourcecolumns.DependentColumn; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.settings.properties.DatabaseSettings; -import java.util.function.Function; - import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.ColumnOptions.DEFAULT_FOR_NULL; import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.ColumnOptions.OPTIONAL; import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createDouble; @@ -17,104 +12,71 @@ import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.cre import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createString; /** - * Column definitions for the AuthMe table. - * - * @param the column type - * @see PlayerAuth + * Contains column definitions for the AuthMe table. */ -public final class AuthMeColumns implements DependentColumn { +public final class AuthMeColumns { - public static final AuthMeColumns NAME = createString( + public static final PlayerAuthColumn NAME = createString( DatabaseSettings.MYSQL_COL_NAME, PlayerAuth::getNickname); - public static final AuthMeColumns NICK_NAME = createString( + public static final PlayerAuthColumn NICK_NAME = createString( DatabaseSettings.MYSQL_COL_REALNAME, PlayerAuth::getRealName); - public static final AuthMeColumns PASSWORD = createString( + public static final PlayerAuthColumn PASSWORD = createString( DatabaseSettings.MYSQL_COL_PASSWORD, auth -> auth.getPassword().getHash()); - public static final AuthMeColumns SALT = createString( + public static final PlayerAuthColumn SALT = createString( DatabaseSettings.MYSQL_COL_SALT, auth -> auth.getPassword().getSalt(), OPTIONAL); - public static final AuthMeColumns EMAIL = createString( + public static final PlayerAuthColumn EMAIL = createString( DatabaseSettings.MYSQL_COL_EMAIL, PlayerAuth::getEmail, DEFAULT_FOR_NULL); - public static final AuthMeColumns LAST_IP = createString( + public static final PlayerAuthColumn LAST_IP = createString( DatabaseSettings.MYSQL_COL_LAST_IP, PlayerAuth::getLastIp); - public static final AuthMeColumns GROUP_ID = createInteger( + public static final PlayerAuthColumn GROUP_ID = createInteger( DatabaseSettings.MYSQL_COL_GROUP, PlayerAuth::getGroupId, OPTIONAL); - public static final AuthMeColumns LAST_LOGIN = createLong( + public static final PlayerAuthColumn LAST_LOGIN = createLong( DatabaseSettings.MYSQL_COL_LASTLOGIN, PlayerAuth::getLastLogin); - public static final AuthMeColumns REGISTRATION_IP = createString( + public static final PlayerAuthColumn REGISTRATION_IP = createString( DatabaseSettings.MYSQL_COL_REGISTER_IP, PlayerAuth::getRegistrationIp); - public static final AuthMeColumns REGISTRATION_DATE = createLong( + public static final PlayerAuthColumn REGISTRATION_DATE = createLong( DatabaseSettings.MYSQL_COL_REGISTER_DATE, PlayerAuth::getRegistrationDate); - public static final AuthMeColumns LOCATION_X = createDouble( + // -------- + // Location columns + // -------- + public static final PlayerAuthColumn LOCATION_X = createDouble( DatabaseSettings.MYSQL_COL_LASTLOC_X, PlayerAuth::getQuitLocX); - public static final AuthMeColumns LOCATION_Y = createDouble( + public static final PlayerAuthColumn LOCATION_Y = createDouble( DatabaseSettings.MYSQL_COL_LASTLOC_Y, PlayerAuth::getQuitLocY); - public static final AuthMeColumns LOCATION_Z = createDouble( + public static final PlayerAuthColumn LOCATION_Z = createDouble( DatabaseSettings.MYSQL_COL_LASTLOC_Z, PlayerAuth::getQuitLocZ); - public static final AuthMeColumns LOCATION_WORLD = createString( + public static final PlayerAuthColumn LOCATION_WORLD = createString( DatabaseSettings.MYSQL_COL_LASTLOC_WORLD, PlayerAuth::getWorld); - public static final AuthMeColumns LOCATION_YAW = createFloat( + public static final PlayerAuthColumn LOCATION_YAW = createFloat( DatabaseSettings.MYSQL_COL_LASTLOC_YAW, PlayerAuth::getYaw); - public static final AuthMeColumns LOCATION_PITCH = createFloat( + public static final PlayerAuthColumn LOCATION_PITCH = createFloat( DatabaseSettings.MYSQL_COL_LASTLOC_PITCH, PlayerAuth::getPitch); + // -------- + // Columns not on PlayerAuth + // -------- + public static final DataSourceColumn IS_LOGGED = createInteger( + DatabaseSettings.MYSQL_COL_ISLOGGED); - private final ColumnType columnType; - private final Property nameProperty; - private final Function playerAuthGetter; - private final boolean isOptional; - private final boolean useDefaultForNull; - - AuthMeColumns(ColumnType type, Property nameProperty, Function playerAuthGetter, - boolean isOptional, boolean useDefaultForNull) { - this.columnType = type; - this.nameProperty = nameProperty; - this.playerAuthGetter = playerAuthGetter; - this.isOptional = isOptional; - this.useDefaultForNull = useDefaultForNull; - } + public static final DataSourceColumn HAS_SESSION = createInteger( + DatabaseSettings.MYSQL_COL_HASSESSION); - public Property getNameProperty() { - return nameProperty; - } - - @Override - public T getValueFromDependent(PlayerAuth playerAuth) { - return playerAuthGetter.apply(playerAuth); - } - - @Override - public String resolveName(ColumnContext columnContext) { - return columnContext.getName(this); - } - - @Override - public ColumnType getType() { - return columnType; - } - - @Override - public boolean isColumnUsed(ColumnContext columnContext) { - return !isOptional || !resolveName(columnContext).isEmpty(); - } - - @Override - public boolean useDefaultForNullValue(ColumnContext columnContext) { - return useDefaultForNull && columnContext.hasDefaultSupport(); + private AuthMeColumns() { } } diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java index 420a26bc0..3400f76cd 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java @@ -8,46 +8,53 @@ import fr.xephi.authme.data.auth.PlayerAuth; import java.util.function.Function; /** - * Util class for initializing {@link AuthMeColumns} constants. + * Util class for initializing {@link DataSourceColumn} objects. */ final class AuthMeColumnsFactory { private AuthMeColumnsFactory() { } - static AuthMeColumns createInteger(Property nameProperty, - Function playerAuthGetter, - ColumnOptions... options) { + static DataSourceColumn createInteger(Property nameProperty, + ColumnOptions... options) { + return new DataSourceColumn<>(StandardTypes.INTEGER, nameProperty, + isOptional(options), hasDefaultForNull(options)); + } + + static PlayerAuthColumn createInteger(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { return createInternal(StandardTypes.INTEGER, nameProperty, playerAuthGetter, options); } - static AuthMeColumns createLong(Property nameProperty, - Function playerAuthGetter, - ColumnOptions... options) { + static PlayerAuthColumn createLong(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { return createInternal(StandardTypes.LONG, nameProperty, playerAuthGetter, options); } - static AuthMeColumns createString(Property nameProperty, - Function playerAuthGetter, - ColumnOptions... options) { + static PlayerAuthColumn createString(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { return createInternal(StandardTypes.STRING, nameProperty, playerAuthGetter, options); } - static AuthMeColumns createDouble(Property nameProperty, - Function playerAuthGetter, - ColumnOptions... options) { + static PlayerAuthColumn createDouble(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { return createInternal(StandardTypes.DOUBLE, nameProperty, playerAuthGetter, options); } - static AuthMeColumns createFloat(Property nameProperty, - Function playerAuthGetter, - ColumnOptions... options) { + static PlayerAuthColumn createFloat(Property nameProperty, + Function playerAuthGetter, + ColumnOptions... options) { return createInternal(StandardTypes.FLOAT, nameProperty, playerAuthGetter, options); } - private static AuthMeColumns createInternal(ColumnType type, Property nameProperty, - Function authGetter, ColumnOptions... options) { - return new AuthMeColumns<>(type, nameProperty, authGetter, isOptional(options), hasDefaultForNull(options)); + private static PlayerAuthColumn createInternal(ColumnType type, Property nameProperty, + Function authGetter, + ColumnOptions... options) { + return new PlayerAuthColumn<>(type, nameProperty, isOptional(options), hasDefaultForNull(options), authGetter); } private static boolean isOptional(ColumnOptions[] options) { diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java index 08202e396..7b6657126 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java @@ -75,7 +75,7 @@ public final class AuthMeColumnsHandler { * @param the column type * @return true upon success, false otherwise */ - public boolean update(String name, AuthMeColumns column, T value) { + public boolean update(String name, DataSourceColumn column, T value) { try { return internalHandler.update(name, column, value); } catch (SQLException e) { @@ -91,7 +91,7 @@ public final class AuthMeColumnsHandler { * @param columns the columns to update in the row * @return true upon success, false otherwise */ - public boolean update(PlayerAuth auth, AuthMeColumns... columns) { + public boolean update(PlayerAuth auth, PlayerAuthColumn... columns) { try { return internalHandler.update(auth.getNickname(), auth, columns); } catch (SQLException e) { @@ -116,6 +116,24 @@ public final class AuthMeColumnsHandler { } } + /** + * Sets the given value to the provided column for all rows which match the predicate. + * + * @param predicate the predicate to filter rows by + * @param column the column to modify on the matched rows + * @param value the new value to set + * @param the column type + * @return number of modified rows + */ + public int update(Predicate predicate, DataSourceColumn column, T value) { + try { + return internalHandler.update(predicate, column, value); + } catch (SQLException e) { + logSqlException(e); + return 0; + } + } + /** * Retrieves the given column from a given row. * @@ -124,7 +142,7 @@ public final class AuthMeColumnsHandler { * @param the column type * @return the result of the lookup */ - public DataSourceValue retrieve(String name, AuthMeColumns column) throws SQLException { + public DataSourceValue retrieve(String name, DataSourceColumn column) throws SQLException { return internalHandler.retrieve(name.toLowerCase(), column); } @@ -135,7 +153,7 @@ public final class AuthMeColumnsHandler { * @param columns the columns to retrieve * @return map-like object with the requested values */ - public DataSourceValues retrieve(String name, AuthMeColumns... columns) throws SQLException { + public DataSourceValues retrieve(String name, DataSourceColumn... columns) throws SQLException { return internalHandler.retrieve(name.toLowerCase(), columns); } @@ -147,7 +165,7 @@ public final class AuthMeColumnsHandler { * @param the column's value type * @return the values of the matching rows */ - public List retrieve(Predicate predicate, AuthMeColumns column) throws SQLException { + public List retrieve(Predicate predicate, DataSourceColumn column) throws SQLException { return internalHandler.retrieve(predicate, column); } @@ -158,7 +176,7 @@ public final class AuthMeColumnsHandler { * @param columns the columns to insert * @return true upon success, false otherwise */ - public boolean insert(PlayerAuth auth, AuthMeColumns... columns) { + public boolean insert(PlayerAuth auth, PlayerAuthColumn... columns) { try { return internalHandler.insert(auth, columns); } catch (SQLException e) { diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java index 8759935db..554266a05 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java @@ -11,7 +11,7 @@ import java.util.Map; public class ColumnContext { private final Settings settings; - private final Map, String> columnNames = new HashMap<>(); + private final Map, String> columnNames = new HashMap<>(); private final boolean hasDefaultSupport; /** @@ -25,7 +25,7 @@ public class ColumnContext { this.hasDefaultSupport = hasDefaultSupport; } - public String getName(AuthMeColumns column) { + public String getName(DataSourceColumn column) { return columnNames.computeIfAbsent(column, k -> settings.getProperty(k.getNameProperty())); } diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java new file mode 100644 index 000000000..4b7fa4ca7 --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java @@ -0,0 +1,57 @@ +package fr.xephi.authme.datasource.columnshandler; + +import ch.jalu.configme.properties.Property; +import ch.jalu.datasourcecolumns.Column; +import ch.jalu.datasourcecolumns.ColumnType; + +/** + * Basic {@link Column} implementation for AuthMe. + * + * @param column type + */ +public class DataSourceColumn implements Column { + + private final ColumnType columnType; + private final Property nameProperty; + private final boolean isOptional; + private final boolean useDefaultForNull; + + /** + * Constructor. + * + * @param type type of the column + * @param nameProperty property defining the column name + * @param isOptional whether or not the column can be skipped (if name is configured to empty string) + * @param useDefaultForNull whether SQL DEFAULT should be used for null values (if supported by the database) + */ + DataSourceColumn(ColumnType type, Property nameProperty, boolean isOptional, boolean useDefaultForNull) { + this.columnType = type; + this.nameProperty = nameProperty; + this.isOptional = isOptional; + this.useDefaultForNull = useDefaultForNull; + } + + public Property getNameProperty() { + return nameProperty; + } + + @Override + public String resolveName(ColumnContext columnContext) { + return columnContext.getName(this); + } + + @Override + public ColumnType getType() { + return columnType; + } + + @Override + public boolean isColumnUsed(ColumnContext columnContext) { + return !isOptional || !resolveName(columnContext).isEmpty(); + } + + @Override + public boolean useDefaultForNullValue(ColumnContext columnContext) { + return useDefaultForNull && columnContext.hasDefaultSupport(); + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java new file mode 100644 index 000000000..43d022b13 --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java @@ -0,0 +1,32 @@ +package fr.xephi.authme.datasource.columnshandler; + +import ch.jalu.configme.properties.Property; +import ch.jalu.datasourcecolumns.ColumnType; +import ch.jalu.datasourcecolumns.DependentColumn; +import fr.xephi.authme.data.auth.PlayerAuth; + +import java.util.function.Function; + +/** + * Implementation for columns which can also be retrieved from a {@link PlayerAuth} object. + * + * @param column type + */ +public class PlayerAuthColumn extends DataSourceColumn implements DependentColumn { + + private final Function playerAuthGetter; + + /* + * Constructor. See parent class for details. + */ + PlayerAuthColumn(ColumnType type, Property nameProperty, boolean isOptional, boolean useDefaultForNull, + Function playerAuthGetter) { + super(type, nameProperty, isOptional, useDefaultForNull); + this.playerAuthGetter = playerAuthGetter; + } + + @Override + public T getValueFromDependent(PlayerAuth auth) { + return playerAuthGetter.apply(auth); + } +} diff --git a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java index c7f093982..eb00d14e0 100644 --- a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java @@ -6,7 +6,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import fr.xephi.authme.data.captcha.CaptchaCodeStorage; import fr.xephi.authme.datasource.Columns; -import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; +import fr.xephi.authme.datasource.columnshandler.DataSourceColumn; +import fr.xephi.authme.datasource.columnshandler.PlayerAuthColumn; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension; import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.process.register.executors.RegistrationMethod; @@ -53,7 +54,7 @@ public class ClassesConsistencyTest { int.class, long.class, float.class, String.class, File.class, Enum.class, collectionsUnmodifiableList(), Charset.class, /* AuthMe */ - Property.class, RegistrationMethod.class, AuthMeColumns.class, + Property.class, RegistrationMethod.class, DataSourceColumn.class, PlayerAuthColumn.class, /* Guava */ ImmutableMap.class, ImmutableList.class); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java index 07c92d13c..dc3fda40e 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.command.executable.authme; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; @@ -38,7 +38,7 @@ public class GetEmailCommandTest { public void shouldReportUnknownUser() { // given String user = "myTestUser"; - given(dataSource.getEmail(user)).willReturn(DataSourceResult.unknownPlayer()); + given(dataSource.getEmail(user)).willReturn(DataSourceValueImpl.unknownRow()); CommandSender sender = mock(CommandSender.class); // when @@ -53,7 +53,7 @@ public class GetEmailCommandTest { // given String user = "userToView"; String email = "user.email@example.org"; - given(dataSource.getEmail(user)).willReturn(DataSourceResult.of(email)); + given(dataSource.getEmail(user)).willReturn(DataSourceValueImpl.of(email)); CommandSender sender = mock(CommandSender.class); // when diff --git a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java index 416649e05..5842ec300 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java @@ -1,12 +1,12 @@ package fr.xephi.authme.command.executable.email; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import ch.jalu.injector.testing.BeforeInjecting; import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; @@ -118,7 +118,7 @@ public class RecoverEmailCommandTest { given(sender.getName()).willReturn(name); given(emailService.hasAllInformation()).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); - given(dataSource.getEmail(name)).willReturn(DataSourceResult.unknownPlayer()); + given(dataSource.getEmail(name)).willReturn(DataSourceValueImpl.unknownRow()); // when command.executeCommand(sender, Collections.singletonList("someone@example.com")); @@ -138,7 +138,7 @@ public class RecoverEmailCommandTest { given(sender.getName()).willReturn(name); given(emailService.hasAllInformation()).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); - given(dataSource.getEmail(name)).willReturn(DataSourceResult.of(DEFAULT_EMAIL)); + given(dataSource.getEmail(name)).willReturn(DataSourceValueImpl.of(DEFAULT_EMAIL)); // when command.executeCommand(sender, Collections.singletonList(DEFAULT_EMAIL)); @@ -158,7 +158,7 @@ public class RecoverEmailCommandTest { given(sender.getName()).willReturn(name); given(emailService.hasAllInformation()).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); - given(dataSource.getEmail(name)).willReturn(DataSourceResult.of("raptor@example.org")); + given(dataSource.getEmail(name)).willReturn(DataSourceValueImpl.of("raptor@example.org")); // when command.executeCommand(sender, Collections.singletonList("wrong-email@example.com")); @@ -180,7 +180,7 @@ public class RecoverEmailCommandTest { given(emailService.sendRecoveryCode(anyString(), anyString(), anyString())).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); String email = "v@example.com"; - given(dataSource.getEmail(name)).willReturn(DataSourceResult.of(email)); + given(dataSource.getEmail(name)).willReturn(DataSourceValueImpl.of(email)); String code = "a94f37"; given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true); given(recoveryCodeService.generateCode(name)).willReturn(code); @@ -205,7 +205,7 @@ public class RecoverEmailCommandTest { given(emailService.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); String email = "vulture@example.com"; - given(dataSource.getEmail(name)).willReturn(DataSourceResult.of(email)); + given(dataSource.getEmail(name)).willReturn(DataSourceValueImpl.of(email)); given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(false); setBukkitServiceToRunTaskAsynchronously(bukkitService); diff --git a/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java b/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java index b99d6a373..732b1ad70 100644 --- a/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.data; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerPermission; @@ -51,7 +51,7 @@ public class VerificationCodeManagerTest { // given String name1 = "ILoveTests"; Player player1 = mockPlayerWithName(name1); - given(dataSource.getEmail(name1)).willReturn(DataSourceResult.of("ilovetests@test.com")); + given(dataSource.getEmail(name1)).willReturn(DataSourceValueImpl.of("ilovetests@test.com")); given(permissionsManager.hasPermission(player1, PlayerPermission.VERIFICATION_CODE)).willReturn(true); String name2 = "StillLovingTests"; Player player2 = mockPlayerWithName(name2); @@ -106,7 +106,7 @@ public class VerificationCodeManagerTest { // given String player = "ILoveTests"; String email = "ilovetests@test.com"; - given(dataSource.getEmail(player)).willReturn(DataSourceResult.of(email)); + given(dataSource.getEmail(player)).willReturn(DataSourceValueImpl.of(email)); VerificationCodeManager codeManager1 = createCodeManager(); VerificationCodeManager codeManager2 = createCodeManager(); codeManager2.codeExistOrGenerateNew(player); @@ -125,7 +125,7 @@ public class VerificationCodeManagerTest { // given String player = "ILoveTests"; String email = "ilovetests@test.com"; - given(dataSource.getEmail(player)).willReturn(DataSourceResult.of(email)); + given(dataSource.getEmail(player)).willReturn(DataSourceValueImpl.of(email)); VerificationCodeManager codeManager1 = createCodeManager(); VerificationCodeManager codeManager2 = createCodeManager(); codeManager2.codeExistOrGenerateNew(player); @@ -145,7 +145,7 @@ public class VerificationCodeManagerTest { String player = "ILoveTests"; String code = "193458"; String email = "ilovetests@test.com"; - given(dataSource.getEmail(player)).willReturn(DataSourceResult.of(email)); + given(dataSource.getEmail(player)).willReturn(DataSourceValueImpl.of(email)); VerificationCodeManager codeManager1 = createCodeManager(); VerificationCodeManager codeManager2 = createCodeManager(); codeManager1.codeExistOrGenerateNew(player); diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index 70df2d48b..8efee23b4 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -1,5 +1,7 @@ package fr.xephi.authme.datasource; +import ch.jalu.datasourcecolumns.data.DataSourceValue; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import com.google.common.collect.Lists; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; @@ -420,12 +422,12 @@ public abstract class AbstractDataSourceIntegrationTest { DataSource dataSource = getDataSource(); // when - DataSourceResult email1 = dataSource.getEmail(user1); - DataSourceResult email2 = dataSource.getEmail(user2); + DataSourceValue email1 = dataSource.getEmail(user1); + DataSourceValue email2 = dataSource.getEmail(user2); // then assertThat(email1.getValue(), equalTo("user@example.org")); - assertThat(email2, is(DataSourceResult.unknownPlayer())); + assertThat(email2, is(DataSourceValueImpl.unknownRow())); } @Test From 26472b6be32e924813f61841b90087ae46a2ea66 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 25 Mar 2018 22:27:44 +0200 Subject: [PATCH 106/155] #1539 Create common parent for SQLite and MySQL --- .../datasource/AbstractSqlDataSource.java | 169 +++++++++++++++++ .../fr/xephi/authme/datasource/MySQL.java | 151 +-------------- .../fr/xephi/authme/datasource/SQLite.java | 174 +----------------- .../xephi/authme/ClassesConsistencyTest.java | 2 + 4 files changed, 175 insertions(+), 321 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java diff --git a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java new file mode 100644 index 000000000..0c40c479d --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java @@ -0,0 +1,169 @@ +package fr.xephi.authme.datasource; + +import ch.jalu.datasourcecolumns.data.DataSourceValue; +import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; +import ch.jalu.datasourcecolumns.data.DataSourceValues; +import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; +import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; +import fr.xephi.authme.security.crypts.HashedPassword; + +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; + +import static ch.jalu.datasourcecolumns.data.UpdateValues.with; +import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eq; +import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eqIgnoreCase; +import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; + +/** + * Common type for SQL-based data sources. Classes implementing this + * must ensure that {@link #columnsHandler} is initialized on creation. + */ +public abstract class AbstractSqlDataSource implements DataSource { + + protected AuthMeColumnsHandler columnsHandler; + + @Override + public boolean isAuthAvailable(String user) { + try { + return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); + } catch (SQLException e) { + logSqlException(e); + return false; + } + } + + @Override + public HashedPassword getPassword(String user) { + try { + DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); + if (values.rowExists()) { + return new HashedPassword(values.get(AuthMeColumns.PASSWORD), values.get(AuthMeColumns.SALT)); + } + } catch (SQLException e) { + logSqlException(e); + } + return null; + } + + @Override + public boolean saveAuth(PlayerAuth auth) { + return columnsHandler.insert(auth, + AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, + AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP); + } + + @Override + public boolean hasSession(String user) { + try { + DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); + return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); + } catch (SQLException e) { + logSqlException(e); + return false; + } + } + + @Override + public boolean updateSession(PlayerAuth auth) { + return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); + } + + @Override + public boolean updatePassword(PlayerAuth auth) { + return updatePassword(auth.getNickname(), auth.getPassword()); + } + + @Override + public boolean updatePassword(String user, HashedPassword password) { + return columnsHandler.update(user, + with(AuthMeColumns.PASSWORD, password.getHash()) + .and(AuthMeColumns.SALT, password.getSalt()).build()); + } + + @Override + public boolean updateQuitLoc(PlayerAuth auth) { + return columnsHandler.update(auth, + AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, + AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); + } + + @Override + public List getAllAuthsByIp(String ip) { + try { + return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); + } catch (SQLException e) { + logSqlException(e); + return Collections.emptyList(); + } + } + + @Override + public int countAuthsByEmail(String email) { + return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); + } + + @Override + public boolean updateEmail(PlayerAuth auth) { + return columnsHandler.update(auth, AuthMeColumns.EMAIL); + } + + @Override + public boolean isLogged(String user) { + try { + DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.IS_LOGGED); + return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); + } catch (SQLException e) { + logSqlException(e); + return false; + } + } + + @Override + public void setLogged(String user) { + columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 1); + } + + @Override + public void setUnlogged(String user) { + columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 0); + } + + @Override + public void grantSession(String user) { + columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); + } + + @Override + public void revokeSession(String user) { + columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); + } + + @Override + public void purgeLogged() { + columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); + } + + @Override + public int getAccountsRegistered() { + return columnsHandler.count(new AlwaysTruePredicate<>()); + } + + @Override + public boolean updateRealName(String user, String realName) { + return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); + } + + @Override + public DataSourceValue getEmail(String user) { + try { + return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); + } catch (SQLException e) { + logSqlException(e); + return DataSourceValueImpl.unknownRow(); + } + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index b8e487e2b..9f4456159 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -1,19 +1,13 @@ package fr.xephi.authme.datasource; -import ch.jalu.datasourcecolumns.data.DataSourceValue; -import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; -import ch.jalu.datasourcecolumns.data.DataSourceValues; -import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; import com.google.common.annotations.VisibleForTesting; import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory; -import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.HooksSettings; @@ -26,14 +20,10 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import static ch.jalu.datasourcecolumns.data.UpdateValues.with; -import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eq; -import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eqIgnoreCase; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; @@ -41,7 +31,7 @@ import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; * MySQL data source. */ @SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore -public class MySQL implements DataSource { +public class MySQL extends AbstractSqlDataSource { private boolean useSsl; private String host; @@ -54,7 +44,6 @@ public class MySQL implements DataSource { private int maxLifetime; private List columnOthers; private Columns col; - private AuthMeColumnsHandler columnsHandler; private MySqlExtension sqlExtension; private HikariDataSource ds; @@ -273,29 +262,6 @@ public class MySQL implements DataSource { } } - @Override - public boolean isAuthAvailable(String user) { - try { - return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); - } catch (SQLException e) { - logSqlException(e); - return false; - } - } - - @Override - public HashedPassword getPassword(String user) { - try { - DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); - if (values.rowExists()) { - return new HashedPassword(values.get(AuthMeColumns.PASSWORD), values.get(AuthMeColumns.SALT)); - } - } catch (SQLException e) { - logSqlException(e); - } - return null; - } - @Override public PlayerAuth getAuth(String user) { String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;"; @@ -318,9 +284,7 @@ public class MySQL implements DataSource { @Override public boolean saveAuth(PlayerAuth auth) { - columnsHandler.insert(auth, - AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, - AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP); + super.saveAuth(auth); try (Connection con = getConnection()) { if (!columnOthers.isEmpty()) { @@ -342,23 +306,6 @@ public class MySQL implements DataSource { return false; } - @Override - public boolean updatePassword(PlayerAuth auth) { - return updatePassword(auth.getNickname(), auth.getPassword()); - } - - @Override - public boolean updatePassword(String user, HashedPassword password) { - return columnsHandler.update(user, - with(AuthMeColumns.PASSWORD, password.getHash()) - .and(AuthMeColumns.SALT, password.getSalt()).build()); - } - - @Override - public boolean updateSession(PlayerAuth auth) { - return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); - } - @Override public Set getRecordsToPurge(long until) { Set list = new HashSet<>(); @@ -396,18 +343,6 @@ public class MySQL implements DataSource { return false; } - @Override - public boolean updateQuitLoc(PlayerAuth auth) { - return columnsHandler.update(auth, - AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, - AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); - } - - @Override - public boolean updateEmail(PlayerAuth auth) { - return columnsHandler.update(auth, AuthMeColumns.EMAIL); - } - @Override public void closeConnection() { if (ds != null && !ds.isClosed()) { @@ -415,21 +350,6 @@ public class MySQL implements DataSource { } } - @Override - public List getAllAuthsByIp(String ip) { - try { - return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); - } catch (SQLException e) { - logSqlException(e); - return Collections.emptyList(); - } - } - - @Override - public int countAuthsByEmail(String email) { - return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); - } - @Override public void purgeRecords(Collection toPurge) { String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; @@ -448,73 +368,6 @@ public class MySQL implements DataSource { return DataSourceType.MYSQL; } - @Override - public boolean isLogged(String user) { - try { - DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.IS_LOGGED); - return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); - } catch (SQLException e) { - logSqlException(e); - return false; - } - } - - @Override - public void setLogged(String user) { - columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 1); - } - - @Override - public void setUnlogged(String user) { - columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 0); - } - - @Override - public boolean hasSession(String user) { - try { - DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); - return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); - } catch (SQLException e) { - logSqlException(e); - return false; - } - } - - @Override - public void grantSession(String user) { - columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); - } - - @Override - public void revokeSession(String user) { - columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); - } - - @Override - public void purgeLogged() { - columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); - } - - @Override - public int getAccountsRegistered() { - return columnsHandler.count(new AlwaysTruePredicate<>()); - } - - @Override - public boolean updateRealName(String user, String realName) { - return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); - } - - @Override - public DataSourceValue getEmail(String user) { - try { - return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); - } catch (SQLException e) { - logSqlException(e); - return DataSourceValueImpl.unknownRow(); - } - } - @Override public List getAllAuths() { List auths = new ArrayList<>(); diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 5c78e0bbe..b2f639a37 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -1,15 +1,9 @@ package fr.xephi.authme.datasource; -import ch.jalu.datasourcecolumns.data.DataSourceValue; -import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; -import ch.jalu.datasourcecolumns.data.DataSourceValues; -import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; -import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -23,14 +17,10 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import static ch.jalu.datasourcecolumns.data.UpdateValues.with; -import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eq; -import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eqIgnoreCase; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; @@ -38,7 +28,7 @@ import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; * SQLite data source. */ @SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore -public class SQLite implements DataSource { +public class SQLite extends AbstractSqlDataSource { private final Settings settings; private final File dataFolder; @@ -46,7 +36,6 @@ public class SQLite implements DataSource { private final String tableName; private final Columns col; private Connection con; - private AuthMeColumnsHandler columnsHandler; /** * Constructor for SQLite. @@ -71,6 +60,7 @@ public class SQLite implements DataSource { ConsoleLogger.logException("Error during SQLite initialization:", ex); throw ex; } + this.columnsHandler = AuthMeColumnsHandler.createForSqlite(con, settings); } @VisibleForTesting @@ -215,29 +205,6 @@ public class SQLite implements DataSource { } } - @Override - public boolean isAuthAvailable(String user) { - try { - return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); - } catch (SQLException e) { - logSqlException(e); - return false; - } - } - - @Override - public HashedPassword getPassword(String user) { - try { - DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); - if (values.rowExists()) { - return new HashedPassword(values.get(AuthMeColumns.PASSWORD), values.get(AuthMeColumns.SALT)); - } - } catch (SQLException e) { - logSqlException(e); - } - return null; - } - @Override public PlayerAuth getAuth(String user) { String sql = "SELECT * FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);"; @@ -254,30 +221,6 @@ public class SQLite implements DataSource { return null; } - @Override - public boolean saveAuth(PlayerAuth auth) { - return columnsHandler.insert(auth, - AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, - AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP); - } - - @Override - public boolean updatePassword(PlayerAuth auth) { - return updatePassword(auth.getNickname(), auth.getPassword()); - } - - @Override - public boolean updatePassword(String user, HashedPassword password) { - return columnsHandler.update(user, - with(AuthMeColumns.PASSWORD, password.getHash()) - .and(AuthMeColumns.SALT, password.getSalt()).build()); - } - - @Override - public boolean updateSession(PlayerAuth auth) { - return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); - } - @Override public Set getRecordsToPurge(long until) { Set list = new HashSet<>(); @@ -325,18 +268,6 @@ public class SQLite implements DataSource { return false; } - @Override - public boolean updateQuitLoc(PlayerAuth auth) { - return columnsHandler.update(auth, - AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, - AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); - } - - @Override - public boolean updateEmail(PlayerAuth auth) { - return columnsHandler.update(auth, AuthMeColumns.EMAIL); - } - @Override public void closeConnection() { try { @@ -348,112 +279,11 @@ public class SQLite implements DataSource { } } - @Override - public List getAllAuthsByIp(String ip) { - try { - return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); - } catch (SQLException e) { - logSqlException(e); - return Collections.emptyList(); - } - } - - @Override - public int countAuthsByEmail(String email) { - return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); - } - @Override public DataSourceType getType() { return DataSourceType.SQLITE; } - @Override - public boolean isLogged(String user) { - String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, user); - try (ResultSet rs = pst.executeQuery()) { - if (rs.next()) { - return rs.getInt(col.IS_LOGGED) == 1; - } - } - } catch (SQLException ex) { - logSqlException(ex); - } - return false; - } - - @Override - public void setLogged(String user) { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE LOWER(" + col.NAME + ")=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 1); - pst.setString(2, user); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } - } - - @Override - public void setUnlogged(String user) { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE LOWER(" + col.NAME + ")=?;"; - try (PreparedStatement pst = con.prepareStatement(sql)) { - pst.setInt(1, 0); - pst.setString(2, user); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } - } - - @Override - public boolean hasSession(String user) { - try { - DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); - return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); - } catch (SQLException e) { - logSqlException(e); - return false; - } - } - - @Override - public void grantSession(String user) { - columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); - } - - @Override - public void revokeSession(String user) { - columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); - } - - @Override - public void purgeLogged() { - columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); - } - - @Override - public int getAccountsRegistered() { - return columnsHandler.count(new AlwaysTruePredicate<>()); - } - - @Override - public boolean updateRealName(String user, String realName) { - return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); - } - - @Override - public DataSourceValue getEmail(String user) { - try { - return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); - } catch (SQLException e) { - logSqlException(e); - return DataSourceValueImpl.unknownRow(); - } - } - @Override public List getAllAuths() { List auths = new ArrayList<>(); diff --git a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java index eb00d14e0..f02b5dc9b 100644 --- a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import fr.xephi.authme.data.captcha.CaptchaCodeStorage; +import fr.xephi.authme.datasource.AbstractSqlDataSource; import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.datasource.columnshandler.DataSourceColumn; import fr.xephi.authme.datasource.columnshandler.PlayerAuthColumn; @@ -62,6 +63,7 @@ public class ClassesConsistencyTest { private static final Set> CLASSES_EXCLUDED_FROM_VISIBILITY_TEST = ImmutableSet.of( Whirlpool.class, // not our implementation, so we don't touch it MySqlExtension.class, // has immutable protected fields used by all children + AbstractSqlDataSource.class, // protected members for inheritance Columns.class // uses non-static String constants, which is safe ); From f752b9d7a71b6bc0ee572f59ff54f8165abc5f15 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Mon, 26 Mar 2018 21:50:56 +0200 Subject: [PATCH 107/155] [CI-SKIP] Fix jenkins url in pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b6b1d4ec2..a5c49a3c4 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ jenkins - http://ci.codemc.org/job/AuthMeReloaded/ + http://ci.codemc.org/job/AuthMe/job/AuthMeReloaded/ From 68329a876172b53dabcff704be36692f601dadb9 Mon Sep 17 00:00:00 2001 From: RikoDEV Date: Fri, 30 Mar 2018 16:01:22 +0200 Subject: [PATCH 108/155] Update messages_pl.yml (#1548) --- src/main/resources/messages/messages_pl.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 4d9061dfd..cc512acc3 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -79,7 +79,7 @@ on_join_validation: country_banned: '&4Ten kraj jest zbanowany na tym serwerze' not_owner_error: '&cNie jesteś właścicielem tego konta, wybierz inny nick!' invalid_name_case: '&cPowinieneś dołączyć do serwera z nicku %valid, a nie %invalid.' - # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' + quick_command: '&cUżyłeś komendy zbyt szybko! Ponownie dołącz do serwera i poczekaj chwilę, zanim użyjesz dowolnej komendy.' # Email email: From fc54c0311b4f8245c4ef1bcf1de5a60576c6504d Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 2 Apr 2018 23:19:13 +0200 Subject: [PATCH 109/155] #1539 Columns handler: finishing touches - Add relocation rule for shading of the library - Fix SQLite connection not being refreshed on reload --- pom.xml | 5 ++++- src/main/java/fr/xephi/authme/datasource/SQLite.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 01034dc66..a76536998 100644 --- a/pom.xml +++ b/pom.xml @@ -258,6 +258,10 @@ ch.jalu.configme fr.xephi.authme.libs.ch.jalu.configme
    + + ch.jalu.datasourcecolumns + fr.xephi.authme.libs.ch.jalu.datasourcecolumns + com.zaxxer.hikari fr.xephi.authme.libs.com.zaxxer.hikari @@ -792,7 +796,6 @@ ch.jalu datasourcecolumns 0.1-SNAPSHOT - compile true diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index b2f639a37..b7ed2083f 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -60,7 +60,6 @@ public class SQLite extends AbstractSqlDataSource { ConsoleLogger.logException("Error during SQLite initialization:", ex); throw ex; } - this.columnsHandler = AuthMeColumnsHandler.createForSqlite(con, settings); } @VisibleForTesting @@ -86,6 +85,7 @@ public class SQLite extends AbstractSqlDataSource { ConsoleLogger.debug("SQLite driver loaded"); this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db"); + this.columnsHandler = AuthMeColumnsHandler.createForSqlite(con, settings); } /** From 106dea161151a686c014a33ebd0c91e37c92dd35 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 2 Apr 2018 23:43:52 +0200 Subject: [PATCH 110/155] Minor: fix JavaDoc warnings --- .../datasource/columnshandler/AuthMeColumnsHandler.java | 3 +++ .../authme/datasource/columnshandler/ConnectionSupplier.java | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java index 7b6657126..bb0d80b77 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java @@ -141,6 +141,7 @@ public final class AuthMeColumnsHandler { * @param column the column whose value should be retrieved * @param the column type * @return the result of the lookup + * @throws SQLException . */ public DataSourceValue retrieve(String name, DataSourceColumn column) throws SQLException { return internalHandler.retrieve(name.toLowerCase(), column); @@ -152,6 +153,7 @@ public final class AuthMeColumnsHandler { * @param name the account name to look up * @param columns the columns to retrieve * @return map-like object with the requested values + * @throws SQLException . */ public DataSourceValues retrieve(String name, DataSourceColumn... columns) throws SQLException { return internalHandler.retrieve(name.toLowerCase(), columns); @@ -164,6 +166,7 @@ public final class AuthMeColumnsHandler { * @param column the column to retrieve from the matching rows * @param the column's value type * @return the values of the matching rows + * @throws SQLException . */ public List retrieve(Predicate predicate, DataSourceColumn column) throws SQLException { return internalHandler.retrieve(predicate, column); diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java index 4d419a219..77fbe8f3a 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java @@ -10,7 +10,10 @@ import java.sql.SQLException; public interface ConnectionSupplier { /** - * @return connection object to the database + * Returns a connection to the database. + * + * @return the connection + * @throws SQLException . */ Connection get() throws SQLException; From 9326094d9c1620845b5aa837a683583b0df092c7 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Tue, 3 Apr 2018 00:12:25 +0200 Subject: [PATCH 111/155] #1141 Fix review remarks by @games647 - Use SHA512 to generate keys instead of default SHA1 - Declare google authenticator dependency as optional and add relocation rule --- pom.xml | 17 +++++++--------- .../fr/xephi/authme/message/MessageKey.java | 2 +- .../security/totp/TotpAuthenticator.java | 20 ++++++++++++------- .../security/totp/TotpAuthenticatorTest.java | 14 ++++++++++++- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 0cd2db1d5..703b9fa67 100644 --- a/pom.xml +++ b/pom.xml @@ -251,16 +251,8 @@ fr.xephi.authme.libs.com.google - ch.jalu.injector - fr.xephi.authme.libs.jalu.injector - - - ch.jalu.configme - fr.xephi.authme.libs.ch.jalu.configme - - - ch.jalu.datasourcecolumns - fr.xephi.authme.libs.ch.jalu.datasourcecolumns + ch.jalu + fr.xephi.authme.libs.ch.jalu com.zaxxer.hikari @@ -290,6 +282,10 @@ de.mkammerer fr.xephi.authme.libs.de.mkammerer + + com.warrenstrange + fr.xephi.authme.libs.com.warrenstrange + javax.inject fr.xephi.authme.libs.javax.inject @@ -482,6 +478,7 @@ com.warrenstrange googleauth 1.1.2 + true diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index 5d340964b..538b52598 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -197,7 +197,7 @@ public enum MessageKey { /** Your secret code is %code. You can scan it from here %url */ TWO_FACTOR_CREATE("two_factor.code_created", "%code", "%url"), - /** Please submit your two-factor authentication code with /2fa code <code>. */ + /** Please submit your two-factor authentication code with /2fa code <code>. */ TWO_FACTOR_CODE_REQUIRED("two_factor.code_required"), /** Two-factor authentication is already enabled for your account! */ diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java index d546df071..cffc09cd9 100644 --- a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java +++ b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java @@ -1,9 +1,11 @@ package fr.xephi.authme.security.totp; -import com.google.common.annotations.VisibleForTesting; import com.warrenstrange.googleauth.GoogleAuthenticator; +import com.warrenstrange.googleauth.GoogleAuthenticatorConfig; +import com.warrenstrange.googleauth.GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder; import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; +import com.warrenstrange.googleauth.HmacHashFunction; import com.warrenstrange.googleauth.IGoogleAuthenticator; import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; @@ -18,16 +20,20 @@ public class TotpAuthenticator { private final IGoogleAuthenticator authenticator; private final BukkitService bukkitService; - @Inject TotpAuthenticator(BukkitService bukkitService) { - this(new GoogleAuthenticator(), bukkitService); + this.authenticator = createGoogleAuthenticator(); + this.bukkitService = bukkitService; } - @VisibleForTesting - TotpAuthenticator(IGoogleAuthenticator authenticator, BukkitService bukkitService) { - this.authenticator = authenticator; - this.bukkitService = bukkitService; + /** + * @return new Google Authenticator instance + */ + protected IGoogleAuthenticator createGoogleAuthenticator() { + GoogleAuthenticatorConfig config = new GoogleAuthenticatorConfigBuilder() + .setHmacHashFunction(HmacHashFunction.HmacSHA512) + .build(); + return new GoogleAuthenticator(config); } /** diff --git a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java index b675ef8a0..27434cced 100644 --- a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java +++ b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java @@ -36,7 +36,7 @@ public class TotpAuthenticatorTest { @Before public void initializeTotpAuthenticator() { - totpAuthenticator = new TotpAuthenticator(googleAuthenticator, bukkitService); + totpAuthenticator = new TotpAuthenticatorTestImpl(bukkitService); } @Test @@ -85,4 +85,16 @@ public class TotpAuthenticatorTest { assertThat(result, equalTo(false)); verifyZeroInteractions(googleAuthenticator); } + + private final class TotpAuthenticatorTestImpl extends TotpAuthenticator { + + TotpAuthenticatorTestImpl(BukkitService bukkitService) { + super(bukkitService); + } + + @Override + protected IGoogleAuthenticator createGoogleAuthenticator() { + return googleAuthenticator; + } + } } From 1f9bf3875584516ef836239e9e56d65c1df48eb4 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 3 Apr 2018 09:45:27 -0600 Subject: [PATCH 112/155] Added EmailChangedEvent (#1549) * Added EmailChangedEvent * Fix failing tests Silly. * Documented the EmailChangedEvent * Separate messages for cancelled email event * Added lang todos for all the languages I can't speak I wish I could though. * Checkstyle satisfaction * Changed log level to info for cancelled events --- .../authme/data/VerificationCodeManager.java | 2 +- .../authme/events/EmailChangedEvent.java | 87 +++++++++++++++++++ .../fr/xephi/authme/message/MessageKey.java | 6 ++ .../authme/process/email/AsyncAddEmail.java | 12 +++ .../process/email/AsyncChangeEmail.java | 18 +++- src/main/resources/messages/messages_bg.yml | 2 + src/main/resources/messages/messages_br.yml | 2 + src/main/resources/messages/messages_cz.yml | 2 + src/main/resources/messages/messages_de.yml | 2 + src/main/resources/messages/messages_en.yml | 2 + src/main/resources/messages/messages_eo.yml | 2 + src/main/resources/messages/messages_es.yml | 2 + src/main/resources/messages/messages_et.yml | 2 + src/main/resources/messages/messages_eu.yml | 2 + src/main/resources/messages/messages_fi.yml | 2 + src/main/resources/messages/messages_fr.yml | 2 + src/main/resources/messages/messages_gl.yml | 2 + src/main/resources/messages/messages_hu.yml | 2 + src/main/resources/messages/messages_id.yml | 2 + src/main/resources/messages/messages_it.yml | 2 + src/main/resources/messages/messages_ko.yml | 2 + src/main/resources/messages/messages_lt.yml | 2 + src/main/resources/messages/messages_nl.yml | 2 + src/main/resources/messages/messages_pl.yml | 2 + src/main/resources/messages/messages_pt.yml | 2 + src/main/resources/messages/messages_ro.yml | 2 + src/main/resources/messages/messages_ru.yml | 8 +- src/main/resources/messages/messages_sk.yml | 2 + src/main/resources/messages/messages_tr.yml | 2 + src/main/resources/messages/messages_uk.yml | 4 +- src/main/resources/messages/messages_vn.yml | 2 + src/main/resources/messages/messages_zhcn.yml | 2 + src/main/resources/messages/messages_zhhk.yml | 2 + src/main/resources/messages/messages_zhmc.yml | 2 + src/main/resources/messages/messages_zhtw.yml | 2 + .../process/email/AsyncAddEmailTest.java | 34 ++++++++ .../process/email/AsyncChangeEmailTest.java | 40 ++++++++- 37 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/events/EmailChangedEvent.java diff --git a/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java b/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java index c5c2d7257..1cd176684 100644 --- a/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java +++ b/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java @@ -162,7 +162,7 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup { * * @param name the name of the player to generate a code for */ - public void verify(String name){ + public void verify(String name) { verifiedPlayers.add(name.toLowerCase()); } diff --git a/src/main/java/fr/xephi/authme/events/EmailChangedEvent.java b/src/main/java/fr/xephi/authme/events/EmailChangedEvent.java new file mode 100644 index 000000000..7d9468cab --- /dev/null +++ b/src/main/java/fr/xephi/authme/events/EmailChangedEvent.java @@ -0,0 +1,87 @@ +package fr.xephi.authme.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import javax.annotation.Nullable; + +/** + * This event is called when a player adds or changes his email address. + */ +public class EmailChangedEvent extends CustomEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private final String oldEmail; + private final String newEmail; + private boolean isCancelled; + + /** + * Constructor + * + * @param player The player that changed email + * @param oldEmail Old email player had on file. Can be null when user adds an email + * @param newEmail New email that player tries to set. In case of adding email, this will contain + * the email is trying to set. + * @param isAsync should this event be called asynchronously? + */ + public EmailChangedEvent(Player player, @Nullable String oldEmail, String newEmail, boolean isAsync) { + super(isAsync); + this.player = player; + this.oldEmail = oldEmail; + this.newEmail = newEmail; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + /** + * Gets the player who changes the email + * + * @return The player who changed the email + */ + public Player getPlayer() { + return player; + } + + /** + * Gets the old email in case user tries to change existing email. + * + * @return old email stored on file. Can be null when user never had an email and adds a new one. + */ + public @Nullable String getOldEmail() { + return this.oldEmail; + } + + /** + * Gets the new email. + * + * @return the email user is trying to set. If user adds email and never had one before, + * this is where such email can be found. + */ + public String getNewEmail() { + return this.newEmail; + } + + @Override + public void setCancelled(boolean cancelled) { + this.isCancelled = cancelled; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + /** + * Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link Event}. + * + * @return The list of handlers + */ + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index 355f14a9a..357e7d8cc 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -167,12 +167,18 @@ public enum MessageKey { /** Email address successfully added to your account! */ EMAIL_ADDED_SUCCESS("email.added"), + /** Adding email was not allowed */ + EMAIL_ADD_NOT_ALLOWED("email.add_not_allowed"), + /** Please confirm your email address! */ CONFIRM_EMAIL_MESSAGE("email.request_confirmation"), /** Email address changed correctly! */ EMAIL_CHANGED_SUCCESS("email.changed"), + /** Changing email was not allowed */ + EMAIL_CHANGE_NOT_ALLOWED("email.change_not_allowed"), + /** Your current email address is: %email */ EMAIL_SHOW("email.email_show", "%email"), diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java index 016d6169d..1896bfd3b 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java @@ -4,8 +4,10 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.EmailChangedEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.AsynchronousProcess; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.bungeecord.BungeeSender; @@ -35,6 +37,9 @@ public class AsyncAddEmail implements AsynchronousProcess { @Inject private BungeeSender bungeeSender; + @Inject + private BukkitService bukkitService; + AsyncAddEmail() { } /** @@ -57,6 +62,13 @@ public class AsyncAddEmail implements AsynchronousProcess { } else if (!validationService.isEmailFreeForRegistration(email, player)) { service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); } else { + EmailChangedEvent event = bukkitService.createAndCallEvent(isAsync + -> new EmailChangedEvent(player, null, email, isAsync)); + if (event.isCancelled()) { + ConsoleLogger.info("Could not add email to player '" + player + "' – event was cancelled"); + service.send(player, MessageKey.EMAIL_ADD_NOT_ALLOWED); + return; + } auth.setEmail(email); if (dataSource.updateEmail(auth)) { playerCache.updatePlayer(auth); diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java index 8edd94961..26a5da9e7 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java @@ -1,10 +1,13 @@ package fr.xephi.authme.process.email; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.EmailChangedEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.AsynchronousProcess; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.bungeecord.BungeeSender; @@ -32,6 +35,9 @@ public class AsyncChangeEmail implements AsynchronousProcess { @Inject private BungeeSender bungeeSender; + + @Inject + private BukkitService bukkitService; AsyncChangeEmail() { } @@ -57,14 +63,22 @@ public class AsyncChangeEmail implements AsynchronousProcess { } else if (!validationService.isEmailFreeForRegistration(newEmail, player)) { service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); } else { - saveNewEmail(auth, player, newEmail); + saveNewEmail(auth, player, oldEmail, newEmail); } } else { outputUnloggedMessage(player); } } - private void saveNewEmail(PlayerAuth auth, Player player, String newEmail) { + private void saveNewEmail(PlayerAuth auth, Player player, String oldEmail, String newEmail) { + EmailChangedEvent event = bukkitService.createAndCallEvent(isAsync + -> new EmailChangedEvent(player, oldEmail, newEmail, isAsync)); + if (event.isCancelled()) { + ConsoleLogger.info("Could not change email for player '" + player + "' – event was cancelled"); + service.send(player, MessageKey.EMAIL_CHANGE_NOT_ALLOWED); + return; + } + auth.setEmail(newEmail); if (dataSource.updateEmail(auth)) { playerCache.updatePlayer(auth); diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index 8b073bd96..3a6f34948 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -99,6 +99,8 @@ email: send_failure: 'Съобщението не беше изпратено. Моля свържете се с администратора.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' email_cooldown_error: '&cВече е бил изпратен имейл адрес. Трябва а изчакаш %time преди да пратиш нов.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 69f5e3954..69062b23c 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -102,6 +102,8 @@ email: send_failure: '&cO e-mail não pôde ser enviado, reporte isso a um administrador!' change_password_expired: 'Você não pode mais usar esse comando de recuperação de senha!' email_cooldown_error: '&cUm e-mail já foi enviado, espere mais %time antes de enviar novamente!' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index 5ed2ceb41..e3bd5c35f 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -99,6 +99,8 @@ email: send_failure: 'Email nemohl být odeslán. Kontaktujte prosím admina.' change_password_expired: 'Nemůžeš si změnit heslo pomocí toho příkazu.' email_cooldown_error: '&cEmail už byl nedávno odeslán. Musíš čekat %time před odesláním nového.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 84679c486..97639a9af 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -99,6 +99,8 @@ email: send_failure: 'Die E-Mail konnte nicht gesendet werden. Bitte kontaktiere einen Administrator.' change_password_expired: 'Mit diesem Befehl kannst du dein Passwort nicht mehr ändern.' email_cooldown_error: '&cEine E-Mail wurde erst kürzlich versendet. Du musst %time warten, bevor du eine neue anfordern kannst.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index 6d9d28798..971945afc 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -98,6 +98,8 @@ email: add_email_request: '&3Please add your email to your account with the command: /email add ' change_password_expired: 'You cannot change your password using this command anymore.' email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + add_not_allowed: '&cAdding email was not allowed' + change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_eo.yml b/src/main/resources/messages/messages_eo.yml index c5783c831..390acdca8 100644 --- a/src/main/resources/messages/messages_eo.yml +++ b/src/main/resources/messages/messages_eo.yml @@ -99,6 +99,8 @@ email: send_failure: 'La retpoŝto ne estis sendita. Bonvolu kontakti administranto.' change_password_expired: 'Vi ne povas ŝanĝi vian pasvorton per tiu ĉi komando plu.' email_cooldown_error: '&cRetmesaĝon jam sendita lastatempe. Vi devas atendi %time antaŭ vi povas sendi novan.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index 50363ead1..bde1f29ea 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -100,6 +100,8 @@ email: send_failure: 'No se ha podido enviar el correo electrónico. Por favor, contacta con un administrador.' change_password_expired: 'No puedes cambiar la contraseña utilizando este comando.' email_cooldown_error: '&cEl correo ha sido enviado recientemente. Debes esperar %time antes de volver a enviar uno nuevo.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_et.yml b/src/main/resources/messages/messages_et.yml index 68ce520f5..ba5e0b15e 100644 --- a/src/main/resources/messages/messages_et.yml +++ b/src/main/resources/messages/messages_et.yml @@ -99,6 +99,8 @@ email: send_failure: 'Meili ei õnnestunud saata. Kontakteeru meeskonnaga.' change_password_expired: '&3Enam ei saa vahetada oma parooli kasutades seda käsklust.' email_cooldown_error: '&cEmail juba saadeti. Sa pead ootama %time ennem, kui saad uuesti saata.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index 0507d1042..65bd5575f 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -99,6 +99,8 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index b0b4f3032..95d0a7320 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -99,6 +99,8 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index 52fd4c882..710da4d7f 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -102,6 +102,8 @@ email: send_failure: '&cLe mail n''a pas pu être envoyé. Veuillez contacter un admin.' change_password_expired: 'Vous ne pouvez pas changer votre mot de passe avec cette commande.' email_cooldown_error: '&cUn mail de récupération a déjà été envoyé récemment. Veuillez attendre %time pour le demander de nouveau.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index d22caa7cb..fb79b6646 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -99,6 +99,8 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index 1d420b746..72f8a15be 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -99,6 +99,8 @@ email: send_failure: 'Nem sikerült elküldeni az emailt. Lépj kapcsolatba egy adminnal.' change_password_expired: 'Ezzel a paranccsal már nem módosíthatja jelszavát.' email_cooldown_error: '&cEgy emailt már kiküldtünk. Következő email küldése előtt várnod kell: %time.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 65e84cd9c..5e5b3f3cd 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -99,6 +99,8 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index 22354cda8..70162b775 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -102,6 +102,8 @@ email: send_failure: 'Non è stato possibile inviare l''email di recupero. Per favore contatta un amministratore.' change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index 41049b884..5d875932e 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -101,6 +101,8 @@ email: send_failure: '이메일을 보낼 수 없습니다. 관리자에게 알려주세요.' change_password_expired: '더 이상 이 명령어를 통해 비밀번호를 변경할 수 없습니다.' email_cooldown_error: '&c이메일을 이미 발송했습니다. %time 후에 다시 발송할 수 있습니다.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index 15eea3048..3df013699 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -99,6 +99,8 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index b8fc40950..86f7b8ff2 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -99,6 +99,8 @@ email: send_failure: 'De e-mail kon niet verzonden worden. Neem contact op met een administrator.' change_password_expired: 'Je kunt je wachtwoord niet meer veranderen met dit commando.' email_cooldown_error: '&cEr is recent al een e-mail verzonden. Je moet %time wachten voordat je een nieuw bericht kunt versturen.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index cc512acc3..41272a082 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -99,6 +99,8 @@ email: send_failure: 'Nie można wysłać e-maila. Skontaktuj się z administracją.' change_password_expired: 'Nie zmienisz już hasła przy użyciu tej komendy.' email_cooldown_error: '&cE-mail został wysłany, musisz poczekać %time przed wysłaniem następnego.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index 2fb0ad2c4..b7cabdc8b 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -99,6 +99,8 @@ email: send_failure: 'Não foi possivel enviar o email. Por favor contate um administrador.' change_password_expired: 'Você não pode mais alterar a sua password usando este comando.' email_cooldown_error: '&cUm email já foi enviado recentemente.Por favor, espere %time antes de enviar novamente' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml index 6fcacb052..4d40556d1 100644 --- a/src/main/resources/messages/messages_ro.yml +++ b/src/main/resources/messages/messages_ro.yml @@ -99,6 +99,8 @@ email: send_failure: 'Email-ul nu a putut fi trimis. Ta rugam contactatezi un administrator.' change_password_expired: 'Nu mai iti poti schimba parola folosind aceasta comanda.' email_cooldown_error: '&cAi primit deja un mail pentru schimbarea parolei. Trebuie sa astepti %time inainte de a trimite unul nou.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 25a8ca032..81d5c6f6b 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -79,7 +79,7 @@ on_join_validation: country_banned: '&4Вход с IP-адресов вашей страны запрещён на этом сервере.' not_owner_error: 'Вы не являетесь владельцем данной уч. записи. Выберите себе другое имя!' invalid_name_case: 'Неверное имя! Зайдите под именем %valid, а не %invalid.' - # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' + quick_command: 'Вы вводили команды слишком часто! Пожалуйста заходите снова и вводите команды помедленнее.' # Email email: @@ -99,6 +99,8 @@ email: send_failure: 'Письмо не может быть отправлено. Свяжитесь в администратором.' change_password_expired: 'Больше нельзя сменить свой пароль, используя эту команду.' email_cooldown_error: '&cПисьмо было отправлено недавно. Подождите %time, прежде чем отправить новое.' + add_not_allowed: '&cДобавление электронной почты не было разрешено.' + change_not_allowed: '&cИзменение электронной почты не было разрешено.' # Password recovery by email recovery: @@ -117,8 +119,8 @@ captcha: usage_captcha: '&3Необходимо ввести текст с каптчи. Используйте «/captcha %captcha_code»' wrong_captcha: '&cНеверно! Используйте «/captcha %captcha_code».' valid_captcha: '&2Вы успешно решили каптчу!' - # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' - # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' + captcha_for_registration: 'Чтобы зарегистрироваться, решите каптчу используя команду: «/captcha %captcha_code»' + register_captcha_valid: '&2Вы успешно решили каптчу! Теперь вы можете зарегистрироваться командой «/register»' # Verification code verification: diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index 5af6e5030..2553fc0dd 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -105,6 +105,8 @@ email: send_failure: 'Email nemohol byť poslaný. Prosím kontaktuj Administrátora.' change_password_expired: 'Už nemôžeš zmeniť svoje heslo týmto príkazom.' email_cooldown_error: '&cEmail bol nedávno poslaný. Musíš počkať %time predtým ako ti pošleme nový.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index b9aff0ae9..fd7d5e292 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -99,6 +99,8 @@ email: send_failure: 'Eposta gonderilemedi. Yetkili ile iletisime gec.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' email_cooldown_error: '&cKisa bir sure once eposta gonderildi. Yeni bir eposta almak icin %time beklemelisin.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index 3ecf52905..d509d4ebe 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -95,10 +95,12 @@ email: # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' already_used: '&4До цієї електронної пошти прив’язано забагато акаунтів!' - incomplete_settings: '&4[AuthMe] Error: Не всі необхідні налаштування є встановленими, щоб надсилати електронну пошту. Будь ласка, повідомте адміністратора!' + incomplete_settings: '&4Не всі необхідні налаштування є встановленими, щоб надсилати електронну пошту. Будь ласка, повідомте адміністратора!' # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index 5e138f4f6..8fc170c04 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -99,6 +99,8 @@ email: send_failure: 'Không thể gửi thư. Vui lòng liên hệ với ban quản trị.' change_password_expired: '&cBạn không thể thay đổi mật khẩu bằng lệnh này từ nay.' email_cooldown_error: '&cMột bức thư đã được gửi gần đây. Bạn phải chờ %time trước khi có thể gửi một bức thư mới.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index 78c3c89b6..70193285b 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -99,6 +99,8 @@ email: send_failure: '邮件发送失败,请联系管理员' change_password_expired: '您不能使用此命令更改密码' email_cooldown_error: '&c邮件已在几分钟前发送,您需要等待 %time 后才能再次请求发送' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index 9d080748b..694d532eb 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -102,6 +102,8 @@ email: send_failure: '&8[&6用戶系統&8] &c電郵系統錯誤,請聯絡伺服器管理員。 &7(err: smtperr)' change_password_expired: '&8[&6用戶系統&8] 此指令已過期,請重新辦理。' email_cooldown_error: '&8[&6用戶系統&8] &c你已經辦理過重寄郵件,請等待 %time 後再嘗試吧。' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_zhmc.yml b/src/main/resources/messages/messages_zhmc.yml index a151a8cf8..c0b418025 100644 --- a/src/main/resources/messages/messages_zhmc.yml +++ b/src/main/resources/messages/messages_zhmc.yml @@ -99,6 +99,8 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index 43a7b5338..fa9946bf9 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -101,6 +101,8 @@ email: send_failure: '&b【AuthMe】&4無法傳送電子郵件,請聯絡管理員.' change_password_expired: '&b【AuthMe】&6您現在不能使用這個指令變更密碼了.' email_cooldown_error: '&b【AuthMe】&c電子郵件已經寄出了. 您只能在 %time 後才能傳送.' + # TODO add_not_allowed: '&cAdding email was not allowed' + # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java index b945e9f93..6d2fdd222 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java @@ -4,7 +4,9 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.EmailChangedEvent; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.bungeecord.BungeeSender; @@ -15,11 +17,13 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.function.Function; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; /** @@ -49,6 +53,9 @@ public class AsyncAddEmailTest { @Mock private BungeeSender bungeeSender; + @Mock + private BukkitService bukkitService; + @BeforeClass public static void setUp() { TestHelper.setupLogger(); @@ -66,6 +73,8 @@ public class AsyncAddEmailTest { given(dataSource.updateEmail(any(PlayerAuth.class))).willReturn(true); given(validationService.validateEmail(email)).willReturn(true); given(validationService.isEmailFreeForRegistration(email, player)).willReturn(true); + EmailChangedEvent event = spy(new EmailChangedEvent(player, null, email, false)); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(event); // when asyncAddEmail.addEmail(player, email); @@ -89,6 +98,8 @@ public class AsyncAddEmailTest { given(dataSource.updateEmail(any(PlayerAuth.class))).willReturn(false); given(validationService.validateEmail(email)).willReturn(true); given(validationService.isEmailFreeForRegistration(email, player)).willReturn(true); + EmailChangedEvent event = spy(new EmailChangedEvent(player, null, email, false)); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(event); // when asyncAddEmail.addEmail(player, email); @@ -184,4 +195,27 @@ public class AsyncAddEmailTest { verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); } + @Test + public void shouldNotAddOnCancelledEvent() { + // given + String email = "player@mail.tld"; + given(player.getName()).willReturn("TestName"); + given(playerCache.isAuthenticated("testname")).willReturn(true); + PlayerAuth auth = mock(PlayerAuth.class); + given(auth.getEmail()).willReturn(null); + given(playerCache.getAuth("testname")).willReturn(auth); + given(validationService.validateEmail(email)).willReturn(true); + given(validationService.isEmailFreeForRegistration(email, player)).willReturn(true); + EmailChangedEvent event = spy(new EmailChangedEvent(player, null, email, false)); + event.setCancelled(true); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(event); + + // when + asyncAddEmail.addEmail(player, email); + + // then + verify(service).send(player, MessageKey.EMAIL_ADD_NOT_ALLOWED); + verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); + } + } diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java index 4427c45d6..23fb01e69 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java @@ -3,7 +3,9 @@ package fr.xephi.authme.process.email; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.EmailChangedEvent; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.bungeecord.BungeeSender; @@ -14,10 +16,13 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.function.Function; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,6 +53,9 @@ public class AsyncChangeEmailTest { @Mock private BungeeSender bungeeSender; + @Mock + private BukkitService bukkitService; + @Test public void shouldChangeEmail() { // given @@ -59,7 +67,9 @@ public class AsyncChangeEmailTest { given(dataSource.updateEmail(auth)).willReturn(true); given(validationService.validateEmail(newEmail)).willReturn(true); given(validationService.isEmailFreeForRegistration(newEmail, player)).willReturn(true); - + EmailChangedEvent event = spy(new EmailChangedEvent(player, "old@mail.tld", newEmail, false)); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(event); + // when process.changeEmail(player, "old@mail.tld", newEmail); @@ -81,6 +91,8 @@ public class AsyncChangeEmailTest { given(dataSource.updateEmail(auth)).willReturn(true); given(validationService.validateEmail(newEmail)).willReturn(true); given(validationService.isEmailFreeForRegistration(newEmail, player)).willReturn(true); + EmailChangedEvent event = spy(new EmailChangedEvent(player, oldEmail, newEmail, false)); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(event); // when process.changeEmail(player, "old-mail@example.org", newEmail); @@ -102,6 +114,8 @@ public class AsyncChangeEmailTest { given(dataSource.updateEmail(auth)).willReturn(false); given(validationService.validateEmail(newEmail)).willReturn(true); given(validationService.isEmailFreeForRegistration(newEmail, player)).willReturn(true); + EmailChangedEvent event = spy(new EmailChangedEvent(player, "old@mail.tld", newEmail, false)); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(event); // when process.changeEmail(player, "old@mail.tld", newEmail); @@ -219,6 +233,30 @@ public class AsyncChangeEmailTest { verify(service).send(player, MessageKey.REGISTER_MESSAGE); } + @Test + public void shouldNotChangeOnCancelledEvent() { + // given + String newEmail = "new@example.com"; + String oldEmail = "old@example.com"; + given(player.getName()).willReturn("Username"); + given(playerCache.isAuthenticated("username")).willReturn(true); + PlayerAuth auth = authWithMail(oldEmail); + given(playerCache.getAuth("username")).willReturn(auth); + given(validationService.validateEmail(newEmail)).willReturn(true); + given(validationService.isEmailFreeForRegistration(newEmail, player)).willReturn(true); + EmailChangedEvent event = spy(new EmailChangedEvent(player, oldEmail, newEmail, false)); + event.setCancelled(true); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(event); + + // when + process.changeEmail(player, oldEmail, newEmail); + + // then + verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); + verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); + verify(service).send(player, MessageKey.EMAIL_CHANGE_NOT_ALLOWED); + } + private static PlayerAuth authWithMail(String email) { PlayerAuth auth = mock(PlayerAuth.class); when(auth.getEmail()).thenReturn(email); From 80538b4bb24f6cdf512a19cbff8fc86374a769bd Mon Sep 17 00:00:00 2001 From: games647 Date: Thu, 5 Apr 2018 15:16:46 +0200 Subject: [PATCH 113/155] Force english language during unit testing Fixes #1536 --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a76536998..a04861c22 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,8 @@ 2.20.1 - -Dfile.encoding=${project.build.sourceEncoding} @{argLine} + + -Dfile.encoding=${project.build.sourceEncoding} -Duser.language=en @{argLine} ${project.skipExtendedHashTests} From b56133fe8f4808bc122c25a954deeedd197d9c9f Mon Sep 17 00:00:00 2001 From: RatchetCinemaESP Date: Thu, 12 Apr 2018 15:58:28 +0200 Subject: [PATCH 114/155] Update messages_es.yml (#1553) Translated lines: - 83 - 103 - 104 --- src/main/resources/messages/messages_es.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index bde1f29ea..899631cce 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -80,7 +80,7 @@ on_join_validation: country_banned: '¡Tu país ha sido baneado de este servidor!' not_owner_error: 'No eres el propietario de esta cuenta. ¡Por favor, elije otro nombre!' invalid_name_case: 'Solo puedes unirte mediante el nombre de usuario %valid, no %invalid.' - # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' + quick_command: 'Has usado el comando demasiado rápido! Porfavor, entra al servidor de nuevo y espera un poco antes de usar cualquier comando.' # Email email: @@ -100,8 +100,8 @@ email: send_failure: 'No se ha podido enviar el correo electrónico. Por favor, contacta con un administrador.' change_password_expired: 'No puedes cambiar la contraseña utilizando este comando.' email_cooldown_error: '&cEl correo ha sido enviado recientemente. Debes esperar %time antes de volver a enviar uno nuevo.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' + add_not_allowed: '&cNo se permite añadir un Email' + change_not_allowed: '&cNo se permite el cambio de Email' # Password recovery by email recovery: From 71826db23d376a8d394499f8df3e9fdaf2776fb3 Mon Sep 17 00:00:00 2001 From: Maxetto Date: Fri, 13 Apr 2018 19:34:29 +0200 Subject: [PATCH 115/155] Update messages_it.yml --- src/main/resources/messages/messages_it.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index 70162b775..d99cae84c 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -4,8 +4,6 @@ # %username% - Sostituisce il nome dell'utente che riceve il messaggio. # %displayname% - Sostituisce il nickname (e i colori) dell'utente che riceve il messaggio. -# Registrazione - # Registration registration: disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.' @@ -82,7 +80,7 @@ on_join_validation: country_banned: '&4Il tuo paese è bandito da questo server!' not_owner_error: 'Non sei il proprietario di questo account. Per favore scegli un altro nome!' invalid_name_case: 'Dovresti entrare con questo nome utente "%valid", al posto di "%invalid".' - # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' + quick_command: 'Hai usato un comando troppo velocemente dal tuo accesso! Per favore, rientra nel server e aspetta un po'' di più prima di usare un qualsiasi comando.' # Email email: @@ -102,12 +100,12 @@ email: send_failure: 'Non è stato possibile inviare l''email di recupero. Per favore contatta un amministratore.' change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' + add_not_allowed: '&cNon hai il permesso di aggiungere un indirizzo email' + change_not_allowed: '&cNon hai il permesso di cambiare l''indirizzo email' # Password recovery by email recovery: - forgot_password_hint: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery ' + forgot_password_hint: '&3Hai dimenticato la tua password? Puoi recuperarla usando il comando: /email recovery ' command_usage: '&cUtilizzo: /email recovery ' email_sent: '&2Una email di recupero è stata appena inviata al tuo indirizzo email!' code: From 156260c7a9dd35753f83d108d4577ec7e1511078 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 10:51:00 +0200 Subject: [PATCH 116/155] Remove duplicated relocation pattern --- pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pom.xml b/pom.xml index 703b9fa67..eb8a0b2d1 100644 --- a/pom.xml +++ b/pom.xml @@ -295,10 +295,6 @@ org.bstats fr.xephi.authme.libs.org.bstats - - com.warrenstrange - fr.xephi.authme.libs.com.warrenstrange - From 5cc58da85f49e6b994fe73d04917709112b1233b Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 11:02:20 +0200 Subject: [PATCH 117/155] Update HikariCP --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21910e4ef..1688990cc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ | **Code quality:** | [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) | | **Jenkins CI:** | [![Jenkins Status](https://img.shields.io/website-up-down-green-red/http/shields.io.svg?label=ci.codemc.org)](https://ci.codemc.org/) [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded) ![Build Tests](https://img.shields.io/jenkins/t/https/ci.codemc.org/job/AuthMe/job/AuthMeReloaded.svg) | | **Other CIs:** | [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) | -| **Dependencies:** | [![Dependency Status](https://gemnasium.com/badges/github.com/AuthMe/AuthMeReloaded.svg)](https://gemnasium.com/github.com/AuthMe/AuthMeReloaded) | +| **Dependencies:** | [![Dependency Status](https://beta.gemnasium.com/badges/github.com/AuthMe/AuthMeReloaded.svg)](https://beta.gemnasium.com/projects/github.com/AuthMe/AuthMeReloaded) | ## Description diff --git a/pom.xml b/pom.xml index a04861c22..7182cfbbf 100644 --- a/pom.xml +++ b/pom.xml @@ -441,7 +441,7 @@ com.zaxxer HikariCP - 2.7.8 + 3.1.0 true From ba4ed7bdd97355ef24bdb72c8aaa961bce5f79b5 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 11:10:02 +0200 Subject: [PATCH 118/155] Update Mockito --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7182cfbbf..c971d156b 100644 --- a/pom.xml +++ b/pom.xml @@ -820,7 +820,7 @@ org.mockito mockito-core test - 2.16.0 + 2.18.0 hamcrest-core @@ -839,7 +839,7 @@ com.h2database h2 - 1.4.196 + 1.4.197 test From 6e16abc34e2d417982a80167e970c7e4d7012039 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 11:45:21 +0200 Subject: [PATCH 119/155] Don't purge users if unable to load permission data --- .../xephi/authme/listener/PlayerListener.java | 12 +++- .../authme/permission/PermissionsManager.java | 62 +++++++++++++++---- .../permission/handlers/LuckPermsHandler.java | 9 ++- .../handlers/PermissionHandler.java | 5 +- .../handlers/PermissionLoadUserException.java | 13 ++++ .../authme/task/purge/PurgeExecutor.java | 10 ++- .../fr/xephi/authme/task/purge/PurgeTask.java | 8 +-- 7 files changed, 85 insertions(+), 34 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 7857114e3..5b6327127 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -1,11 +1,13 @@ package fr.xephi.authme.listener; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.QuickCommandsProtectionManager; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.handlers.PermissionLoadUserException; import fr.xephi.authme.process.Management; import fr.xephi.authme.service.AntiBotService; import fr.xephi.authme.service.BukkitService; @@ -260,9 +262,13 @@ public class PlayerListener implements Listener { // Keep pre-UUID compatibility try { - permissionsManager.loadUserData(event.getUniqueId()); - } catch (NoSuchMethodError e) { - permissionsManager.loadUserData(name); + try { + permissionsManager.loadUserData(event.getUniqueId()); + } catch (NoSuchMethodError e) { + permissionsManager.loadUserData(name); + } + } catch (PermissionLoadUserException e) { + ConsoleLogger.logException("Unable to load the permission data of user " + name, e); } try { diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java index 55845f745..ec1fff9bb 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java @@ -8,6 +8,7 @@ import fr.xephi.authme.permission.handlers.BPermissionsHandler; import fr.xephi.authme.permission.handlers.LuckPermsHandler; import fr.xephi.authme.permission.handlers.PermissionHandler; import fr.xephi.authme.permission.handlers.PermissionHandlerException; +import fr.xephi.authme.permission.handlers.PermissionLoadUserException; import fr.xephi.authme.permission.handlers.PermissionsExHandler; import fr.xephi.authme.permission.handlers.VaultHandler; import fr.xephi.authme.permission.handlers.ZPermissionsHandler; @@ -110,7 +111,9 @@ public class PermissionsManager implements Reloadable { * Creates a permission handler for the provided permission systems if possible. * * @param type the permission systems type for which to create a corresponding permission handler + * * @return the permission handler, or {@code null} if not possible + * * @throws PermissionHandlerException during initialization of the permission handler */ private PermissionHandler createPermissionHandler(PermissionsSystemType type) throws PermissionHandlerException { @@ -228,8 +231,9 @@ public class PermissionsManager implements Reloadable { /** * Check if the given player has permission for the given permission node. * - * @param joiningPlayer The player to check + * @param joiningPlayer The player to check * @param permissionNode The permission node to verify + * * @return true if the player has permission, false otherwise */ public boolean hasPermission(JoiningPlayer joiningPlayer, PermissionNode permissionNode) { @@ -262,7 +266,7 @@ public class PermissionsManager implements Reloadable { * Check whether the offline player with the given name has permission for the given permission node. * This method is used as a last resort when nothing besides the name is known. * - * @param name The name of the player + * @param name The name of the player * @param permissionNode The permission node to verify * * @return true if the player has permission, false otherwise @@ -317,7 +321,7 @@ public class PermissionsManager implements Reloadable { * @param groupName The group name. * * @return True if the player is in the specified group, false otherwise. - * False is also returned if groups aren't supported by the used permissions system. + * False is also returned if groups aren't supported by the used permissions system. */ public boolean isInGroup(OfflinePlayer player, String groupName) { return isEnabled() && handler.isInGroup(player, groupName); @@ -330,7 +334,7 @@ public class PermissionsManager implements Reloadable { * @param groupName The name of the group. * * @return True if succeed, false otherwise. - * False is also returned if this feature isn't supported for the current permissions system. + * False is also returned if this feature isn't supported for the current permissions system. */ public boolean addGroup(OfflinePlayer player, String groupName) { if (!isEnabled() || StringUtils.isEmpty(groupName)) { @@ -346,7 +350,7 @@ public class PermissionsManager implements Reloadable { * @param groupNames The name of the groups to add. * * @return True if at least one group was added, false otherwise. - * False is also returned if this feature isn't supported for the current permissions system. + * False is also returned if this feature isn't supported for the current permissions system. */ public boolean addGroups(OfflinePlayer player, Collection groupNames) { // If no permissions system is used, return false @@ -373,7 +377,7 @@ public class PermissionsManager implements Reloadable { * @param groupName The name of the group. * * @return True if succeed, false otherwise. - * False is also returned if this feature isn't supported for the current permissions system. + * False is also returned if this feature isn't supported for the current permissions system. */ public boolean removeGroup(OfflinePlayer player, String groupName) { return isEnabled() && handler.removeFromGroup(player, groupName); @@ -386,7 +390,7 @@ public class PermissionsManager implements Reloadable { * @param groupNames The name of the groups to remove. * * @return True if at least one group was removed, false otherwise. - * False is also returned if this feature isn't supported for the current permissions system. + * False is also returned if this feature isn't supported for the current permissions system. */ public boolean removeGroups(OfflinePlayer player, Collection groupNames) { // If no permissions system is used, return false @@ -414,7 +418,7 @@ public class PermissionsManager implements Reloadable { * @param groupName The name of the group. * * @return True if succeed, false otherwise. - * False is also returned if this feature isn't supported for the current permissions system. + * False is also returned if this feature isn't supported for the current permissions system. */ public boolean setGroup(OfflinePlayer player, String groupName) { return isEnabled() && handler.setGroup(player, groupName); @@ -428,7 +432,7 @@ public class PermissionsManager implements Reloadable { * @param player The player to remove all groups from. * * @return True if succeed, false otherwise. - * False will also be returned if this feature isn't supported for the used permissions system. + * False will also be returned if this feature isn't supported for the used permissions system. */ public boolean removeAllGroups(OfflinePlayer player) { // If no permissions system is used, return false @@ -443,15 +447,47 @@ public class PermissionsManager implements Reloadable { return removeGroups(player, groupNames); } - public void loadUserData(UUID uuid) { - if(!isEnabled()) { + /** + * Loads the permission data of the given player. + * + * @param offlinePlayer the offline player. + * @return true if the load was successful. + */ + public boolean loadUserData(OfflinePlayer offlinePlayer) { + try { + try { + loadUserData(offlinePlayer.getUniqueId()); + } catch (NoSuchMethodError e) { + loadUserData(offlinePlayer.getName()); + } + } catch (PermissionLoadUserException e) { + ConsoleLogger.logException("Unable to load the permission data of user " + offlinePlayer.getName(), e); + return false; + } + return true; + } + + /** + * Loads the permission data of the given player unique identifier. + * + * @param uuid the {@link UUID} of the player. + * @throws PermissionLoadUserException if the action failed. + */ + public void loadUserData(UUID uuid) throws PermissionLoadUserException { + if (!isEnabled()) { return; } handler.loadUserData(uuid); } - public void loadUserData(String name) { - if(!isEnabled()) { + /** + * Loads the permission data of the given player name. + * + * @param name the name of the player. + * @throws PermissionLoadUserException if the action failed. + */ + public void loadUserData(String name) throws PermissionLoadUserException { + if (!isEnabled()) { return; } handler.loadUserData(name); diff --git a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java index f01f14c34..0d465ef3c 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java @@ -189,22 +189,21 @@ public class LuckPermsHandler implements PermissionHandler { } @Override - public void loadUserData(UUID uuid) { + public void loadUserData(UUID uuid) throws PermissionLoadUserException { try { luckPermsApi.getUserManager().loadUser(uuid).get(5, TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { - e.printStackTrace(); + throw new PermissionLoadUserException("Unable to load the permission data of the user " + uuid, e); } } @Override - public void loadUserData(String name) { + public void loadUserData(String name) throws PermissionLoadUserException { try { UUID uuid = luckPermsApi.getStorage().getUUID(name).get(5, TimeUnit.SECONDS); loadUserData(uuid); } catch (InterruptedException | ExecutionException | TimeoutException e) { - e.printStackTrace(); + throw new PermissionLoadUserException("Unable to load the permission data of the user " + name, e); } } - } diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java index fe3f54057..831bc583f 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java @@ -107,10 +107,9 @@ public interface PermissionHandler { */ PermissionsSystemType getPermissionSystem(); - default void loadUserData(UUID uuid) { + default void loadUserData(UUID uuid) throws PermissionLoadUserException { } - default void loadUserData(String name) { + default void loadUserData(String name) throws PermissionLoadUserException { } - } diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java b/src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java new file mode 100644 index 000000000..697b49182 --- /dev/null +++ b/src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java @@ -0,0 +1,13 @@ +package fr.xephi.authme.permission.handlers; + +import java.util.UUID; + +/** + * Exception thrown when a {@link PermissionHandler#loadUserData(UUID uuid)} request fails. + */ +public class PermissionLoadUserException extends Exception { + + public PermissionLoadUserException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java index a3e42f756..36c951ffc 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java @@ -49,7 +49,7 @@ public class PurgeExecutor { * players and names. * * @param players the players to purge - * @param names names to purge + * @param names names to purge */ public void executePurge(Collection players, Collection names) { // Purge other data @@ -212,15 +212,13 @@ public class PurgeExecutor { } for (OfflinePlayer offlinePlayer : cleared) { - try { - permissionsManager.loadUserData(offlinePlayer.getUniqueId()); - } catch (NoSuchMethodError e) { - permissionsManager.loadUserData(offlinePlayer.getName()); + if (!permissionsManager.loadUserData(offlinePlayer)) { + ConsoleLogger.warning("Unable to purge the permissions of user " + offlinePlayer + "!"); + continue; } permissionsManager.removeAllGroups(offlinePlayer); } ConsoleLogger.info("AutoPurge: Removed permissions from " + cleared.size() + " player(s)."); } - } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java index 27b424150..686bab86d 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java @@ -3,6 +3,7 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; +import fr.xephi.authme.permission.handlers.PermissionLoadUserException; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; @@ -73,10 +74,9 @@ class PurgeTask extends BukkitRunnable { OfflinePlayer offlinePlayer = offlinePlayers[nextPosition]; if (offlinePlayer.getName() != null && toPurge.remove(offlinePlayer.getName().toLowerCase())) { - try { - permissionsManager.loadUserData(offlinePlayer.getUniqueId()); - } catch (NoSuchMethodError e) { - permissionsManager.loadUserData(offlinePlayer.getName()); + if(!permissionsManager.loadUserData(offlinePlayer)) { + ConsoleLogger.warning("Unable to check if the user " + offlinePlayer.getName() + " can be purged!"); + continue; } if (!permissionsManager.hasPermissionOffline(offlinePlayer, PlayerStatePermission.BYPASS_PURGE)) { playerPortion.add(offlinePlayer); From d533f8e19c1af8d3788efb77ea0cb7a27263ec7f Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 12:09:07 +0200 Subject: [PATCH 120/155] Fix unit testing whoops --- .../xephi/authme/task/purge/PurgeTaskTest.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java b/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java index 93538c858..2ecf6a13a 100644 --- a/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java +++ b/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java @@ -216,18 +216,16 @@ public class PurgeTaskTest { private void setPermissionsBehavior() { given(permissionsManager.hasPermissionOffline(any(OfflinePlayer.class), eq(BYPASS_NODE))) - .willAnswer(new Answer() { - @Override - public Boolean answer(InvocationOnMock invocationOnMock) throws Throwable { - OfflinePlayer player = invocationOnMock.getArgument(0); - Boolean hasPermission = playerBypassAssignments.get(player); - if (hasPermission == null) { - throw new IllegalStateException("Unexpected check of '" + BYPASS_NODE - + "' with player = " + player); - } - return hasPermission; + .willAnswer((Answer) invocationOnMock -> { + OfflinePlayer player = invocationOnMock.getArgument(0); + Boolean hasPermission = playerBypassAssignments.get(player); + if (hasPermission == null) { + throw new IllegalStateException("Unexpected check of '" + BYPASS_NODE + + "' with player = " + player); } + return hasPermission; }); + given(permissionsManager.loadUserData(any(OfflinePlayer.class))).willReturn(true); } private void assertRanPurgeWithPlayers(OfflinePlayer... players) { From bebff1c0c87d2302d82335768eeed80f897e305f Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 12:17:08 +0200 Subject: [PATCH 121/155] Actually provide a config to circleci Epic fail xD --- .circleci/{circle.yml => config.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .circleci/{circle.yml => config.yml} (100%) diff --git a/.circleci/circle.yml b/.circleci/config.yml similarity index 100% rename from .circleci/circle.yml rename to .circleci/config.yml From 65a1438c479b600161dce827a27ce49e603b5078 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 12:19:59 +0200 Subject: [PATCH 122/155] Fix circleci config format --- .circleci/config.yml | 92 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cb1e2ba61..b544c67ad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,49 +1,49 @@ version: 2 jobs: - build_and_test_jdk8: - working_directory: ~/authmereloaded-jdk8 - docker: - - image: circleci/openjdk:8-jdk - environment: - MAVEN_OPTS: -Xmx2048m - steps: - - checkout - - restore_cache: - keys: - - authmereloaded-{{ checksum "pom.xml" }} - - authmereloaded- - - run: mvn -T 2 dependency:go-offline - - save_cache: - paths: - - ~/.m2 - key: authmereloaded-{{ checksum "pom.xml" }} - - run: mvn -T 2 package - - store_test_results: - path: target/surefire-reports - - store_artifacts: - path: target/*.jar - build_and_test_jdk9: - working_directory: ~/authmereloaded-jdk9 - docker: - - image: circleci/openjdk:9-jdk - environment: - MAVEN_OPTS: -Xmx2048m - steps: - - checkout - - restore_cache: - key: authmereloaded-{{ checksum "pom.xml" }} - - run: mvn -T 2 dependency:go-offline - - save_cache: - paths: - - ~/.m2 - key: authmereloaded-{{ checksum "pom.xml" }} - - run: mvn -T 2 package - - store_test_results: - path: target/surefire-reports - - run: cp ./target/*.jar $CIRCLE_ARTIFACTS + build_and_test_jdk8: + working_directory: ~/authmereloaded-jdk8 + docker: + - image: circleci/openjdk:8-jdk + environment: + MAVEN_OPTS: -Xmx2048m + steps: + - checkout + - restore_cache: + keys: + - authmereloaded-{{ checksum "pom.xml" }} + - authmereloaded- + - run: mvn -T 2 dependency:go-offline + - save_cache: + paths: + - ~/.m2 + key: authmereloaded-{{ checksum "pom.xml" }} + - run: mvn -T 2 package + - store_test_results: + path: target/surefire-reports + - store_artifacts: + path: target/*.jar + build_and_test_jdk9: + working_directory: ~/authmereloaded-jdk9 + docker: + - image: circleci/openjdk:9-jdk + environment: + MAVEN_OPTS: -Xmx2048m + steps: + - checkout + - restore_cache: + key: authmereloaded-{{ checksum "pom.xml" }} + - run: mvn -T 2 dependency:go-offline + - save_cache: + paths: + - ~/.m2 + key: authmereloaded-{{ checksum "pom.xml" }} + - run: mvn -T 2 package + - store_test_results: + path: target/surefire-reports + - run: cp ./target/*.jar $CIRCLE_ARTIFACTS workflows: - version: 2 - build_and_test: - jobs: - - build_and_test_jdk8 - - build_and_test_jdk9 + version: 2 + build_and_test: + jobs: + - build_and_test_jdk8 + - build_and_test_jdk9 From 8722a3dbab42454e4423f82c15e522912478c5af Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 12:29:30 +0200 Subject: [PATCH 123/155] Improve circle configuration file --- .circleci/config.yml | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b544c67ad..3fbbaae46 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,41 +9,44 @@ jobs: steps: - checkout - restore_cache: - keys: + keys: - authmereloaded-{{ checksum "pom.xml" }} - authmereloaded- - run: mvn -T 2 dependency:go-offline - save_cache: - paths: - - ~/.m2 - key: authmereloaded-{{ checksum "pom.xml" }} + paths: + - ~/.m2 + key: authmereloaded-{{ checksum "pom.xml" }} - run: mvn -T 2 package - store_test_results: - path: target/surefire-reports + path: target/surefire-reports - store_artifacts: - path: target/*.jar - build_and_test_jdk9: - working_directory: ~/authmereloaded-jdk9 + path: target/*.jar + build_and_test_jdk10: + working_directory: ~/authmereloaded-jdk10 docker: - - image: circleci/openjdk:9-jdk + - image: circleci/openjdk:10-jdk environment: - MAVEN_OPTS: -Xmx2048m + MAVEN_OPTS: -Xmx2048m steps: - checkout - restore_cache: - key: authmereloaded-{{ checksum "pom.xml" }} + keys: + - authmereloaded-{{ checksum "pom.xml" }} + - authmereloaded- - run: mvn -T 2 dependency:go-offline - save_cache: - paths: - - ~/.m2 - key: authmereloaded-{{ checksum "pom.xml" }} + paths: + - ~/.m2 + key: authmereloaded-{{ checksum "pom.xml" }} - run: mvn -T 2 package - store_test_results: - path: target/surefire-reports - - run: cp ./target/*.jar $CIRCLE_ARTIFACTS + path: target/surefire-reports + - store_artifacts: + path: target/*.jar workflows: version: 2 build_and_test: jobs: - build_and_test_jdk8 - - build_and_test_jdk9 + - build_and_test_jdk10 From 65ad91372e05c9ac3525b58cbf347919ce852b3f Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Thu, 19 Apr 2018 12:32:50 +0200 Subject: [PATCH 124/155] Fix JDK 10 surefire plugin, use batch mode in circleci --- .circleci/config.yml | 8 ++++---- pom.xml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3fbbaae46..c988f79c4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,12 +12,12 @@ jobs: keys: - authmereloaded-{{ checksum "pom.xml" }} - authmereloaded- - - run: mvn -T 2 dependency:go-offline + - run: mvn -T 2 -B dependency:go-offline - save_cache: paths: - ~/.m2 key: authmereloaded-{{ checksum "pom.xml" }} - - run: mvn -T 2 package + - run: mvn -T 2 -B package - store_test_results: path: target/surefire-reports - store_artifacts: @@ -34,12 +34,12 @@ jobs: keys: - authmereloaded-{{ checksum "pom.xml" }} - authmereloaded- - - run: mvn -T 2 dependency:go-offline + - run: mvn -T 2 -B dependency:go-offline - save_cache: paths: - ~/.m2 key: authmereloaded-{{ checksum "pom.xml" }} - - run: mvn -T 2 package + - run: mvn -T 2 -B package - store_test_results: path: target/surefire-reports - store_artifacts: diff --git a/pom.xml b/pom.xml index c971d156b..c6756e67c 100644 --- a/pom.xml +++ b/pom.xml @@ -169,7 +169,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.20.1 + 2.21.0 From 5194f76f39b4d8ad5d01a4c0db00dd5725bdb6d7 Mon Sep 17 00:00:00 2001 From: RikoDEV Date: Sat, 21 Apr 2018 02:24:27 +0200 Subject: [PATCH 125/155] Update of the Polish translation by RikoDEV (#1560) --- src/main/resources/messages/messages_pl.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 41272a082..6184f8e22 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -99,8 +99,8 @@ email: send_failure: 'Nie można wysłać e-maila. Skontaktuj się z administracją.' change_password_expired: 'Nie zmienisz już hasła przy użyciu tej komendy.' email_cooldown_error: '&cE-mail został wysłany, musisz poczekać %time przed wysłaniem następnego.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' + add_not_allowed: '&cMożliwość dodania adresu e-mail jest wyłączona.' + change_not_allowed: '&cMożliwość zmiany adresu e-mail jest wyłączona.' # Password recovery by email recovery: From baec0349097c1b88ee44d1b680ee5b57888f435d Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sat, 21 Apr 2018 13:02:14 +0200 Subject: [PATCH 126/155] #1555 Add RegisterEvent and AuthMeAsyncPreRegisterEvent (#1559) * #1555 Add RegisterEvent and AuthMeAsyncPreRegisterEvent * Add missing javadoc --- .../events/AuthMeAsyncPreRegisterEvent.java | 70 +++++++++++++++++++ .../fr/xephi/authme/events/RegisterEvent.java | 47 +++++++++++++ .../process/register/AsyncRegister.java | 30 ++++++-- .../register/ProcessSyncEmailRegister.java | 6 ++ .../register/ProcessSyncPasswordRegister.java | 6 ++ .../process/login/AsynchronousLoginTest.java | 9 +-- .../process/register/AsyncRegisterTest.java | 34 +++++++++ 7 files changed, 191 insertions(+), 11 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java create mode 100644 src/main/java/fr/xephi/authme/events/RegisterEvent.java diff --git a/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java b/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java new file mode 100644 index 000000000..af26ad518 --- /dev/null +++ b/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java @@ -0,0 +1,70 @@ +package fr.xephi.authme.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * This event is called when a player uses the register command, + * it's fired even when a user does a /register with invalid arguments. + * {@link #setCanRegister(boolean) event.setCanRegister(false)} prevents the player from registering. + */ +public class AuthMeAsyncPreRegisterEvent extends CustomEvent { + + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private boolean canRegister = true; + + /** + * Constructor. + * + * @param player The player + * @param isAsync True if the event is async, false otherwise + */ + public AuthMeAsyncPreRegisterEvent(Player player, boolean isAsync) { + super(isAsync); + this.player = player; + } + + /** + * Return the player concerned by this event. + * + * @return The player who executed a valid {@code /login} command + */ + public Player getPlayer() { + return player; + } + + /** + * Return whether the player is allowed to register. + * + * @return True if the player can log in, false otherwise + */ + public boolean canRegister() { + return canRegister; + } + + /** + * Define whether or not the player may register. + * + * @param canRegister True to allow the player to log in; false to prevent him + */ + public void setCanRegister(boolean canRegister) { + this.canRegister = canRegister; + } + + /** + * Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link Event}. + * + * @return The list of handlers + */ + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/fr/xephi/authme/events/RegisterEvent.java b/src/main/java/fr/xephi/authme/events/RegisterEvent.java new file mode 100644 index 000000000..2a98d0544 --- /dev/null +++ b/src/main/java/fr/xephi/authme/events/RegisterEvent.java @@ -0,0 +1,47 @@ +package fr.xephi.authme.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * Event fired when a player has successfully registered. + */ +public class RegisterEvent extends CustomEvent { + + private static final HandlerList handlers = new HandlerList(); + private final Player player; + + /** + * Constructor. + * + * @param player The player + */ + public RegisterEvent(Player player) { + this.player = player; + } + + /** + * Return the player that has successfully logged in or registered. + * + * @return The player + */ + public Player getPlayer() { + return player; + } + + /** + * Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link Event}. + * + * @return The list of handlers + */ + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} 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 d50fe9a32..78eaa5679 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -4,14 +4,17 @@ import ch.jalu.injector.factory.SingletonStore; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.AuthMeAsyncPreRegisterEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.AsynchronousProcess; 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.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.bungeecord.BungeeSender; import fr.xephi.authme.service.bungeecord.MessageType; +import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.util.PlayerUtils; @@ -32,6 +35,8 @@ public class AsyncRegister implements AsynchronousProcess { @Inject private PlayerCache playerCache; @Inject + private BukkitService bukkitService; + @Inject private CommonService service; @Inject private SingletonStore registrationExecutorFactory; @@ -44,9 +49,9 @@ public class AsyncRegister implements AsynchronousProcess { /** * Performs the registration process for the given player. * - * @param variant the registration method + * @param variant the registration method * @param parameters the parameters - * @param

    parameters type + * @param

    parameters type */ public

    void register(RegistrationMethod

    variant, P parameters) { if (preRegisterCheck(parameters.getPlayer())) { @@ -57,6 +62,13 @@ public class AsyncRegister implements AsynchronousProcess { } } + /** + * Checks if the player is able to register, in that case the {@link AuthMeAsyncPreRegisterEvent} is invoked. + * + * @param player the player which is trying to register. + * + * @return true if the checks are successful and the event hasn't marked the action as denied, false otherwise. + */ private boolean preRegisterCheck(Player player) { final String name = player.getName().toLowerCase(); if (playerCache.isAuthenticated(name)) { @@ -70,6 +82,13 @@ public class AsyncRegister implements AsynchronousProcess { return false; } + boolean isAsync = service.getProperty(PluginSettings.USE_ASYNC_TASKS); + AuthMeAsyncPreRegisterEvent event = new AuthMeAsyncPreRegisterEvent(player, isAsync); + bukkitService.callEvent(event); + if (!event.canRegister()) { + return false; + } + return isPlayerIpAllowedToRegister(player); } @@ -77,11 +96,11 @@ public class AsyncRegister implements AsynchronousProcess { * Executes the registration. * * @param parameters the registration parameters - * @param executor the executor to perform the registration process with - * @param

    registration params type + * @param executor the executor to perform the registration process with + * @param

    registration params type */ private

    - void executeRegistration(P parameters, RegistrationExecutor

    executor) { + void executeRegistration(P parameters, RegistrationExecutor

    executor) { PlayerAuth auth = executor.buildPlayerAuth(parameters); if (database.saveAuth(auth)) { executor.executePostPersistAction(parameters); @@ -95,6 +114,7 @@ public class AsyncRegister implements AsynchronousProcess { * Checks whether the registration threshold has been exceeded for the given player's IP address. * * @param player the player to check + * * @return true if registration may take place, false otherwise (IP check failed) */ private boolean isPlayerIpAllowedToRegister(Player player) { diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java index 6ab4a874a..e740a0ded 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java @@ -2,8 +2,10 @@ package fr.xephi.authme.process.register; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.limbo.LimboService; +import fr.xephi.authme.events.RegisterEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.SynchronousProcess; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.util.PlayerUtils; import org.bukkit.entity.Player; @@ -15,6 +17,9 @@ import javax.inject.Inject; */ public class ProcessSyncEmailRegister implements SynchronousProcess { + @Inject + private BukkitService bukkitService; + @Inject private CommonService service; @@ -34,6 +39,7 @@ public class ProcessSyncEmailRegister implements SynchronousProcess { limboService.replaceTasksAfterRegistration(player); player.saveData(); + bukkitService.callEvent(new RegisterEvent(player)); ConsoleLogger.fine(player.getName() + " registered " + PlayerUtils.getPlayerIp(player)); } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index 30e7f59ae..dc8aa136f 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -2,8 +2,10 @@ package fr.xephi.authme.process.register; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.limbo.LimboService; +import fr.xephi.authme.events.RegisterEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.SynchronousProcess; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.bungeecord.BungeeSender; import fr.xephi.authme.settings.commandconfig.CommandManager; @@ -31,6 +33,9 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { @Inject private CommandManager commandManager; + @Inject + private BukkitService bukkitService; + ProcessSyncPasswordRegister() { } @@ -60,6 +65,7 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { } player.saveData(); + bukkitService.callEvent(new RegisterEvent(player)); ConsoleLogger.fine(player.getName() + " registered " + PlayerUtils.getPlayerIp(player)); // Kick Player after Registration is enabled, kick the player diff --git a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java index 9f21b7ba4..9b57fb318 100644 --- a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java +++ b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java @@ -156,12 +156,9 @@ public class AsynchronousLoginTest { given(commonService.getProperty(DatabaseSettings.MYSQL_COL_GROUP)).willReturn(""); given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); doReturn(false).when(asynchronousLogin).hasReachedMaxLoggedInPlayersForIp(any(Player.class), anyString()); - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - ((AuthMeAsyncPreLoginEvent) invocation.getArgument(0)).setCanLogin(false); - return null; - } + doAnswer((Answer) invocation -> { + ((AuthMeAsyncPreLoginEvent) invocation.getArgument(0)).setCanLogin(false); + return null; }).when(bukkitService).callEvent(any(AuthMeAsyncPreLoginEvent.class)); // when 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 77f91ca88..59c384bb1 100644 --- a/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java +++ b/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java @@ -4,12 +4,15 @@ import ch.jalu.injector.factory.SingletonStore; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.AuthMeAsyncPreRegisterEvent; import fr.xephi.authme.message.MessageKey; 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.BukkitService; import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.entity.Player; @@ -18,9 +21,11 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; @@ -40,6 +45,8 @@ public class AsyncRegisterTest { @Mock private CommonService commonService; @Mock + private BukkitService bukkitService; + @Mock private DataSource dataSource; @Mock private SingletonStore registrationExecutorStore; @@ -102,6 +109,32 @@ public class AsyncRegisterTest { @Test @SuppressWarnings("unchecked") public void shouldStopForFailedExecutorCheck() { + // given + String name = "edbert"; + Player player = mockPlayerWithName(name); + TestHelper.mockPlayerIp(player, "33.44.55.66"); + given(playerCache.isAuthenticated(name)).willReturn(false); + given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true); + given(dataSource.isAuthAvailable(name)).willReturn(false); + given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); + RegistrationExecutor executor = mock(RegistrationExecutor.class); + TwoFactorRegisterParams params = TwoFactorRegisterParams.of(player); + singletonStoreWillReturn(registrationExecutorStore, executor); + doAnswer((Answer) invocation -> { + ((AuthMeAsyncPreRegisterEvent) invocation.getArgument(0)).setCanRegister(false); + return null; + }).when(bukkitService).callEvent(any(AuthMeAsyncPreRegisterEvent.class)); + + // when + asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, params); + + // then + verify(dataSource, only()).isAuthAvailable(name); + } + + @Test + @SuppressWarnings("unchecked") + public void shouldStopForCancelledEvent() { // given String name = "edbert"; Player player = mockPlayerWithName(name); @@ -110,6 +143,7 @@ public class AsyncRegisterTest { given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true); given(commonService.getProperty(RestrictionSettings.MAX_REGISTRATION_PER_IP)).willReturn(0); given(dataSource.isAuthAvailable(name)).willReturn(false); + given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); RegistrationExecutor executor = mock(RegistrationExecutor.class); TwoFactorRegisterParams params = TwoFactorRegisterParams.of(player); given(executor.isRegistrationAdmitted(params)).willReturn(false); From 29ac3a702207ac080980c9f132716dcdc1e4c4b3 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 22 Apr 2018 11:13:27 +0200 Subject: [PATCH 127/155] #1141 Fixes to TOTP implementation - Revert back to SHA1 as HMAC hash function so that it works with Google authenticator - Add message to user to tell him to run /2fa confirm to add a TOTP code --- docs/commands.md | 13 +++- docs/config.md | 13 +++- docs/permission_nodes.md | 7 ++- docs/translations.md | 62 +++++++++---------- .../authme/command/CommandInitializer.java | 3 +- .../executable/totp/AddTotpCommand.java | 1 + .../fr/xephi/authme/message/MessageKey.java | 25 ++++---- .../security/totp/TotpAuthenticator.java | 8 +-- src/main/resources/messages/messages_bg.yml | 18 +++++- src/main/resources/messages/messages_br.yml | 18 +++++- src/main/resources/messages/messages_cz.yml | 18 +++++- src/main/resources/messages/messages_de.yml | 18 +++++- src/main/resources/messages/messages_en.yml | 1 + src/main/resources/messages/messages_eo.yml | 18 +++++- src/main/resources/messages/messages_es.yml | 18 +++++- src/main/resources/messages/messages_et.yml | 18 +++++- src/main/resources/messages/messages_eu.yml | 18 +++++- src/main/resources/messages/messages_fi.yml | 18 +++++- src/main/resources/messages/messages_fr.yml | 18 +++++- src/main/resources/messages/messages_gl.yml | 18 +++++- src/main/resources/messages/messages_hu.yml | 18 +++++- src/main/resources/messages/messages_id.yml | 18 +++++- src/main/resources/messages/messages_it.yml | 18 +++++- src/main/resources/messages/messages_ko.yml | 18 +++++- src/main/resources/messages/messages_lt.yml | 18 +++++- src/main/resources/messages/messages_nl.yml | 18 +++++- src/main/resources/messages/messages_pl.yml | 18 +++++- src/main/resources/messages/messages_pt.yml | 18 +++++- src/main/resources/messages/messages_ro.yml | 18 +++++- src/main/resources/messages/messages_ru.yml | 18 +++++- src/main/resources/messages/messages_sk.yml | 18 +++++- src/main/resources/messages/messages_tr.yml | 18 +++++- src/main/resources/messages/messages_uk.yml | 18 +++++- src/main/resources/messages/messages_vn.yml | 18 +++++- src/main/resources/messages/messages_zhcn.yml | 18 +++++- src/main/resources/messages/messages_zhhk.yml | 18 +++++- src/main/resources/messages/messages_zhmc.yml | 18 +++++- src/main/resources/messages/messages_zhtw.yml | 18 +++++- src/main/resources/plugin.yml | 4 +- 39 files changed, 510 insertions(+), 149 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 5bb49c2b9..163399c0e 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,5 +1,5 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` @@ -85,6 +85,15 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). - **/changepassword** <oldPassword> <newPassword>: Command to change your password using AuthMeReloaded.
    Requires `authme.player.changepassword` - **/changepassword help** [query]: View detailed help for /changepassword commands. +- **/totp**: Performs actions related to two-factor authentication. +- **/totp code** <code>: Processes the two-factor authentication code during login. +- **/totp add**: Enables two-factor authentication for your account. +
    Requires `authme.player.totpadd` +- **/totp confirm** <code>: Saves the generated TOTP secret after confirmation. +
    Requires `authme.player.totpadd` +- **/totp remove** <code>: Disables two-factor authentication for your account. +
    Requires `authme.player.totpremove` +- **/totp help** [query]: View detailed help for /totp commands. - **/captcha** <captcha>: Captcha command for AuthMeReloaded.
    Requires `authme.player.captcha` - **/captcha help** [query]: View detailed help for /captcha commands. @@ -95,4 +104,4 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Feb 02 20:09:14 CET 2018 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:00:10 CEST 2018 diff --git a/docs/config.md b/docs/config.md index 956da56a2..51ee86a9c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,5 +1,5 @@ - + ## AuthMe Configuration The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, @@ -43,6 +43,8 @@ DataSource: mySQLColumnLogged: 'isLogged' # Column for storing if a player has a valid session or not mySQLColumnHasSession: 'hasSession' + # Column for storing a player's TOTP key (for two-factor authentication) + mySQLtotpKey: 'totp' # Column for storing the player's last IP mySQLColumnIp: 'ip' # Column for storing players lastlogins @@ -138,6 +140,8 @@ settings: - '/reg' - '/email' - '/captcha' + - '/2fa' + - '/totp' # Max number of allowed registrations per IP # The value 0 means an unlimited number of registrations! maxRegPerIp: 1 @@ -384,7 +388,7 @@ Protection: # Apply the protection also to registered usernames enableProtectionRegistered: true # Countries allowed to join the server and register. For country codes, see - # http://dev.maxmind.com/geoip/legacy/codes/iso3166/ + # https://dev.maxmind.com/geoip/legacy/codes/iso3166/ # PLEASE USE QUOTES! countries: - 'US' @@ -404,6 +408,9 @@ Protection: antiBotDuration: 10 # Delay in seconds before the antibot activation antiBotDelay: 60 + quickCommands: + # Kicks the player that issued a command before the defined time after the join process + denyCommandsBeforeMilliseconds: 1000 Purge: # If enabled, AuthMe automatically purges old, unused accounts useAutoPurge: false @@ -555,4 +562,4 @@ To change settings on a running server, save your changes to config.yml and use --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Jan 21 18:49:44 CET 2018 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:00:10 CEST 2018 diff --git a/docs/permission_nodes.md b/docs/permission_nodes.md index f3b5edac4..828d23b53 100644 --- a/docs/permission_nodes.md +++ b/docs/permission_nodes.md @@ -1,5 +1,5 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. @@ -57,13 +57,16 @@ The following are the permission nodes that are currently supported by the lates - **authme.player.email.see** – Command permission to see the own email address. - **authme.player.login** – Command permission to login. - **authme.player.logout** – Command permission to logout. +- **authme.player.protection.quickcommandsprotection** – Permission that enables on join quick commands checks for the player. - **authme.player.register** – Command permission to register. - **authme.player.security.verificationcode** – Permission to use the email verification codes feature. - **authme.player.seeownaccounts** – Permission to use to see own other accounts. +- **authme.player.totpadd** – Permission to enable two-factor authentication. +- **authme.player.totpremove** – Permission to disable two-factor authentication. - **authme.player.unregister** – Command permission to unregister. - **authme.vip** – When the server is full and someone with this permission joins the server, someone will be kicked. --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Dec 01 19:16:17 CET 2017 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:00:13 CEST 2018 diff --git a/docs/translations.md b/docs/translations.md index 2f778c291..66f4c7937 100644 --- a/docs/translations.md +++ b/docs/translations.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code @@ -8,37 +8,37 @@ in your config.yml to use the language, or use another language code to start a Code | Language | Translated |   ---- | -------- | ---------: | ------ [en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | bar -[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 86% | bar -[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 90% | bar -[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 90% | bar -[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 90% | bar -[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 90% | bar -[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 100% | bar -[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 90% | bar -[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 48% | bar -[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 51% | bar -[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 100% | bar -[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 54% | bar -[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 98% | bar -[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 53% | bar -[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | bar -[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 98% | bar -[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 40% | bar -[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 90% | bar -[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | bar -[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 90% | bar -[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 90% | bar -[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 98% | bar -[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 90% | bar -[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 86% | bar -[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 71% | bar -[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 87% | bar -[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 100% | bar -[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 90% | bar -[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 73% | bar -[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 98% | bar +[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 76% | bar +[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 80% | bar +[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 80% | bar +[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 80% | bar +[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 80% | bar +[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 92% | bar +[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 80% | bar +[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 42% | bar +[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 45% | bar +[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 89% | bar +[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 48% | bar +[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 87% | bar +[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 47% | bar +[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 92% | bar +[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 89% | bar +[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 36% | bar +[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 80% | bar +[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 92% | bar +[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 80% | bar +[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 80% | bar +[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 92% | bar +[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 80% | bar +[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 76% | bar +[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 63% | bar +[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 77% | bar +[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 89% | bar +[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 80% | bar +[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 65% | bar +[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 87% | bar --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Feb 02 20:09:17 CET 2018 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:09:12 CEST 2018 diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index e2450ba80..6dbbcea59 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -563,10 +563,11 @@ public class CommandInitializer { // Register the base totp code CommandDescription.builder() - .parent(null) + .parent(totpBase) .labels("code", "c") .description("Command for logging in") .detailedDescription("Processes the two-factor authentication code during login.") + .withArgument("code", "The TOTP code to use to log in", MANDATORY) .executableCommand(TotpCodeCommand.class) .register(); diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java index 9238c3b07..a52741b22 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java @@ -35,6 +35,7 @@ public class AddTotpCommand extends PlayerCommand { TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player); messages.send(player, MessageKey.TWO_FACTOR_CREATE, createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl()); + messages.send(player, MessageKey.TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED); } else { messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); } diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index c2fc5d472..6171f80aa 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -125,7 +125,7 @@ public enum MessageKey { /** Forgot your password? Please use the command: /email recovery <yourEmail> */ FORGOT_PASSWORD_MESSAGE("recovery.forgot_password_hint"), - /** To login you have to solve a captcha code, please use the command: /captcha %captcha_code */ + /** To log in you have to solve a captcha code, please use the command: /captcha %captcha_code */ USAGE_CAPTCHA("captcha.usage_captcha", "%captcha_code"), /** Wrong captcha, please type "/captcha %captcha_code" into the chat! */ @@ -134,7 +134,7 @@ public enum MessageKey { /** Captcha code solved correctly! */ CAPTCHA_SUCCESS("captcha.valid_captcha"), - /** To register you have to solve a captcha code first, please use the command: /captcha %captcha_code */ + /** To register you have to solve a captcha first, please use the command: /captcha %captcha_code */ CAPTCHA_FOR_REGISTRATION_REQUIRED("captcha.captcha_for_registration", "%captcha_code"), /** Valid captcha! You may now register with /register */ @@ -203,7 +203,10 @@ public enum MessageKey { /** Your secret code is %code. You can scan it from here %url */ TWO_FACTOR_CREATE("two_factor.code_created", "%code", "%url"), - /** Please submit your two-factor authentication code with /2fa code <code>. */ + /** Please confirm your code with /2fa confirm <code> */ + TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED("two_factor.confirmation_required"), + + /** Please submit your two-factor authentication code with /2fa code <code> */ TWO_FACTOR_CODE_REQUIRED("two_factor.code_required"), /** Two-factor authentication is already enabled for your account! */ @@ -276,27 +279,21 @@ public enum MessageKey { EMAIL_COOLDOWN_ERROR("email.email_cooldown_error", "%time"), /** - * The command you are trying to execute is sensitive and requires a verification! - * A verification code has been sent to your email, - * run the command "/verification [code]" to verify your identity. + * This command is sensitive and requires an email verification! + * Check your inbox and follow the email's instructions. */ VERIFICATION_CODE_REQUIRED("verification.code_required"), /** Usage: /verification <code> */ USAGE_VERIFICATION_CODE("verification.command_usage"), - /** Incorrect code, please type "/verification <code>" into the chat! */ + /** Incorrect code, please type "/verification <code>" into the chat, using the code you received by email */ INCORRECT_VERIFICATION_CODE("verification.incorrect_code"), - /** - * Your identity has been verified! - * You can now execute every sensitive command within the current session! - */ + /** Your identity has been verified! You can now execute all commands within the current session! */ VERIFICATION_CODE_VERIFIED("verification.success"), - /** - * You can already execute every sensitive command within the current session! - */ + /** You can already execute every sensitive command within the current session! */ VERIFICATION_CODE_ALREADY_VERIFIED("verification.already_verified"), /** Your code has expired! Execute another sensitive command to get a new code! */ diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java index cffc09cd9..9fc1c6a2a 100644 --- a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java +++ b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java @@ -1,11 +1,8 @@ package fr.xephi.authme.security.totp; import com.warrenstrange.googleauth.GoogleAuthenticator; -import com.warrenstrange.googleauth.GoogleAuthenticatorConfig; -import com.warrenstrange.googleauth.GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder; import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; -import com.warrenstrange.googleauth.HmacHashFunction; import com.warrenstrange.googleauth.IGoogleAuthenticator; import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; @@ -30,10 +27,7 @@ public class TotpAuthenticator { * @return new Google Authenticator instance */ protected IGoogleAuthenticator createGoogleAuthenticator() { - GoogleAuthenticatorConfig config = new GoogleAuthenticatorConfigBuilder() - .setHmacHashFunction(HmacHashFunction.HmacSHA512) - .build(); - return new GoogleAuthenticator(config); + return new GoogleAuthenticator(); } /** diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index 3a6f34948..4687a8b99 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -60,7 +60,6 @@ misc: logout: '&2Излязохте успешно!' reload: '&2Конфигурацията и база данните бяха презаредени правилно!' usage_change_password: '&cКоманда: /changepassword Стара-Парола Нова-Парола' - two_factor_create: '&2Кода е %code. Можеш да го провериш оттука: %url' accounts_owned_self: 'Претежаваш %count акаунт/а:' accounts_owned_other: 'Потребителят %name има %count акаунт/а:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cСтарият имейл е грешен, опитайте отново!' invalid: '&cИмейла е невалиден, опитайте с друг!' added: '&2Имейл адреса е добавен!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cМоля потвърди своя имейл адрес!' changed: '&2Имейл адреса е сменен!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Твоят имейл адрес е: &f%email' no_email_for_account: '&2Няма добавен имейл адрес към акаунта.' already_used: '&4Имейл адреса вече се използва, опитайте с друг.' @@ -99,8 +100,6 @@ email: send_failure: 'Съобщението не беше изпратено. Моля свържете се с администратора.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' email_cooldown_error: '&cВече е бил изпратен имейл адрес. Трябва а изчакаш %time преди да пратиш нов.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'часа' day: 'ден' days: 'дена' + +# Two-factor authentication +two_factor: + code_created: '&2Кода е %code. Можеш да го провериш оттука: %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 69062b23c..40c58fc8f 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -63,7 +63,6 @@ misc: logout: '&2Desconectado com sucesso!' reload: '&2Configuração e o banco de dados foram recarregados corretamente!' usage_change_password: '&cUse: /changepassword ' - two_factor_create: '&2O seu código secreto é %code. Você pode verificá-lo a partir daqui %url' accounts_owned_self: 'Você tem %count contas:' accounts_owned_other: 'O jogador %name tem %count contas:' @@ -93,8 +92,10 @@ email: old_email_invalid: '&cE-mail velho inválido, tente novamente!' invalid: '&E-mail inválido, tente novamente!' added: '&2Email adicionado com sucesso à sua conta!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cPor favor confirme seu endereço de email!' changed: '&2Troca de email com sucesso.!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2O seu endereço de e-mail atual é: &f%email' no_email_for_account: '&2Você atualmente não têm endereço de e-mail associado a esta conta.' already_used: '&4O endereço de e-mail já está sendo usado' @@ -102,8 +103,6 @@ email: send_failure: '&cO e-mail não pôde ser enviado, reporte isso a um administrador!' change_password_expired: 'Você não pode mais usar esse comando de recuperação de senha!' email_cooldown_error: '&cUm e-mail já foi enviado, espere mais %time antes de enviar novamente!' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -145,3 +144,16 @@ time: hours: 'horas' day: 'dia' days: 'dias' + +# Two-factor authentication +two_factor: + code_created: '&2O seu código secreto é %code. Você pode verificá-lo a partir daqui %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index e3bd5c35f..025b39f7a 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -60,7 +60,6 @@ misc: logout: '&cÚspěšně jsi se odhlásil.' reload: '&cZnovu načtení nastavení AuthMe proběhlo úspěšně.' usage_change_password: '&cPoužij: "/changepassword StaréHeslo NovéHeslo".' - two_factor_create: '&2Tvůj tajný kód je %code. Můžeš ho oskenovat zde %url' accounts_owned_self: 'Vlastníš tyto účty (%count):' accounts_owned_other: 'Hráč %name vlastní tyto účty (%count):' @@ -90,8 +89,10 @@ email: old_email_invalid: '[AuthMe] Starý email je chybně zadán!' invalid: '[AuthMe] Nesprávný email' added: '[AuthMe] Email přidán!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '[AuthMe] Potvrď prosím svůj email!' changed: '[AuthMe] Email změněn!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Váš aktuální email je: &f%email' no_email_for_account: '&2K tomuto účtu nemáte přidanou žádnou emailovou adresu.' already_used: '&4Tato emailová adresa je již používána' @@ -99,8 +100,6 @@ email: send_failure: 'Email nemohl být odeslán. Kontaktujte prosím admina.' change_password_expired: 'Nemůžeš si změnit heslo pomocí toho příkazu.' email_cooldown_error: '&cEmail už byl nedávno odeslán. Musíš čekat %time před odesláním nového.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'hodin' day: 'dny' days: 'dnu' + +# Two-factor authentication +two_factor: + code_created: '&2Tvůj tajný kód je %code. Můžeš ho oskenovat zde %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 97639a9af..006626234 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -60,7 +60,6 @@ misc: logout: '&2Erfolgreich ausgeloggt' reload: '&2Konfiguration und Datenbank wurden erfolgreich neu geladen.' usage_change_password: '&cBenutze: /changepassword ' - two_factor_create: '&2Dein geheimer Code ist %code. Du kannst ihn hier abfragen: %url' accounts_owned_self: 'Du besitzt %count Accounts:' accounts_owned_other: 'Der Spieler %name hat %count Accounts:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cDie alte E-Mail ist ungültig!' invalid: '&cUngültige E-Mail!' added: '&2E-Mail hinzugefügt!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cBitte bestätige deine E-Mail!' changed: '&2E-Mail aktualisiert!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Deine aktuelle E-Mail-Adresse ist: &f%email' no_email_for_account: '&2Du hast zur Zeit keine E-Mail-Adresse für deinen Account hinterlegt.' already_used: '&4Diese E-Mail-Adresse wird bereits genutzt.' @@ -99,8 +100,6 @@ email: send_failure: 'Die E-Mail konnte nicht gesendet werden. Bitte kontaktiere einen Administrator.' change_password_expired: 'Mit diesem Befehl kannst du dein Passwort nicht mehr ändern.' email_cooldown_error: '&cEine E-Mail wurde erst kürzlich versendet. Du musst %time warten, bevor du eine neue anfordern kannst.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'Stunden' day: 'Tag' days: 'Tage' + +# Two-factor authentication +two_factor: + code_created: '&2Dein geheimer Code ist %code. Du kannst ihn hier abfragen: %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index e32d1e46f..917d57cd1 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -132,6 +132,7 @@ verification: two_factor: code_created: '&2Your secret code is %code. You can scan it from here %url' + confirmation_required: 'Please confirm your code with /2fa confirm ' code_required: 'Please submit your two-factor authentication code with /2fa code ' already_enabled: 'Two-factor authentication is already enabled for your account!' enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' diff --git a/src/main/resources/messages/messages_eo.yml b/src/main/resources/messages/messages_eo.yml index 390acdca8..5636ab729 100644 --- a/src/main/resources/messages/messages_eo.yml +++ b/src/main/resources/messages/messages_eo.yml @@ -60,7 +60,6 @@ misc: logout: '&2Elsalutita sukcese!' reload: '&2Agordo kaj datumbazo estis larditaj korekte!' usage_change_password: '&cUzado: /changepassword ' - two_factor_create: '&2Via sekreta kodo estas %code. Vi povas skani ĝin de tie %url' accounts_owned_self: 'Vi posedas %count kontoj:' accounts_owned_other: 'La ludanto %name havas %count kontojn::' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cNevalida malnovaj retpoŝto, provu denove!' invalid: '&cNevalida retadreso, provu denove!' added: '&2Retpoŝtadreso sukcese aldonitaj al via konto!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cBonvolu konfirmi vian retadreson!' changed: '&2Retpoŝtadreso ŝanĝis ĝuste!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Via nuna retadreso estas: &f%email' no_email_for_account: '&2Vi aktuale ne havas retadreson asociita kun ĉi tiu konto.' already_used: '&4La retpoŝto jam estas uzata' @@ -99,8 +100,6 @@ email: send_failure: 'La retpoŝto ne estis sendita. Bonvolu kontakti administranto.' change_password_expired: 'Vi ne povas ŝanĝi vian pasvorton per tiu ĉi komando plu.' email_cooldown_error: '&cRetmesaĝon jam sendita lastatempe. Vi devas atendi %time antaŭ vi povas sendi novan.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'horoj' day: 'tago' days: 'tagoj' + +# Two-factor authentication +two_factor: + code_created: '&2Via sekreta kodo estas %code. Vi povas skani ĝin de tie %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index 899631cce..6070411f5 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -61,7 +61,6 @@ misc: logout: '&cDesconectado correctamente.' reload: '&fLa configuración y la base de datos han sido recargados' usage_change_password: '&fUso: /changepw contraseñaActual contraseñaNueva' - two_factor_create: '&2Tu código secreto es %code. Lo puedes escanear desde aquí %url' accounts_owned_self: 'Eres propietario de %count cuentas:' accounts_owned_other: 'El jugador %name tiene %count cuentas:' @@ -91,8 +90,10 @@ email: old_email_invalid: '[AuthMe] Email anterior inválido!' invalid: '[AuthMe] Email inválido' added: '[AuthMe] Email agregado !' + add_not_allowed: '&cNo se permite añadir un Email' request_confirmation: '[AuthMe] Confirma tu Email !' changed: '[AuthMe] Email cambiado !' + change_not_allowed: '&cNo se permite el cambio de Email' email_show: '&2Tu dirección de E-Mail actual es: &f%email' no_email_for_account: '&2No tienes ningun E-Mail asociado en esta cuenta.' already_used: '&4La dirección Email ya está siendo usada' @@ -100,8 +101,6 @@ email: send_failure: 'No se ha podido enviar el correo electrónico. Por favor, contacta con un administrador.' change_password_expired: 'No puedes cambiar la contraseña utilizando este comando.' email_cooldown_error: '&cEl correo ha sido enviado recientemente. Debes esperar %time antes de volver a enviar uno nuevo.' - add_not_allowed: '&cNo se permite añadir un Email' - change_not_allowed: '&cNo se permite el cambio de Email' # Password recovery by email recovery: @@ -143,3 +142,16 @@ time: hours: 'horas' day: 'día' days: 'días' + +# Two-factor authentication +two_factor: + code_created: '&2Tu código secreto es %code. Lo puedes escanear desde aquí %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_et.yml b/src/main/resources/messages/messages_et.yml index ba5e0b15e..3484fae69 100644 --- a/src/main/resources/messages/messages_et.yml +++ b/src/main/resources/messages/messages_et.yml @@ -60,7 +60,6 @@ misc: logout: '&2Edukalt välja logitud!!' reload: '&2Andmebaas uuendatud!' usage_change_password: '&cKasutus: /changepassword ' - two_factor_create: '&2Su salajane kood on %code. Skänni see siin: %url' accounts_owned_self: 'Sa omad %count kontot:' accounts_owned_other: 'Mängijal %name on %count kontot:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cVale vana meiliaadress, proovi uuesti.' invalid: '&cVale meiliaadress, proovi uuesti.' added: '&2Meiliaadress edukalt vahetatud!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cPalun kinnita oma meiliaadress.' changed: '&2Meiliaadress edukalt vahetatud.' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Su meiliaadress on: &f%email' no_email_for_account: '&2Selle kasutajaga pole seotud ühtegi meiliaadressi.' already_used: '&4Meiliaadress juba kasutuses.' @@ -99,8 +100,6 @@ email: send_failure: 'Meili ei õnnestunud saata. Kontakteeru meeskonnaga.' change_password_expired: '&3Enam ei saa vahetada oma parooli kasutades seda käsklust.' email_cooldown_error: '&cEmail juba saadeti. Sa pead ootama %time ennem, kui saad uuesti saata.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'tundi' day: 'päev' days: 'päeva' + +# Two-factor authentication +two_factor: + code_created: '&2Su salajane kood on %code. Skänni see siin: %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index 65bd5575f..cfa0f78b3 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -60,7 +60,6 @@ misc: logout: '&cAtera zara' reload: '&fConfiguration and database has been reloaded' usage_change_password: '&fErabili: /changepassword pasahitzZaharra pasahitzBerria' - # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO accounts_owned_self: 'You own %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:' @@ -90,8 +89,10 @@ email: old_email_invalid: '[AuthMe] Email zaharra okerra!' invalid: '[AuthMe] Email okerrea' added: '[AuthMe] Emaila gehitu duzu !' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '[AuthMe] Konfirmatu zure emaila !' changed: '[AuthMe] Emaila aldatua!' + # TODO change_not_allowed: '&cChanging email was not allowed' # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' # TODO already_used: '&4The email address is already being used' @@ -99,8 +100,6 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: # TODO hours: 'hours' # TODO day: 'day' # TODO days: 'days' + +# Two-factor authentication +two_factor: + # TODO code_created: '&2Your secret code is %code. You can scan it from here %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index 95d0a7320..00a29ba4e 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -60,7 +60,6 @@ misc: logout: '&cKirjauduit ulos palvelimelta.' reload: '&fAsetukset uudelleenladattu' usage_change_password: '&fKäyttötapa: /changepassword vanhaSalasana uusiSalasana' - # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO accounts_owned_self: 'You own %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:' @@ -90,8 +89,10 @@ email: old_email_invalid: '[AuthMe] Vanha sähköposti on väärä!' invalid: '[AuthMe] Väärä sähköposti' added: '[AuthMe] Sähköposti lisätty!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '[AuthMe] Vahvistuta sähköposti!' changed: '[AuthMe] Sähköposti vaihdettu!' + # TODO change_not_allowed: '&cChanging email was not allowed' # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' # TODO already_used: '&4The email address is already being used' @@ -99,8 +100,6 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: # TODO hours: 'hours' # TODO day: 'day' # TODO days: 'days' + +# Two-factor authentication +two_factor: + # TODO code_created: '&2Your secret code is %code. You can scan it from here %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index 710da4d7f..9a492cc72 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -63,7 +63,6 @@ misc: logout: '&cVous avez été déconnecté !' reload: '&aAuthMe a été relancé avec succès.' usage_change_password: '&cPour changer de mot de passe, utilisez "/changepassword "' - two_factor_create: '&aVotre code secret est &2%code&a. Vous pouvez le scanner depuis &2%url' accounts_owned_self: 'Vous avez %count comptes:' accounts_owned_other: 'Le joueur %name a %count comptes:' @@ -93,8 +92,10 @@ email: old_email_invalid: '&cAncien email invalide !' invalid: '&cL''email inscrit est invalide !' added: '&aEmail enregistré. En cas de perte de MDP, faites "/email recover "' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cLa confirmation de l''email est manquante ou éronnée.' changed: '&aVotre email a été mis à jour.' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&fL''email enregistré pour votre compte est: %email' no_email_for_account: '&c&oVous n''avez aucun email enregistré sur votre compte.' already_used: '&cCet email est déjà utilisé !' @@ -102,8 +103,6 @@ email: send_failure: '&cLe mail n''a pas pu être envoyé. Veuillez contacter un admin.' change_password_expired: 'Vous ne pouvez pas changer votre mot de passe avec cette commande.' email_cooldown_error: '&cUn mail de récupération a déjà été envoyé récemment. Veuillez attendre %time pour le demander de nouveau.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -145,3 +144,16 @@ time: hours: 'heures' day: 'jour' days: 'jours' + +# Two-factor authentication +two_factor: + code_created: '&aVotre code secret est &2%code&a. Vous pouvez le scanner depuis &2%url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index fb79b6646..6b6f8707e 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -60,7 +60,6 @@ misc: logout: '&cSesión pechada con éxito' reload: '&fRecargáronse a configuración e a base de datos' usage_change_password: '&fUso: /changepassword ' - # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO accounts_owned_self: 'You own %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:' @@ -90,8 +89,10 @@ email: old_email_invalid: '[AuthMe] O correo vello non é válido!' invalid: '[AuthMe] Correo non válido' added: '[AuthMe] Correo engadido!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '[AuthMe] Confirma o teu correo!' changed: '[AuthMe] Cambiouse o correo!' + # TODO change_not_allowed: '&cChanging email was not allowed' # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' # TODO already_used: '&4The email address is already being used' @@ -99,8 +100,6 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: # TODO hours: 'hours' # TODO day: 'day' # TODO days: 'days' + +# Two-factor authentication +two_factor: + # TODO code_created: '&2Your secret code is %code. You can scan it from here %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index 72f8a15be..4f2fe6020 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -60,7 +60,6 @@ misc: logout: '&cSikeresen kijelentkeztél!' reload: 'Beállítások és az adatbázis újratöltve!' usage_change_password: 'Használat: "/changepassword <új jelszó>".' - two_factor_create: '&2A titkos kódod a következő: %code. Vagy skenneld be a következő oldalról: %url' accounts_owned_self: '%count db regisztrációd van:' accounts_owned_other: 'A %name nevű játékosnak, %count db regisztrációja van:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cHibás a régi email cím, próbáld újra!' invalid: '&cHibás az email cím, próbáld újra!' added: '&2Az email címed rögzítése sikeresen megtörtént!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cKérlek, ellenőrízd az email címedet!' changed: '&2Az email cím cseréje sikeresen megtörtént!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2A jelenlegi email-ed a következő: &f%email' no_email_for_account: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.' already_used: '&4Ez az email cím már használatban van!' @@ -99,8 +100,6 @@ email: send_failure: 'Nem sikerült elküldeni az emailt. Lépj kapcsolatba egy adminnal.' change_password_expired: 'Ezzel a paranccsal már nem módosíthatja jelszavát.' email_cooldown_error: '&cEgy emailt már kiküldtünk. Következő email küldése előtt várnod kell: %time.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'óra' day: 'nap' days: 'nap' + +# Two-factor authentication +two_factor: + code_created: '&2A titkos kódod a következő: %code. Vagy skenneld be a következő oldalról: %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 5e5b3f3cd..9e9d61215 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -60,7 +60,6 @@ misc: logout: '&2Berhasil logout!' reload: '&2Konfigurasi dan database telah dimuat ulang!' usage_change_password: '&cUsage: /changepassword ' - # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO accounts_owned_self: 'You own %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cEmail lama tidak valid, coba lagi!' invalid: '&cAlamat email tidak valid, coba lagi!' added: '&2Berhasil menambahkan alamat email ke akunmu!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cSilahkan konfirmasi alamat email kamu!' changed: '&2Alamat email telah diubah dengan benar!' + # TODO change_not_allowed: '&cChanging email was not allowed' # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' # TODO already_used: '&4The email address is already being used' @@ -99,8 +100,6 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: # TODO hours: 'hours' # TODO day: 'day' # TODO days: 'days' + +# Two-factor authentication +two_factor: + # TODO code_created: '&2Your secret code is %code. You can scan it from here %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index d99cae84c..d46a244fd 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -61,7 +61,6 @@ misc: logout: '&2Disconnessione avvenuta correttamente!' reload: '&2La configurazione e il database sono stati ricaricati correttamente!' usage_change_password: '&cUtilizzo: /changepassword ' - two_factor_create: '&2Il tuo codice segreto è: &f%code%%nl%&2Puoi anche scannerizzare il codice QR da qui: &f%url' accounts_owned_self: 'Possiedi %count account:' accounts_owned_other: 'Il giocatore %name possiede %count account:' @@ -91,8 +90,10 @@ email: old_email_invalid: '&cIl vecchio indirizzo email inserito non è valido, riprova!' invalid: '&cL''indirizzo email inserito non è valido, riprova!' added: '&2Indirizzo email aggiunto correttamente al tuo account!' + add_not_allowed: '&cNon hai il permesso di aggiungere un indirizzo email' request_confirmation: '&cPer favore, conferma il tuo indirizzo email!' changed: '&2Indirizzo email cambiato correttamente!' + change_not_allowed: '&cNon hai il permesso di cambiare l''indirizzo email' email_show: '&2Il tuo indirizzo email al momento è: &f%email' no_email_for_account: '&2Al momento non hai nessun indirizzo email associato al tuo account.' already_used: '&4L''indirizzo email inserito è già in uso' @@ -100,8 +101,6 @@ email: send_failure: 'Non è stato possibile inviare l''email di recupero. Per favore contatta un amministratore.' change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' - add_not_allowed: '&cNon hai il permesso di aggiungere un indirizzo email' - change_not_allowed: '&cNon hai il permesso di cambiare l''indirizzo email' # Password recovery by email recovery: @@ -143,3 +142,16 @@ time: hours: 'ore' day: 'giorno' days: 'giorni' + +# Two-factor authentication +two_factor: + code_created: '&2Il tuo codice segreto è: &f%code%%nl%&2Puoi anche scannerizzare il codice QR da qui: &f%url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index 5d875932e..32b5ffd43 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -62,7 +62,6 @@ misc: logout: '&2로그아웃 되었습니다!' reload: '&2설정과 데이터 베이스가 새로고침 되었습니다!' usage_change_password: '&c사용법: /changepassword <예전 비밀번호> <새 비밀번호>' - two_factor_create: '&2당신의 비밀 코드는 %code 입니다. %url 에서 스캔할 수 있습니다' accounts_owned_self: '%count 개의 계정을 소유하고 있습니다.' accounts_owned_other: '플레이어 %name 는 %count 개의 계정을 소유하고 있습니다:' @@ -92,8 +91,10 @@ email: old_email_invalid: '&c예전 이메일 주소가 잘못되었습니다. 다시 시도해보세요!' invalid: '&c이메일 주소가 잘못되었습니다. 다시 시도해보세요!' added: '&2계정에 이메일 주소를 추가했습니다!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&c이메일 주소를 확인해주세요!' changed: '&2이메일 주소가 변경되었습니다!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2현재 이메일 주소: &f%email' no_email_for_account: '&2현재 이 계정과 연결된 이메일 주소가 없습니다.' already_used: '&4이메일 주소가 이미 사용 중입니다.' @@ -101,8 +102,6 @@ email: send_failure: '이메일을 보낼 수 없습니다. 관리자에게 알려주세요.' change_password_expired: '더 이상 이 명령어를 통해 비밀번호를 변경할 수 없습니다.' email_cooldown_error: '&c이메일을 이미 발송했습니다. %time 후에 다시 발송할 수 있습니다.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -144,3 +143,16 @@ time: hours: '시간' day: '일' days: '일' + +# Two-factor authentication +two_factor: + code_created: '&2당신의 비밀 코드는 %code 입니다. %url 에서 스캔할 수 있습니다' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index 3df013699..e58a9fd6e 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -60,7 +60,6 @@ misc: logout: '&aSekmingai atsijungete' reload: '&aNustatymai ir duomenu baze buvo perkrauta.' usage_change_password: '&ePanaudojimas: /changepassword senasSlaptazodis naujasSlaptazodis' - # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO accounts_owned_self: 'You own %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:' @@ -90,8 +89,10 @@ email: # TODO old_email_invalid: '&cInvalid old email, try again!' # TODO invalid: '&cInvalid email address, try again!' # TODO added: '&2Email address successfully added to your account!' + # TODO add_not_allowed: '&cAdding email was not allowed' # TODO request_confirmation: '&cPlease confirm your email address!' # TODO changed: '&2Email address changed correctly!' + # TODO change_not_allowed: '&cChanging email was not allowed' # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' # TODO already_used: '&4The email address is already being used' @@ -99,8 +100,6 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: # TODO hours: 'hours' # TODO day: 'day' # TODO days: 'days' + +# Two-factor authentication +two_factor: + # TODO code_created: '&2Your secret code is %code. You can scan it from here %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index 86f7b8ff2..00b99161b 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -60,7 +60,6 @@ misc: logout: '&2Je bent succesvol uitgelogd!' reload: '&2De configuratie en database zijn succesvol herladen!' usage_change_password: '&cGebruik: /changepassword ' - two_factor_create: '&2Je geheime code is %code. Je kunt hem scannen op %url' accounts_owned_self: 'Je bezit %count accounts:' accounts_owned_other: 'De speler %name heeft %count accounts:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cOngeldig oud e-mailadres, probeer het opnieuw!' invalid: '&cOngeldig E-mailadres, probeer het opnieuw!' added: '&2Het e-mailadres is succesvol toegevoegd aan je account!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cVerifiëer je e-mailadres alsjeblieft!' changed: '&2Het e-mailadres is succesvol veranderd!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Jouw huidige e-mailadres is: %email' no_email_for_account: '&2Je hebt nog geen e-mailadres toegevoegd aan dit account.' already_used: '&4Dit e-mailadres wordt al gebruikt' @@ -99,8 +100,6 @@ email: send_failure: 'De e-mail kon niet verzonden worden. Neem contact op met een administrator.' change_password_expired: 'Je kunt je wachtwoord niet meer veranderen met dit commando.' email_cooldown_error: '&cEr is recent al een e-mail verzonden. Je moet %time wachten voordat je een nieuw bericht kunt versturen.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'uren' day: 'dag' days: 'dagen' + +# Two-factor authentication +two_factor: + code_created: '&2Je geheime code is %code. Je kunt hem scannen op %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 6184f8e22..b923533bd 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -60,7 +60,6 @@ misc: logout: '&cPomyślnie wylogowany' reload: '&fKonfiguracja bazy danych została przeładowana.' usage_change_password: '&fUżycie: /changepassword ' - two_factor_create: '&2Twój sekretny kod to %code. Możesz zeskanować go tutaj: %url' accounts_owned_self: '&7Posiadasz %count kont:' accounts_owned_other: '&7Gracz %name posiada %count kont:' @@ -90,8 +89,10 @@ email: old_email_invalid: '[AuthMe] Stary e-mail niepoprawny!' invalid: '[AuthMe] Nieprawidłowy adres e-mail.' added: '[AuthMe] E-mail został dodany do Twojego konta!' + add_not_allowed: '&cMożliwość dodania adresu e-mail jest wyłączona.' request_confirmation: '[AuthMe] Potwierdź swój adres e-mail!' changed: '[AuthMe] E-mail został zmieniony!' + change_not_allowed: '&cMożliwość zmiany adresu e-mail jest wyłączona.' email_show: '&2Twój aktualny adres e-mail to: &f%email' no_email_for_account: '&2Nie posiadasz adresu e-mail przypisanego do tego konta.' already_used: '&4Ten adres e-mail jest aktualnie używany!' @@ -99,8 +100,6 @@ email: send_failure: 'Nie można wysłać e-maila. Skontaktuj się z administracją.' change_password_expired: 'Nie zmienisz już hasła przy użyciu tej komendy.' email_cooldown_error: '&cE-mail został wysłany, musisz poczekać %time przed wysłaniem następnego.' - add_not_allowed: '&cMożliwość dodania adresu e-mail jest wyłączona.' - change_not_allowed: '&cMożliwość zmiany adresu e-mail jest wyłączona.' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'godzin' day: 'dzień' days: 'dni' + +# Two-factor authentication +two_factor: + code_created: '&2Twój sekretny kod to %code. Możesz zeskanować go tutaj: %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index b7cabdc8b..56b19dd32 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -60,7 +60,6 @@ misc: logout: '&cSaida com sucesso' reload: '&fConfiguração e base de dados foram recarregadas' usage_change_password: '&fUse: /changepassword ' - two_factor_create: '&2O seu código secreto é o %code. Você pode verificá-lo a partir daqui %url' accounts_owned_self: 'Você possui %count contas:' accounts_owned_other: 'O jogador %name possui %count contas:' @@ -90,8 +89,10 @@ email: old_email_invalid: 'Email antigo inválido!' invalid: 'Email inválido!' added: 'Email adicionado com sucesso!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: 'Confirme o seu email!' changed: 'Email alterado com sucesso!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2O seu endereço de email atual é &f%email' no_email_for_account: '&2Você atualmente não tem um endereço de email associado a essa conta.' already_used: '&4O endereço de e-mail já está sendo usado' @@ -99,8 +100,6 @@ email: send_failure: 'Não foi possivel enviar o email. Por favor contate um administrador.' change_password_expired: 'Você não pode mais alterar a sua password usando este comando.' email_cooldown_error: '&cUm email já foi enviado recentemente.Por favor, espere %time antes de enviar novamente' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'horas' day: 'dia' days: 'dias' + +# Two-factor authentication +two_factor: + code_created: '&2O seu código secreto é o %code. Você pode verificá-lo a partir daqui %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml index 4d40556d1..3d9c6cfed 100644 --- a/src/main/resources/messages/messages_ro.yml +++ b/src/main/resources/messages/messages_ro.yml @@ -60,7 +60,6 @@ misc: logout: '&2Te-ai dezautentificat cu succes!' reload: '&2Configuratiile si baza de date sau reincarcat corect!' usage_change_password: '&cFoloseste comanda: /changepassword ' - two_factor_create: '&2Codul tau secret este %code. Il poti scana de aici %url' accounts_owned_self: 'Detii %count conturi:' accounts_owned_other: 'Jucatorul %name are %count conturi:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cEmail-ul vechi este invalid, incearca din nou!' invalid: '&cEmail-ul este invalid, incearca din nou!' added: '&2Email-ul a fost adaugat cu succes la contul tau!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cTe rugam sa confirmi adresa ta de email!' changed: '&2Email-ul a fost schimbat cu succes!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Adresa ta curenta de email este: &f%email' no_email_for_account: '&2Nu ai nici o adresa de email asociata cu acest cont.' already_used: '&4Email-ul acesta este deja folosit de altcineva' @@ -99,8 +100,6 @@ email: send_failure: 'Email-ul nu a putut fi trimis. Ta rugam contactatezi un administrator.' change_password_expired: 'Nu mai iti poti schimba parola folosind aceasta comanda.' email_cooldown_error: '&cAi primit deja un mail pentru schimbarea parolei. Trebuie sa astepti %time inainte de a trimite unul nou.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'ore' day: 'zi' days: 'zile' + +# Two-factor authentication +two_factor: + code_created: '&2Codul tau secret este %code. Il poti scana de aici %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 81d5c6f6b..fd28978a4 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -60,7 +60,6 @@ misc: logout: '&2Вы успешно вышли.' reload: '&6Конфигурация и база данных перезагружены.' usage_change_password: '&cИспользование: /changepassword <пароль> <новый пароль>' - two_factor_create: '&2Ваш секретный код — %code. Просканируйте его здесь: %url' accounts_owned_self: 'У вас %count уч. записей:' accounts_owned_other: 'У игрока %name %count уч. записей:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cНедействительная старая электронная почта!' invalid: '&cНедействительный адрес электронной почты!' added: '&2Электронная почта успешно добавлена!' + add_not_allowed: '&cДобавление электронной почты не было разрешено.' request_confirmation: '&cПодтвердите свою электронную почту!' changed: '&2Адрес электронной почты изменён!' + change_not_allowed: '&cИзменение электронной почты не было разрешено.' email_show: '&2Текущий адрес электронной почты — &f%email' no_email_for_account: '&2К вашей уч. записи не привязана электронная почта.' already_used: '&4Эта электронная почта уже используется.' @@ -99,8 +100,6 @@ email: send_failure: 'Письмо не может быть отправлено. Свяжитесь в администратором.' change_password_expired: 'Больше нельзя сменить свой пароль, используя эту команду.' email_cooldown_error: '&cПисьмо было отправлено недавно. Подождите %time, прежде чем отправить новое.' - add_not_allowed: '&cДобавление электронной почты не было разрешено.' - change_not_allowed: '&cИзменение электронной почты не было разрешено.' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'ч.' day: 'дн.' days: 'дн.' + +# Two-factor authentication +two_factor: + code_created: '&2Ваш секретный код — %code. Просканируйте его здесь: %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index 2553fc0dd..73ba9f6e1 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -66,7 +66,6 @@ misc: logout: '&cBol si úspešne odhlásený.' reload: '&fZnovu načítanie konfigurácie a databázy bolo úspešné.' usage_change_password: '&fPoužitie: /changepassword ' - two_factor_create: '&2Tvoj tajný kód je %code. Môžeš ho oskenovať tu: %url' accounts_owned_self: 'Vlastníš tieto účty(%count): ' accounts_owned_other: 'Hráč %name vlastní tieto účty(%count): ' @@ -96,8 +95,10 @@ email: old_email_invalid: '&cNeplatný starý email, skús to znovu!' invalid: '&cNeplatná emailová adresa, skús to znovu!' added: '&2Emailová adresa bola úspešne pridaná k tvojmu účtu!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cProsím potvrď svoju emailovú adresu!' changed: '&2Emailová adresa bola úspešne zmenená!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Tvoja súčastná emailová adresa je: &f%email' no_email_for_account: '&2Momentálne nemáš emailovú adresu spojenú s týmto účtom.' already_used: '&4Túto emailovú adresu už niekto používa.' @@ -105,8 +106,6 @@ email: send_failure: 'Email nemohol byť poslaný. Prosím kontaktuj Administrátora.' change_password_expired: 'Už nemôžeš zmeniť svoje heslo týmto príkazom.' email_cooldown_error: '&cEmail bol nedávno poslaný. Musíš počkať %time predtým ako ti pošleme nový.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -148,3 +147,16 @@ time: hours: 'hod.' day: 'd.' days: 'd.' + +# Two-factor authentication +two_factor: + code_created: '&2Tvoj tajný kód je %code. Môžeš ho oskenovať tu: %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index fd7d5e292..39a14b865 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -60,7 +60,6 @@ misc: logout: '&2Basariyla cikis yaptin!' reload: '&2Ayarlar ve veritabani yenilendi!' usage_change_password: '&cKullanim: /changepassword ' - two_factor_create: '&2Gizli kodunuz %code. Buradan test edebilirsin, %url' accounts_owned_self: 'Sen %count hesaba sahipsin:' accounts_owned_other: 'Oyuncu %name %count hesaba sahip:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cGecersiz eski eposta, tekrar deneyin!' invalid: '&cGecersiz eposta, tekrar deneyin!' added: '&2Eposta basariyla kullaniciniza eklendi!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cLutfen tekrar epostanizi giriniz!' changed: '&2Epostaniz basariyla degistirildi!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Suanki eposta adresin: &f%email' no_email_for_account: '&2Bu hesapla iliskili bir eposta bulunmuyor.' already_used: '&4Eposta adresi zaten kullaniliyor.' @@ -99,8 +100,6 @@ email: send_failure: 'Eposta gonderilemedi. Yetkili ile iletisime gec.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' email_cooldown_error: '&cKisa bir sure once eposta gonderildi. Yeni bir eposta almak icin %time beklemelisin.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'saat' day: 'gun' days: 'gun' + +# Two-factor authentication +two_factor: + code_created: '&2Gizli kodunuz %code. Buradan test edebilirsin, %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index d509d4ebe..034238d3f 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -60,7 +60,6 @@ misc: logout: '&2Ви вийшли зі свого акаунта!' reload: '&2Конфігурації та базу даних було успішно перезавантажено!' usage_change_password: '&cСинтаксис: /changepassword <старийПароль> <новийПароль>' - two_factor_create: '&2Ваш секретний код — %code %nl%&2Можете зкопіювати його за цим посиланням — %url' accounts_owned_self: 'Кількість ваших твінк‒акаунтів: %count:' accounts_owned_other: 'Кількість твінк‒акаунтів гравця %name: %count' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cСтарий e-mail, що прив’язано до вашого акаунта, відрізняється від введеного вами.' invalid: '&cФормат вказаного e-mail’у є некоректним, або його домен внесено до блеклисту.' added: '&2Електронну пошту успішно прив’язано до вашого акаунта.' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cАдреси не співпадають.' changed: '&2E-mail успішно змінено.' + # TODO change_not_allowed: '&cChanging email was not allowed' # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' already_used: '&4До цієї електронної пошти прив’язано забагато акаунтів!' @@ -99,8 +100,6 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: # TODO hours: 'hours' # TODO day: 'day' # TODO days: 'days' + +# Two-factor authentication +two_factor: + code_created: '&2Ваш секретний код — %code %nl%&2Можете зкопіювати його за цим посиланням — %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index 8fc170c04..10a813ef6 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -60,7 +60,6 @@ misc: logout: '&2Bạn đã đăng xuất!' reload: '&2Cấu hình và cơ sở dử liệu đã được nạp lại!' usage_change_password: '&cSử dụng: /changepassword ' - two_factor_create: '&2Mã bí mật của bạn là %code. Bạn có thể quét nó tại đây %url' accounts_owned_self: 'Bạn sở hữu %count tài khoản:' accounts_owned_other: 'Người chơi %name có %count tài khoản:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&cEmail cũ không hợp lệ, vui lòng thử lại!' invalid: '&cĐại chỉ email không hợp lệ, vui lòng thử lại!' added: '&2Địa chỉ email đã thêm vào tài khoản của bạn thành công!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&cVui lòng xác nhận địa chỉ email của bạn!' changed: '&2Địa chỉ email đã thay đổi!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2Địa chỉ email hiện tại của bạn là: &f%email' no_email_for_account: '&2Hiện tại bạn chưa liên kết bất kỳ email nào với tài khoản này.' already_used: '&4Địa chỉ email đã được sử dụng' @@ -99,8 +100,6 @@ email: send_failure: 'Không thể gửi thư. Vui lòng liên hệ với ban quản trị.' change_password_expired: '&cBạn không thể thay đổi mật khẩu bằng lệnh này từ nay.' email_cooldown_error: '&cMột bức thư đã được gửi gần đây. Bạn phải chờ %time trước khi có thể gửi một bức thư mới.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: 'giờ' day: 'ngày' days: 'ngày' + +# Two-factor authentication +two_factor: + code_created: '&2Mã bí mật của bạn là %code. Bạn có thể quét nó tại đây %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index 70193285b..ec7a4b01e 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -60,7 +60,6 @@ misc: logout: '&8[&6玩家系统&8] &c已成功登出!' reload: '&8[&6玩家系统&8] &f配置以及数据已经重新加载完毕' usage_change_password: '&8[&6玩家系统&8] &f正确用法:“/changepassword 旧密码 新密码”' - two_factor_create: '&8[&6玩家系统&8] &2你的代码是 %code,你可以使用 %url 来扫描' accounts_owned_self: '您拥有 %count 个账户:' accounts_owned_other: '玩家 %name 拥有 %count 个账户:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&8[&6玩家系统&8] &f旧邮箱无效!' invalid: '&8[&6玩家系统&8] &f无效的邮箱' added: '&8[&6玩家系统&8] &f邮箱已添加 !' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&8[&6玩家系统&8] &f确认你的邮箱 !' changed: '&8[&6玩家系统&8] &f邮箱已改变 !' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&2您当前的电子邮件地址为: &f%email' no_email_for_account: '&2您当前并没有任何邮箱与该账号绑定' already_used: '&8[&6玩家系统&8] &4邮箱已被使用' @@ -99,8 +100,6 @@ email: send_failure: '邮件发送失败,请联系管理员' change_password_expired: '您不能使用此命令更改密码' email_cooldown_error: '&c邮件已在几分钟前发送,您需要等待 %time 后才能再次请求发送' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: hours: '小时' day: '天' days: '天' + +# Two-factor authentication +two_factor: + code_created: '&8[&6玩家系统&8] &2你的代码是 %code,你可以使用 %url 来扫描' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index 694d532eb..996872c86 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -63,7 +63,6 @@ misc: logout: '&8[&6用戶系統&8] &b你成功登出了。' reload: '&8[&6用戶系統&8] &b登入系統設定及資料庫重新載入完畢。' usage_change_password: '&8[&6用戶系統&8] &f用法:《 /changepassword <舊密碼> <新密碼> 》' - two_factor_create: '&8[&6用戶系統 - 兩步驗證碼&8] &b你的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' accounts_owned_self: '你擁有 %count 個帳戶:' accounts_owned_other: '玩家《%name》擁有 %count 個帳戶:' @@ -93,8 +92,10 @@ email: old_email_invalid: '&8[&6用戶系統&8] &c你所填寫的舊電郵地址並不正確。' invalid: '&8[&6用戶系統&8] &c你所填寫的電郵地址並不正確。' added: '&8[&6用戶系統&8] &a已新增你的電郵地址。' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&8[&6用戶系統&8] &5請重覆輸入你的電郵地址。' changed: '&8[&6用戶系統&8] &a你的電郵地址已更改。' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&8[&6用戶系統&8] &2你所使用的電郵地址為:&f%email' no_email_for_account: '&8[&6用戶系統&8] &2你並未有綁定電郵地址到此帳戶。' already_used: '&8[&6用戶系統&8] &4這個電郵地址已被使用。' @@ -102,8 +103,6 @@ email: send_failure: '&8[&6用戶系統&8] &c電郵系統錯誤,請聯絡伺服器管理員。 &7(err: smtperr)' change_password_expired: '&8[&6用戶系統&8] 此指令已過期,請重新辦理。' email_cooldown_error: '&8[&6用戶系統&8] &c你已經辦理過重寄郵件,請等待 %time 後再嘗試吧。' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -145,3 +144,16 @@ time: hours: '小時' day: '日' days: '日' + +# Two-factor authentication +two_factor: + code_created: '&8[&6用戶系統 - 兩步驗證碼&8] &b你的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_zhmc.yml b/src/main/resources/messages/messages_zhmc.yml index c0b418025..cf9fe986f 100644 --- a/src/main/resources/messages/messages_zhmc.yml +++ b/src/main/resources/messages/messages_zhmc.yml @@ -60,7 +60,6 @@ misc: logout: '&2已成功註銷!' reload: '&2伺服器已正確地被重新加載配置和數據庫!' usage_change_password: '&c使用方法: "/changepassword [舊密碼] [新密碼]"' - two_factor_create: '&2您的密碼是 %code。您可以從這裡掃描 %url' accounts_owned_self: '您擁有 %count 個帳戶:' accounts_owned_other: '玩家 %name 擁有 %count 個帳戶:' @@ -90,8 +89,10 @@ email: old_email_invalid: '&c舊電子郵件地址無效,請重試!' invalid: '&c電子郵件地址無效,請重試!' added: '&2電子郵件地址已成功添加到您的帳戶!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&c請確認你的電郵地址!' changed: '&2已正確地更改電子郵件地址!' + # TODO change_not_allowed: '&cChanging email was not allowed' # TODO email_show: '&2Your current email address is: &f%email' # TODO no_email_for_account: '&2You currently don''t have email address associated with this account.' already_used: '&4此電子郵件地址已被使用' @@ -99,8 +100,6 @@ email: # TODO send_failure: 'The email could not be sent. Please contact an administrator.' # TODO change_password_expired: 'You cannot change your password using this command anymore.' # TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -142,3 +141,16 @@ time: # TODO hours: 'hours' # TODO day: 'day' # TODO days: 'days' + +# Two-factor authentication +two_factor: + code_created: '&2您的密碼是 %code。您可以從這裡掃描 %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index fa9946bf9..f6192193a 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -62,7 +62,6 @@ misc: logout: '&b【AuthMe】&6您已成功登出' reload: '&b【AuthMe】&6已重新讀取設定檔及資料庫' usage_change_password: '&b【AuthMe】&6用法: &c"/changepassword <舊密碼> <新密碼>"' - two_factor_create: '&b【AuthMe - 兩步驗證碼】&b您的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' accounts_owned_self: '&b【AuthMe】&6您擁有 %count 個帳號:' accounts_owned_other: '&b【AuthMe】&6玩家 %name 擁有 %count 個帳號:' @@ -92,8 +91,10 @@ email: old_email_invalid: '&b【AuthMe】&6舊的Email無效!' invalid: '&b【AuthMe】&6無效的Email!' added: '&b【AuthMe】&6已添加Email!' + # TODO add_not_allowed: '&cAdding email was not allowed' request_confirmation: '&b【AuthMe】&6請驗證您的Email!' changed: '&b【AuthMe】&6Email已變更!' + # TODO change_not_allowed: '&cChanging email was not allowed' email_show: '&b【AuthMe】&2目前的電子郵件: &f%email' no_email_for_account: '&b【AuthMe】&2您目前沒有設置電子郵件.' already_used: '&b【AuthMe】&4這個電郵地址已被使用。' @@ -101,8 +102,6 @@ email: send_failure: '&b【AuthMe】&4無法傳送電子郵件,請聯絡管理員.' change_password_expired: '&b【AuthMe】&6您現在不能使用這個指令變更密碼了.' email_cooldown_error: '&b【AuthMe】&c電子郵件已經寄出了. 您只能在 %time 後才能傳送.' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' # Password recovery by email recovery: @@ -144,3 +143,16 @@ time: hours: '時' day: '天' days: '天' + +# Two-factor authentication +two_factor: + code_created: '&b【AuthMe - 兩步驗證碼】&b您的登入金鑰為&9「%c%code&9」&b,掃描連結為:&c %url' + # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' + # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' + # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' + # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' + # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' + # TODO removed_success: 'Successfully removed two-factor auth from your account' + # TODO invalid_code: 'Invalid code!' diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 46f2dd13c..1be18288c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -23,7 +23,7 @@ commands: usage: /email show|add|change|recover|code|setpassword login: description: Login command - usage: /login [2facode] + usage: /login aliases: - l - log @@ -48,7 +48,7 @@ commands: - cp totp: description: TOTP commands - usage: /totp add|confirm|remove + usage: /totp code|add|confirm|remove aliases: - 2fa captcha: From ecaffbabfca85ef8cf936b088f6b4d61cc88e965 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 22 Apr 2018 12:45:34 +0200 Subject: [PATCH 128/155] Small cleanups / changes amassed over time - Small javadoc fixes - Simplifications - Move logException method from StringUtils to ExceptionUtils --- src/main/java/fr/xephi/authme/AuthMe.java | 9 +++------ .../java/fr/xephi/authme/ConsoleLogger.java | 4 ++-- .../data/limbo/AllowFlightRestoreType.java | 2 +- .../authme/process/register/AsyncRegister.java | 6 ++---- .../xephi/authme/security/crypts/BCrypt.java | 4 ++-- .../fr/xephi/authme/security/crypts/Ipb4.java | 6 +++--- .../xephi/authme/security/crypts/XfBCrypt.java | 4 ++-- .../fr/xephi/authme/util/ExceptionUtils.java | 10 ++++++++++ .../java/fr/xephi/authme/util/StringUtils.java | 11 ----------- .../fr/xephi/authme/CodeClimateConfigTest.java | 18 +++--------------- .../message/YamlTextFileCheckerTest.java | 3 ++- .../xephi/authme/util/ExceptionUtilsTest.java | 14 ++++++++++++++ .../fr/xephi/authme/util/StringUtilsTest.java | 14 -------------- 13 files changed, 44 insertions(+), 61 deletions(-) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 4aeb536c1..6a2b3583c 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -2,9 +2,7 @@ package fr.xephi.authme; import ch.jalu.injector.Injector; import ch.jalu.injector.InjectorBuilder; - import com.google.common.annotations.VisibleForTesting; - import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.datasource.DataSource; @@ -35,9 +33,6 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.CleanupTask; import fr.xephi.authme.task.purge.PurgeService; import fr.xephi.authme.util.ExceptionUtils; - -import java.io.File; - import org.apache.commons.lang.SystemUtils; import org.bukkit.Server; import org.bukkit.command.Command; @@ -48,6 +43,8 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.scheduler.BukkitScheduler; +import java.io.File; + import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; import static fr.xephi.authme.util.Utils.isClassLoaded; @@ -160,7 +157,7 @@ public class AuthMe extends JavaPlugin { // Sponsor messages ConsoleLogger.info("Development builds are available on our jenkins, thanks to FastVM.io"); - ConsoleLogger.info("Do you want a good vps for your game server? Look at our sponsor FastVM.io leader " + ConsoleLogger.info("Do you want a good vps for your game server? Look at our sponsor FastVM.io leader " + "as virtual server provider!"); // Successful message diff --git a/src/main/java/fr/xephi/authme/ConsoleLogger.java b/src/main/java/fr/xephi/authme/ConsoleLogger.java index 0d9332985..3e8d59a4a 100644 --- a/src/main/java/fr/xephi/authme/ConsoleLogger.java +++ b/src/main/java/fr/xephi/authme/ConsoleLogger.java @@ -5,7 +5,7 @@ import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.ExceptionUtils; import java.io.File; import java.io.FileWriter; @@ -101,7 +101,7 @@ public final class ConsoleLogger { * @param th The Throwable to log */ public static void logException(String message, Throwable th) { - warning(message + " " + StringUtils.formatException(th)); + warning(message + " " + ExceptionUtils.formatException(th)); writeLog(Throwables.getStackTraceAsString(th)); } diff --git a/src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java b/src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java index 523885213..753650b67 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java +++ b/src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java @@ -32,7 +32,7 @@ public enum AllowFlightRestoreType { } }, - /** Always set flight enabled to false. */ + /** The user's flight handling is not modified. */ NOTHING { @Override public void restoreAllowFlight(Player player, LimboPlayer limbo) { 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 78eaa5679..fa5d03613 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -14,7 +14,6 @@ import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.bungeecord.BungeeSender; import fr.xephi.authme.service.bungeecord.MessageType; -import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.util.PlayerUtils; @@ -82,9 +81,8 @@ public class AsyncRegister implements AsynchronousProcess { return false; } - boolean isAsync = service.getProperty(PluginSettings.USE_ASYNC_TASKS); - AuthMeAsyncPreRegisterEvent event = new AuthMeAsyncPreRegisterEvent(player, isAsync); - bukkitService.callEvent(event); + AuthMeAsyncPreRegisterEvent event = bukkitService.createAndCallEvent( + isAsync -> new AuthMeAsyncPreRegisterEvent(player, isAsync)); if (!event.canRegister()) { return false; } diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCrypt.java b/src/main/java/fr/xephi/authme/security/crypts/BCrypt.java index 02e12d459..8b454c799 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/BCrypt.java +++ b/src/main/java/fr/xephi/authme/security/crypts/BCrypt.java @@ -8,7 +8,7 @@ import fr.xephi.authme.security.crypts.description.SaltType; import fr.xephi.authme.security.crypts.description.Usage; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.HooksSettings; -import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.ExceptionUtils; import javax.inject.Inject; @@ -39,7 +39,7 @@ public class BCrypt implements EncryptionMethod { try { return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash()); } catch (IllegalArgumentException e) { - ConsoleLogger.warning("Bcrypt checkpw() returned " + StringUtils.formatException(e)); + ConsoleLogger.warning("Bcrypt checkpw() returned " + ExceptionUtils.formatException(e)); } return false; } diff --git a/src/main/java/fr/xephi/authme/security/crypts/Ipb4.java b/src/main/java/fr/xephi/authme/security/crypts/Ipb4.java index 762897955..c7bfcd65b 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Ipb4.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Ipb4.java @@ -2,12 +2,12 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.security.HashUtils; -import fr.xephi.authme.util.RandomStringUtils; import fr.xephi.authme.security.crypts.description.HasSalt; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.SaltType; import fr.xephi.authme.security.crypts.description.Usage; -import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.ExceptionUtils; +import fr.xephi.authme.util.RandomStringUtils; /** @@ -37,7 +37,7 @@ public class Ipb4 implements EncryptionMethod { try { return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash()); } catch (IllegalArgumentException e) { - ConsoleLogger.warning("Bcrypt checkpw() returned " + StringUtils.formatException(e)); + ConsoleLogger.warning("Bcrypt checkpw() returned " + ExceptionUtils.formatException(e)); } return false; } diff --git a/src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java b/src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java index 3ef4e4301..846807e6c 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java +++ b/src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java @@ -2,7 +2,7 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.security.HashUtils; -import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.ExceptionUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,7 +32,7 @@ public class XfBCrypt implements EncryptionMethod { try { return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash()); } catch (IllegalArgumentException e) { - ConsoleLogger.warning("XfBCrypt checkpw() returned " + StringUtils.formatException(e)); + ConsoleLogger.warning("XfBCrypt checkpw() returned " + ExceptionUtils.formatException(e)); } return false; } diff --git a/src/main/java/fr/xephi/authme/util/ExceptionUtils.java b/src/main/java/fr/xephi/authme/util/ExceptionUtils.java index 6a5adde69..fd5ae8852 100644 --- a/src/main/java/fr/xephi/authme/util/ExceptionUtils.java +++ b/src/main/java/fr/xephi/authme/util/ExceptionUtils.java @@ -33,4 +33,14 @@ public final class ExceptionUtils { } return null; } + + /** + * Format the information from a Throwable as string, retaining the type and its message. + * + * @param th the throwable to process + * @return string with the type of the Throwable and its message, e.g. "[IOException]: Could not open stream" + */ + public static String formatException(Throwable th) { + return "[" + th.getClass().getSimpleName() + "]: " + th.getMessage(); + } } diff --git a/src/main/java/fr/xephi/authme/util/StringUtils.java b/src/main/java/fr/xephi/authme/util/StringUtils.java index 1f200c0f0..5c8613005 100644 --- a/src/main/java/fr/xephi/authme/util/StringUtils.java +++ b/src/main/java/fr/xephi/authme/util/StringUtils.java @@ -66,17 +66,6 @@ public final class StringUtils { return str == null || str.trim().isEmpty(); } - /** - * Format the information from a Throwable as string, retaining the type and its message. - * - * @param th The throwable to process - * - * @return String with the type of the Throwable and its message, e.g. "[IOException]: Could not open stream" - */ - public static String formatException(Throwable th) { - return "[" + th.getClass().getSimpleName() + "]: " + th.getMessage(); - } - /** * Check that the given needle is in the middle of the haystack, i.e. that the haystack * contains the needle and that it is not at the very start or end. diff --git a/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java b/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java index 7eff3a72d..e645922e7 100644 --- a/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java +++ b/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java @@ -30,21 +30,9 @@ public class CodeClimateConfigTest { assertThat(excludePaths, not(empty())); removeTestsExclusionOrThrow(excludePaths); for (String path : excludePaths) { - verifySourceFileExists(path); - } - } - - private static void verifySourceFileExists(String path) { - // Note ljacqu 20170323: In the future, we could have legitimate exclusions that don't fulfill these checks, - // in which case this test needs to be adapted accordingly. - if (!path.startsWith(TestHelper.SOURCES_FOLDER)) { - fail("Unexpected path '" + path + "': expected to start with sources folder"); - } else if (!path.endsWith(".java")) { - fail("Expected path '" + path + "' to end with '.java'"); - } - - if (!new File(path).exists()) { - fail("Path '" + path + "' does not exist!"); + if (!new File(path).exists()) { + fail("Path '" + path + "' does not exist!"); + } } } diff --git a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java index 9fbd27bdb..b04259b21 100644 --- a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java +++ b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.message; import fr.xephi.authme.TestHelper; import fr.xephi.authme.command.help.HelpSection; +import fr.xephi.authme.util.ExceptionUtils; import fr.xephi.authme.util.StringUtils; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.BeforeClass; @@ -85,7 +86,7 @@ public class YamlTextFileCheckerTest { errors.add("Message for '" + mandatoryKey + "' is empty"); } } catch (Exception e) { - errors.add("Could not load file: " + StringUtils.formatException(e)); + errors.add("Could not load file: " + ExceptionUtils.formatException(e)); } } } diff --git a/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java b/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java index 8685d7f33..9f60c53af 100644 --- a/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java @@ -4,8 +4,10 @@ import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import org.junit.Test; +import java.net.MalformedURLException; import java.util.ConcurrentModificationException; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; @@ -58,4 +60,16 @@ public class ExceptionUtilsTest { // given / when / then TestHelper.validateHasOnlyPrivateEmptyConstructor(ExceptionUtils.class); } + + @Test + public void shouldFormatException() { + // given + MalformedURLException ex = new MalformedURLException("Unrecognized URL format"); + + // when + String result = ExceptionUtils.formatException(ex); + + // then + assertThat(result, equalTo("[MalformedURLException]: Unrecognized URL format")); + } } diff --git a/src/test/java/fr/xephi/authme/util/StringUtilsTest.java b/src/test/java/fr/xephi/authme/util/StringUtilsTest.java index 7111f81b8..76e7ae754 100644 --- a/src/test/java/fr/xephi/authme/util/StringUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/StringUtilsTest.java @@ -3,8 +3,6 @@ package fr.xephi.authme.util; import fr.xephi.authme.TestHelper; import org.junit.Test; -import java.net.MalformedURLException; - import static java.util.Arrays.asList; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -63,18 +61,6 @@ public class StringUtilsTest { assertFalse(StringUtils.isEmpty(" test")); } - @Test - public void shouldFormatException() { - // given - MalformedURLException ex = new MalformedURLException("Unrecognized URL format"); - - // when - String result = StringUtils.formatException(ex); - - // then - assertThat(result, equalTo("[MalformedURLException]: Unrecognized URL format")); - } - @Test public void shouldGetDifferenceWithNullString() { // given/when/then From cff456c285cd3c8042579b4f3a1063e894a89de7 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 22 Apr 2018 12:51:41 +0200 Subject: [PATCH 129/155] Help message updater: specify the name of the updated file --- .../authme/UpdateHelpMessagesCommand.java | 5 +- .../service/HelpTranslationGenerator.java | 4 +- .../authme/UpdateHelpMessagesCommandTest.java | 70 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommandTest.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java index c737b98dd..d790962a5 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java @@ -7,6 +7,7 @@ import fr.xephi.authme.service.HelpTranslationGenerator; import org.bukkit.command.CommandSender; import javax.inject.Inject; +import java.io.File; import java.io.IOException; import java.util.List; @@ -24,8 +25,8 @@ public class UpdateHelpMessagesCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments) { try { - helpTranslationGenerator.updateHelpFile(); - sender.sendMessage("Successfully updated the help file"); + File updatedFile = helpTranslationGenerator.updateHelpFile(); + sender.sendMessage("Successfully updated the help file '" + updatedFile.getName() + "'"); helpMessagesService.reloadMessagesFile(); } catch (IOException e) { sender.sendMessage("Could not update help file: " + e.getMessage()); diff --git a/src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java b/src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java index 6ecd05490..21407b4f0 100644 --- a/src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java +++ b/src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java @@ -44,15 +44,17 @@ public class HelpTranslationGenerator { /** * Updates the help file to contain entries for all commands. * + * @return the help file that has been updated * @throws IOException if the help file cannot be written to */ - public void updateHelpFile() throws IOException { + public File updateHelpFile() throws IOException { String languageCode = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); File helpFile = new File(dataFolder, "messages/help_" + languageCode + ".yml"); Map helpEntries = generateHelpMessageEntries(); String helpEntriesYaml = exportToYaml(helpEntries); Files.write(helpFile.toPath(), helpEntriesYaml.getBytes(), StandardOpenOption.TRUNCATE_EXISTING); + return helpFile; } private static String exportToYaml(Map helpEntries) { diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommandTest.java new file mode 100644 index 000000000..94e53ff27 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommandTest.java @@ -0,0 +1,70 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.command.help.HelpMessagesService; +import fr.xephi.authme.service.HelpTranslationGenerator; +import org.bukkit.command.CommandSender; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link UpdateHelpMessagesCommand}. + */ +@RunWith(MockitoJUnitRunner.class) +public class UpdateHelpMessagesCommandTest { + + @InjectMocks + private UpdateHelpMessagesCommand command; + + @Mock + private HelpTranslationGenerator helpTranslationGenerator; + @Mock + private HelpMessagesService helpMessagesService; + + @BeforeClass + public static void setUpLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldUpdateHelpMessage() throws IOException { + // given + File updatedFile = new File("some/path/help_xx.yml"); + given(helpTranslationGenerator.updateHelpFile()).willReturn(updatedFile); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Collections.emptyList()); + + // then + verify(helpMessagesService).reloadMessagesFile(); + verify(sender).sendMessage("Successfully updated the help file 'help_xx.yml'"); + } + + @Test + public void shouldCatchAndReportException() throws IOException { + // given + given(helpTranslationGenerator.updateHelpFile()).willThrow(new IOException("Couldn't do the thing")); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Collections.emptyList()); + + // then + verify(sender).sendMessage("Could not update help file: Couldn't do the thing"); + verifyZeroInteractions(helpMessagesService); + } +} From ecdcaf24796e4668a7e622012ae08232165b87c1 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 22 Apr 2018 13:26:51 +0200 Subject: [PATCH 130/155] Fix failing tests --- .../process/register/AsyncRegisterTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 59c384bb1..029ff90ce 100644 --- a/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java +++ b/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java @@ -12,7 +12,6 @@ import fr.xephi.authme.process.register.executors.RegistrationMethod; import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; -import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.entity.Player; @@ -21,11 +20,11 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; + +import java.util.function.Function; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; @@ -108,7 +107,7 @@ public class AsyncRegisterTest { @Test @SuppressWarnings("unchecked") - public void shouldStopForFailedExecutorCheck() { + public void shouldStopForCanceledEvent() { // given String name = "edbert"; Player player = mockPlayerWithName(name); @@ -116,14 +115,13 @@ public class AsyncRegisterTest { given(playerCache.isAuthenticated(name)).willReturn(false); given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true); given(dataSource.isAuthAvailable(name)).willReturn(false); - given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); RegistrationExecutor executor = mock(RegistrationExecutor.class); TwoFactorRegisterParams params = TwoFactorRegisterParams.of(player); singletonStoreWillReturn(registrationExecutorStore, executor); - doAnswer((Answer) invocation -> { - ((AuthMeAsyncPreRegisterEvent) invocation.getArgument(0)).setCanRegister(false); - return null; - }).when(bukkitService).callEvent(any(AuthMeAsyncPreRegisterEvent.class)); + + AuthMeAsyncPreRegisterEvent canceledEvent = new AuthMeAsyncPreRegisterEvent(player, true); + canceledEvent.setCanRegister(false); + given(bukkitService.createAndCallEvent(any(Function.class))).willReturn(canceledEvent); // when asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, params); @@ -134,7 +132,7 @@ public class AsyncRegisterTest { @Test @SuppressWarnings("unchecked") - public void shouldStopForCancelledEvent() { + public void shouldStopForFailedExecutorCheck() { // given String name = "edbert"; Player player = mockPlayerWithName(name); @@ -143,12 +141,14 @@ public class AsyncRegisterTest { given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true); given(commonService.getProperty(RestrictionSettings.MAX_REGISTRATION_PER_IP)).willReturn(0); given(dataSource.isAuthAvailable(name)).willReturn(false); - given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); RegistrationExecutor executor = mock(RegistrationExecutor.class); TwoFactorRegisterParams params = TwoFactorRegisterParams.of(player); given(executor.isRegistrationAdmitted(params)).willReturn(false); singletonStoreWillReturn(registrationExecutorStore, executor); + given(bukkitService.createAndCallEvent(any(Function.class))) + .willReturn(new AuthMeAsyncPreRegisterEvent(player, false)); + // when asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, params); From d55b4bb3b5ad7d35154e8e614eb31cf7ab7bafc1 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 22 Apr 2018 21:27:38 +0200 Subject: [PATCH 131/155] #1561 Fix timing attacks by comparing hashes in constant time (#1563) * #1561 Fix timing attacks by comparing hashes in constant time * #1561 Fix timing attacks in phpBB fallback hashes - As noted by @games647 --- .../java/fr/xephi/authme/security/HashUtils.java | 15 +++++++++++++++ .../fr/xephi/authme/security/crypts/BCrypt2y.java | 4 +++- .../fr/xephi/authme/security/crypts/Joomla.java | 4 +++- .../fr/xephi/authme/security/crypts/Md5vB.java | 3 ++- .../fr/xephi/authme/security/crypts/PhpBB.java | 6 ++++-- .../security/crypts/SeparateSaltMethod.java | 4 +++- .../fr/xephi/authme/security/crypts/Sha256.java | 5 +++-- .../java/fr/xephi/authme/security/crypts/Smf.java | 4 +++- .../authme/security/crypts/UnsaltedMethod.java | 4 +++- .../security/crypts/UsernameSaltMethod.java | 4 +++- .../fr/xephi/authme/security/crypts/Wbb4.java | 5 +++-- .../xephi/authme/security/crypts/Wordpress.java | 4 +++- .../fr/xephi/authme/security/crypts/XAuth.java | 6 ++++-- .../security/HashAlgorithmIntegrationTest.java | 2 ++ .../fr/xephi/authme/security/HashUtilsTest.java | 9 +++++++++ 15 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/main/java/fr/xephi/authme/security/HashUtils.java b/src/main/java/fr/xephi/authme/security/HashUtils.java index 3578c80f3..642081c6d 100644 --- a/src/main/java/fr/xephi/authme/security/HashUtils.java +++ b/src/main/java/fr/xephi/authme/security/HashUtils.java @@ -1,6 +1,7 @@ package fr.xephi.authme.security; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -78,6 +79,20 @@ public final class HashUtils { return hash.length() > 3 && hash.substring(0, 2).equals("$2"); } + /** + * Checks whether the two strings are equal to each other in a time-constant manner. + * This helps to avoid timing side channel attacks, + * cf. issue #1561. + * + * @param string1 first string + * @param string2 second string + * @return true if the strings are equal to each other, false otherwise + */ + public static boolean isEqual(String string1, String string2) { + return MessageDigest.isEqual( + string1.getBytes(StandardCharsets.UTF_8), string2.getBytes(StandardCharsets.UTF_8)); + } + /** * Hash the message with the given algorithm and return the hash in its hexadecimal notation. * diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java b/src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java index cf4807abc..a22a68906 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java +++ b/src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java @@ -3,6 +3,8 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Usage; +import static fr.xephi.authme.security.HashUtils.isEqual; + @Recommendation(Usage.RECOMMENDED) public class BCrypt2y extends HexSaltedMethod { @@ -23,7 +25,7 @@ public class BCrypt2y extends HexSaltedMethod { // The salt is the first 29 characters of the hash String salt = hash.substring(0, 29); - return hash.equals(computeHash(password, salt, null)); + return isEqual(hash, computeHash(password, salt, null)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/Joomla.java b/src/main/java/fr/xephi/authme/security/crypts/Joomla.java index 462f5cb28..2ecc1d8d3 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Joomla.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Joomla.java @@ -4,6 +4,8 @@ import fr.xephi.authme.security.HashUtils; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Usage; +import static fr.xephi.authme.security.HashUtils.isEqual; + @Recommendation(Usage.ACCEPTABLE) public class Joomla extends HexSaltedMethod { @@ -16,7 +18,7 @@ public class Joomla extends HexSaltedMethod { public boolean comparePassword(String password, HashedPassword hashedPassword, String unusedName) { String hash = hashedPassword.getHash(); String[] hashParts = hash.split(":"); - return hashParts.length == 2 && hash.equals(computeHash(password, hashParts[1], null)); + return hashParts.length == 2 && isEqual(hash, computeHash(password, hashParts[1], null)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/Md5vB.java b/src/main/java/fr/xephi/authme/security/crypts/Md5vB.java index c244ec49d..00656964f 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Md5vB.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Md5vB.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security.crypts; +import static fr.xephi.authme.security.HashUtils.isEqual; import static fr.xephi.authme.security.HashUtils.md5; public class Md5vB extends HexSaltedMethod { @@ -13,7 +14,7 @@ public class Md5vB extends HexSaltedMethod { public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { String hash = hashedPassword.getHash(); String[] line = hash.split("\\$"); - return line.length == 4 && hash.equals(computeHash(password, line[2], name)); + return line.length == 4 && isEqual(hash, computeHash(password, line[2], name)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/PhpBB.java b/src/main/java/fr/xephi/authme/security/crypts/PhpBB.java index 70ac322d0..2d641706c 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/PhpBB.java +++ b/src/main/java/fr/xephi/authme/security/crypts/PhpBB.java @@ -10,6 +10,8 @@ import fr.xephi.authme.security.crypts.description.Usage; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; +import static fr.xephi.authme.security.HashUtils.isEqual; + /** * Encryption method compatible with phpBB3. *

    @@ -43,7 +45,7 @@ public class PhpBB implements EncryptionMethod { } else if (hash.length() == 34) { return PhpassSaltedMd5.phpbb_check_hash(password, hash); } else { - return PhpassSaltedMd5.md5(password).equals(hash); + return isEqual(hash, PhpassSaltedMd5.md5(password)); } } @@ -153,7 +155,7 @@ public class PhpBB implements EncryptionMethod { } private static boolean phpbb_check_hash(String password, String hash) { - return _hash_crypt_private(password, hash).equals(hash); + return isEqual(hash, _hash_crypt_private(password, hash)); // #1561: fix timing issue } } } diff --git a/src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java b/src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java index d0dacda4d..c0ec13dd7 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java +++ b/src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java @@ -1,5 +1,7 @@ package fr.xephi.authme.security.crypts; +import static fr.xephi.authme.security.HashUtils.isEqual; + /** * Common supertype for encryption methods which store their salt separately from the hash. */ @@ -19,7 +21,7 @@ public abstract class SeparateSaltMethod implements EncryptionMethod { @Override public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { - return hashedPassword.getHash().equals(computeHash(password, hashedPassword.getSalt(), null)); + return isEqual(hashedPassword.getHash(), computeHash(password, hashedPassword.getSalt(), null)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/Sha256.java b/src/main/java/fr/xephi/authme/security/crypts/Sha256.java index 1b77a2e44..ce6b25492 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Sha256.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Sha256.java @@ -3,6 +3,7 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Usage; +import static fr.xephi.authme.security.HashUtils.isEqual; import static fr.xephi.authme.security.HashUtils.sha256; @Recommendation(Usage.RECOMMENDED) @@ -14,10 +15,10 @@ public class Sha256 extends HexSaltedMethod { } @Override - public boolean comparePassword(String password, HashedPassword hashedPassword, String playerName) { + public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { String hash = hashedPassword.getHash(); String[] line = hash.split("\\$"); - return line.length == 4 && hash.equals(computeHash(password, line[2], "")); + return line.length == 4 && isEqual(hash, computeHash(password, line[2], name)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/Smf.java b/src/main/java/fr/xephi/authme/security/crypts/Smf.java index 24d28fe6c..e24c1b83d 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Smf.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Smf.java @@ -7,6 +7,8 @@ import fr.xephi.authme.security.crypts.description.SaltType; import fr.xephi.authme.security.crypts.description.Usage; import fr.xephi.authme.util.RandomStringUtils; +import static fr.xephi.authme.security.HashUtils.isEqual; + /** * Hashing algorithm for SMF forums. *

    @@ -32,7 +34,7 @@ public class Smf implements EncryptionMethod { @Override public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { - return computeHash(password, null, name).equals(hashedPassword.getHash()); + return isEqual(hashedPassword.getHash(), computeHash(password, null, name)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java b/src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java index a8f2040e5..33815ec77 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java +++ b/src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java @@ -5,6 +5,8 @@ import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.SaltType; import fr.xephi.authme.security.crypts.description.Usage; +import static fr.xephi.authme.security.HashUtils.isEqual; + /** * Common type for encryption methods which do not use any salt whatsoever. */ @@ -26,7 +28,7 @@ public abstract class UnsaltedMethod implements EncryptionMethod { @Override public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { - return hashedPassword.getHash().equals(computeHash(password)); + return isEqual(hashedPassword.getHash(), computeHash(password)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java b/src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java index 23101e22a..f5930fcf5 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java +++ b/src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java @@ -5,6 +5,8 @@ import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.SaltType; import fr.xephi.authme.security.crypts.description.Usage; +import static fr.xephi.authme.security.HashUtils.isEqual; + /** * Common supertype of encryption methods that use a player's username * (or something based on it) as embedded salt. @@ -23,7 +25,7 @@ public abstract class UsernameSaltMethod implements EncryptionMethod { @Override public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { - return hashedPassword.getHash().equals(computeHash(password, name).getHash()); + return isEqual(hashedPassword.getHash(), computeHash(password, name).getHash()); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/Wbb4.java b/src/main/java/fr/xephi/authme/security/crypts/Wbb4.java index d1d4953d1..f396c5d84 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Wbb4.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Wbb4.java @@ -3,6 +3,7 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Usage; +import static fr.xephi.authme.security.HashUtils.isEqual; import static fr.xephi.authme.security.crypts.BCryptService.hashpw; @Recommendation(Usage.RECOMMENDED) @@ -14,12 +15,12 @@ public class Wbb4 extends HexSaltedMethod { } @Override - public boolean comparePassword(String password, HashedPassword hashedPassword, String playerName) { + public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { if (hashedPassword.getHash().length() != 60) { return false; } String salt = hashedPassword.getHash().substring(0, 29); - return computeHash(password, salt, null).equals(hashedPassword.getHash()); + return isEqual(hashedPassword.getHash(), computeHash(password, salt, name)); } @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/Wordpress.java b/src/main/java/fr/xephi/authme/security/crypts/Wordpress.java index 768b92c5d..f70c09496 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Wordpress.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Wordpress.java @@ -12,6 +12,8 @@ import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Arrays; +import static fr.xephi.authme.security.HashUtils.isEqual; + @Recommendation(Usage.ACCEPTABLE) @HasSalt(value = SaltType.TEXT, length = 9) // Note ljacqu 20151228: Wordpress is actually a salted algorithm but salt generation is handled internally @@ -115,7 +117,7 @@ public class Wordpress extends UnsaltedMethod { public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { String hash = hashedPassword.getHash(); String comparedHash = crypt(password, hash); - return comparedHash.equals(hash); + return isEqual(hash, comparedHash); } } diff --git a/src/main/java/fr/xephi/authme/security/crypts/XAuth.java b/src/main/java/fr/xephi/authme/security/crypts/XAuth.java index 9f921b6ae..62f2e0d71 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/XAuth.java +++ b/src/main/java/fr/xephi/authme/security/crypts/XAuth.java @@ -3,6 +3,8 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Usage; +import static fr.xephi.authme.security.HashUtils.isEqual; + @Recommendation(Usage.RECOMMENDED) public class XAuth extends HexSaltedMethod { @@ -23,14 +25,14 @@ public class XAuth extends HexSaltedMethod { } @Override - public boolean comparePassword(String password, HashedPassword hashedPassword, String playerName) { + public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { String hash = hashedPassword.getHash(); int saltPos = password.length() >= hash.length() ? hash.length() - 1 : password.length(); if (saltPos + 12 > hash.length()) { return false; } String salt = hash.substring(saltPos, saltPos + 12); - return hash.equals(computeHash(password, salt, null)); + return isEqual(hash, computeHash(password, salt, name)); } @Override diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java index fc9fd0d66..e59dae647 100644 --- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.security; import ch.jalu.injector.Injector; import ch.jalu.injector.InjectorBuilder; +import fr.xephi.authme.TestHelper; import fr.xephi.authme.security.crypts.Argon2; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; @@ -40,6 +41,7 @@ public class HashAlgorithmIntegrationTest { given(settings.getProperty(SecuritySettings.PBKDF2_NUMBER_OF_ROUNDS)).willReturn(10_000); injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); injector.register(Settings.class, settings); + TestHelper.setupLogger(); } @Test diff --git a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java b/src/test/java/fr/xephi/authme/security/HashUtilsTest.java index 5c1fda220..440e748a2 100644 --- a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java +++ b/src/test/java/fr/xephi/authme/security/HashUtilsTest.java @@ -123,4 +123,13 @@ public class HashUtilsTest { assertThat(HashUtils.isValidBcryptHash("#2ae5fc78"), equalTo(false)); } + @Test + public void shouldCompareStrings() { + // given / when / then + assertThat(HashUtils.isEqual("test", "test"), equalTo(true)); + assertThat(HashUtils.isEqual("test", "Test"), equalTo(false)); + assertThat(HashUtils.isEqual("1234", "1234."), equalTo(false)); + assertThat(HashUtils.isEqual("ພາສາຫວຽດນາມ", "ພາສາຫວຽດນາມ"), equalTo(true)); + assertThat(HashUtils.isEqual("test", "tëst"), equalTo(false)); + } } From b69767c705a55ad682975b6cb8b5ddf14a429ec8 Mon Sep 17 00:00:00 2001 From: games647 Date: Tue, 1 May 2018 13:39:19 +0200 Subject: [PATCH 132/155] Upgrade jacoco dependency to fix Java 10 compatibilty and CircleCI tests --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6756e67c..882d09a7e 100644 --- a/pom.xml +++ b/pom.xml @@ -150,7 +150,7 @@ org.jacoco jacoco-maven-plugin - 0.8.0 + 0.8.1 pre-unit-test From 1e3ed795c10125356a56ed1d34c5aa0bfbcc850c Mon Sep 17 00:00:00 2001 From: ljacqu Date: Tue, 1 May 2018 22:49:07 +0200 Subject: [PATCH 133/155] #1141 2FA implementation fixes - Merge TotpService into TotpAuthenticator - Add missing tests - Migrate old 2fa enabled key to new one --- .../executable/totp/ConfirmTotpCommand.java | 6 +- .../executable/totp/RemoveTotpCommand.java | 6 +- .../executable/totp/TotpCodeCommand.java | 6 +- .../message/updater/MessageUpdater.java | 26 +++- .../security/totp/TotpAuthenticator.java | 9 +- .../authme/security/totp/TotpService.java | 18 --- .../executable/totp/AddTotpCommandTest.java | 93 ++++++++++++ .../totp/ConfirmTotpCommandTest.java | 139 ++++++++++++++++++ .../executable/totp/TotpBaseCommandTest.java | 47 ++++++ .../message/updater/MessageUpdaterTest.java | 17 +++ .../security/totp/TotpAuthenticatorTest.java | 20 +++ .../authme/security/totp/TotpServiceTest.java | 46 ------ .../xephi/authme/message/messages_test2.yml | 2 + 13 files changed, 356 insertions(+), 79 deletions(-) delete mode 100644 src/main/java/fr/xephi/authme/security/totp/TotpService.java create mode 100644 src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java create mode 100644 src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java create mode 100644 src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java delete mode 100644 src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java index 52d1a9802..9dcd99a65 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java @@ -33,13 +33,17 @@ public class ConfirmTotpCommand extends PlayerCommand { messages.send(player, MessageKey.REGISTER_MESSAGE); } else if (auth.getTotpKey() != null) { messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); + } else { + verifyTotpCodeConfirmation(player, arguments.get(0)); } + } + private void verifyTotpCodeConfirmation(Player player, String inputTotpCode) { final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player); if (totpDetails == null) { messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE); } else { - boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, arguments.get(0)); + boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, inputTotpCode); if (isCodeValid) { generateTotpService.removeGenerateTotpKey(player); dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey()); diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java index 20ef4a1bf..1ad42cb8a 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java @@ -5,7 +5,7 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; -import fr.xephi.authme.security.totp.TotpService; +import fr.xephi.authme.security.totp.TotpAuthenticator; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -20,7 +20,7 @@ public class RemoveTotpCommand extends PlayerCommand { private DataSource dataSource; @Inject - private TotpService totpService; + private TotpAuthenticator totpAuthenticator; @Inject private Messages messages; @@ -31,7 +31,7 @@ public class RemoveTotpCommand extends PlayerCommand { if (auth.getTotpKey() == null) { messages.send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR); } else { - if (totpService.verifyCode(auth, arguments.get(0))) { + if (totpAuthenticator.checkCode(auth, arguments.get(0))) { dataSource.removeTotpKey(auth.getNickname()); messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS); } else { diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java index d8b7a28f9..3ddbb9649 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java @@ -10,7 +10,7 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.process.login.AsynchronousLogin; -import fr.xephi.authme.security.totp.TotpService; +import fr.xephi.authme.security.totp.TotpAuthenticator; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -31,7 +31,7 @@ public class TotpCodeCommand extends PlayerCommand { private Messages messages; @Inject - private TotpService totpService; + private TotpAuthenticator totpAuthenticator; @Inject private DataSource dataSource; @@ -61,7 +61,7 @@ public class TotpCodeCommand extends PlayerCommand { } private void processCode(Player player, PlayerAuth auth, String inputCode) { - boolean isCodeValid = totpService.verifyCode(auth, inputCode); + boolean isCodeValid = totpAuthenticator.checkCode(auth, inputCode); if (isCodeValid) { asynchronousLogin.performLogin(player, auth); } else { diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java index 579968eec..433f0b17b 100644 --- a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java @@ -5,7 +5,7 @@ import ch.jalu.configme.configurationdata.ConfigurationData; import ch.jalu.configme.configurationdata.PropertyListBuilder; import ch.jalu.configme.properties.Property; import ch.jalu.configme.properties.StringProperty; -import ch.jalu.configme.resource.YamlFileResource; +import ch.jalu.configme.resource.PropertyResource; import com.google.common.collect.ImmutableMap; import com.google.common.io.Files; import fr.xephi.authme.ConsoleLogger; @@ -57,14 +57,16 @@ public class MessageUpdater { */ private boolean migrateAndSave(File userFile, JarMessageSource jarMessageSource) { // YamlConfiguration escapes all special characters when saving, making the file hard to use, so use ConfigMe - YamlFileResource userResource = new MigraterYamlFileResource(userFile); + PropertyResource userResource = new MigraterYamlFileResource(userFile); // Step 1: Migrate any old keys in the file to the new paths boolean movedOldKeys = migrateOldKeys(userResource); - // Step 2: Take any missing messages from the message files shipped in the AuthMe JAR + // Step 2: Perform newer migrations + boolean movedNewerKeys = migrateKeys(userResource); + // Step 3: Take any missing messages from the message files shipped in the AuthMe JAR boolean addedMissingKeys = addMissingKeys(jarMessageSource, userResource); - if (movedOldKeys || addedMissingKeys) { + if (movedOldKeys || movedNewerKeys || addedMissingKeys) { backupMessagesFile(userFile); SettingsManager settingsManager = new SettingsManager(userResource, null, CONFIGURATION_DATA); @@ -75,7 +77,19 @@ public class MessageUpdater { return false; } - private boolean migrateOldKeys(YamlFileResource userResource) { + private boolean migrateKeys(PropertyResource userResource) { + return moveIfApplicable(userResource, "misc.two_factor_create", MessageKey.TWO_FACTOR_CREATE.getKey()); + } + + private static boolean moveIfApplicable(PropertyResource resource, String oldPath, String newPath) { + if (resource.getString(newPath) == null && resource.getString(oldPath) != null) { + resource.setValue(newPath, resource.getString(oldPath)); + return true; + } + return false; + } + + private boolean migrateOldKeys(PropertyResource userResource) { boolean hasChange = OldMessageKeysMigrater.migrateOldPaths(userResource); if (hasChange) { ConsoleLogger.info("Old keys have been moved to the new ones in your messages_xx.yml file"); @@ -83,7 +97,7 @@ public class MessageUpdater { return hasChange; } - private boolean addMissingKeys(JarMessageSource jarMessageSource, YamlFileResource userResource) { + private boolean addMissingKeys(JarMessageSource jarMessageSource, PropertyResource userResource) { List addedKeys = new ArrayList<>(); for (Property property : CONFIGURATION_DATA.getProperties()) { final String key = property.getPath(); diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java index 9fc1c6a2a..eb922cf43 100644 --- a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java +++ b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java @@ -4,13 +4,14 @@ import com.warrenstrange.googleauth.GoogleAuthenticator; import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; import com.warrenstrange.googleauth.IGoogleAuthenticator; +import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; import javax.inject.Inject; /** - * Provides rudimentary TOTP functions (wraps third-party TOTP implementation). + * Provides TOTP functions (wrapping a third-party TOTP implementation). */ public class TotpAuthenticator { @@ -30,6 +31,10 @@ public class TotpAuthenticator { return new GoogleAuthenticator(); } + public boolean checkCode(PlayerAuth auth, String totpCode) { + return checkCode(auth.getTotpKey(), totpCode); + } + /** * Returns whether the given input code matches for the provided TOTP key. * @@ -58,7 +63,7 @@ public class TotpAuthenticator { private final String totpKey; private final String authenticatorQrCodeUrl; - TotpGenerationResult(String totpKey, String authenticatorQrCodeUrl) { + public TotpGenerationResult(String totpKey, String authenticatorQrCodeUrl) { this.totpKey = totpKey; this.authenticatorQrCodeUrl = authenticatorQrCodeUrl; } diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpService.java b/src/main/java/fr/xephi/authme/security/totp/TotpService.java deleted file mode 100644 index 15e3381f0..000000000 --- a/src/main/java/fr/xephi/authme/security/totp/TotpService.java +++ /dev/null @@ -1,18 +0,0 @@ -package fr.xephi.authme.security.totp; - -import fr.xephi.authme.data.auth.PlayerAuth; - -import javax.inject.Inject; - -/** - * Service for TOTP actions. - */ -public class TotpService { - - @Inject - private TotpAuthenticator totpAuthenticator; - - public boolean verifyCode(PlayerAuth auth, String totpCode) { - return totpAuthenticator.checkCode(auth.getTotpKey(), totpCode); - } -} diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java new file mode 100644 index 000000000..8791226a5 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java @@ -0,0 +1,93 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.security.totp.GenerateTotpService; +import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; +import org.bukkit.entity.Player; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link AddTotpCommand}. + */ +@RunWith(MockitoJUnitRunner.class) +public class AddTotpCommandTest { + + @InjectMocks + private AddTotpCommand addTotpCommand; + + @Mock + private GenerateTotpService generateTotpService; + @Mock + private DataSource dataSource; + @Mock + private Messages messages; + + @Test + public void shouldHandleNonExistentUser() { + // given + Player player = mockPlayerWithName("bob"); + given(dataSource.getAuth("bob")).willReturn(null); + + // when + addTotpCommand.runCommand(player, Collections.emptyList()); + + // then + verify(messages).send(player, MessageKey.REGISTER_MESSAGE); + verifyZeroInteractions(generateTotpService); + } + + @Test + public void shouldNotAddCodeForAlreadyExistingTotp() { + // given + Player player = mockPlayerWithName("arend"); + PlayerAuth auth = PlayerAuth.builder().name("arend") + .totpKey("TOTP2345").build(); + given(dataSource.getAuth("arend")).willReturn(auth); + + // when + addTotpCommand.runCommand(player, Collections.emptyList()); + + // then + verify(messages).send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); + verifyZeroInteractions(generateTotpService); + } + + @Test + public void shouldGenerateTotpCode() { + // given + Player player = mockPlayerWithName("charles"); + PlayerAuth auth = PlayerAuth.builder().name("charles").build(); + given(dataSource.getAuth("charles")).willReturn(auth); + + TotpGenerationResult generationResult = new TotpGenerationResult( + "777Key214", "http://example.org/qr-code/link"); + given(generateTotpService.generateTotpKey(player)).willReturn(generationResult); + + // when + addTotpCommand.runCommand(player, Collections.emptyList()); + + // then + verify(messages).send(player, MessageKey.TWO_FACTOR_CREATE, generationResult.getTotpKey(), generationResult.getAuthenticatorQrCodeUrl()); + verify(messages).send(player, MessageKey.TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED); + } + + private static Player mockPlayerWithName(String name) { + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + return player; + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java new file mode 100644 index 000000000..0d921d37a --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java @@ -0,0 +1,139 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.security.totp.GenerateTotpService; +import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; +import org.bukkit.entity.Player; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link ConfirmTotpCommand}. + */ +@RunWith(MockitoJUnitRunner.class) +public class ConfirmTotpCommandTest { + + @InjectMocks + private ConfirmTotpCommand command; + + @Mock + private GenerateTotpService generateTotpService; + @Mock + private DataSource dataSource; + @Mock + private Messages messages; + + @Test + public void shouldAddTotpCodeToUserAfterSuccessfulConfirmation() { + // given + Player player = mock(Player.class); + String playerName = "George"; + given(player.getName()).willReturn(playerName); + PlayerAuth auth = PlayerAuth.builder().name(playerName).build(); + given(dataSource.getAuth(playerName)).willReturn(auth); + given(generateTotpService.getGeneratedTotpKey(player)).willReturn(new TotpGenerationResult("totp-key", "url-not-relevant")); + String totpCode = "954321"; + given(generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, totpCode)).willReturn(true); + + // when + command.runCommand(player, Collections.singletonList(totpCode)); + + // then + verify(generateTotpService).isTotpCodeCorrectForGeneratedTotpKey(player, totpCode); + verify(generateTotpService).removeGenerateTotpKey(player); + verify(dataSource).setTotpKey(playerName, "totp-key"); + verify(messages).send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS); + } + + @Test + public void shouldHandleWrongTotpCode() { + // given + Player player = mock(Player.class); + String playerName = "George"; + given(player.getName()).willReturn(playerName); + PlayerAuth auth = PlayerAuth.builder().name(playerName).build(); + given(dataSource.getAuth(playerName)).willReturn(auth); + given(generateTotpService.getGeneratedTotpKey(player)).willReturn(new TotpGenerationResult("totp-key", "url-not-relevant")); + String totpCode = "754321"; + given(generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, totpCode)).willReturn(false); + + // when + command.runCommand(player, Collections.singletonList(totpCode)); + + // then + verify(generateTotpService).isTotpCodeCorrectForGeneratedTotpKey(player, totpCode); + verify(generateTotpService, never()).removeGenerateTotpKey(any(Player.class)); + verify(dataSource, only()).getAuth(playerName); + verify(messages).send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE); + } + + @Test + public void shouldHandleMissingTotpKey() { + // given + Player player = mock(Player.class); + String playerName = "George"; + given(player.getName()).willReturn(playerName); + PlayerAuth auth = PlayerAuth.builder().name(playerName).build(); + given(dataSource.getAuth(playerName)).willReturn(auth); + given(generateTotpService.getGeneratedTotpKey(player)).willReturn(null); + + // when + command.runCommand(player, Collections.singletonList("871634")); + + // then + verify(generateTotpService, only()).getGeneratedTotpKey(player); + verify(dataSource, only()).getAuth(playerName); + verify(messages).send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE); + } + + @Test + public void shouldStopForAlreadyExistingTotpKeyOnAccount() { + // given + Player player = mock(Player.class); + String playerName = "George"; + given(player.getName()).willReturn(playerName); + PlayerAuth auth = PlayerAuth.builder().name(playerName).totpKey("A987234").build(); + given(dataSource.getAuth(playerName)).willReturn(auth); + + // when + command.runCommand(player, Collections.singletonList("871634")); + + // then + verify(dataSource, only()).getAuth(playerName); + verifyZeroInteractions(generateTotpService); + verify(messages).send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); + } + + @Test + public void shouldHandleMissingAuthAccount() { + // given + Player player = mock(Player.class); + String playerName = "George"; + given(player.getName()).willReturn(playerName); + given(dataSource.getAuth(playerName)).willReturn(null); + + // when + command.runCommand(player, Collections.singletonList("984685")); + + // then + verify(dataSource, only()).getAuth(playerName); + verifyZeroInteractions(generateTotpService); + verify(messages).send(player, MessageKey.REGISTER_MESSAGE); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java new file mode 100644 index 000000000..0e279f687 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java @@ -0,0 +1,47 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.command.CommandMapper; +import fr.xephi.authme.command.FoundCommandResult; +import fr.xephi.authme.command.help.HelpProvider; +import org.bukkit.command.CommandSender; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link TotpBaseCommand}. + */ +@RunWith(MockitoJUnitRunner.class) +public class TotpBaseCommandTest { + + @InjectMocks + private TotpBaseCommand command; + + @Mock + private CommandMapper mapper; + @Mock + private HelpProvider helpProvider; + + @Test + public void shouldOutputHelp() { + // given + CommandSender sender = mock(CommandSender.class); + FoundCommandResult mappingResult = mock(FoundCommandResult.class); + given(mapper.mapPartsToCommand(sender, Collections.singletonList("totp"))).willReturn(mappingResult); + + // when + command.executeCommand(sender, Collections.emptyList()); + + // then + verify(mapper).mapPartsToCommand(sender, Collections.singletonList("totp")); + verify(helpProvider).outputHelp(sender, mappingResult, HelpProvider.SHOW_CHILDREN); + } +} diff --git a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java index 83f5a5c63..254e0b1a6 100644 --- a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java +++ b/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java @@ -100,6 +100,23 @@ public class MessageUpdaterTest { equalTo("seconds in plural")); } + @Test + public void shouldPerformNewerMigrations() throws IOException { + // given + File messagesFile = temporaryFolder.newFile(); + Files.copy(TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "message/messages_test2.yml"), messagesFile); + + // when + boolean wasChanged = messageUpdater.migrateAndSave(messagesFile, "messages/messages_en.yml", "messages/messages_en.yml"); + + // then + assertThat(wasChanged, equalTo(true)); + FileConfiguration configuration = YamlConfiguration.loadConfiguration(messagesFile); + assertThat(configuration.getString(MessageKey.TWO_FACTOR_CREATE.getKey()), equalTo("Old 2fa create text")); + assertThat(configuration.getString(MessageKey.WRONG_PASSWORD.getKey()), equalTo("test2 - wrong password")); // from pre-5.5 key + assertThat(configuration.getString(MessageKey.SECOND.getKey()), equalTo("second")); // from messages_en.yml + } + @Test public void shouldHaveAllKeysInConfigurationData() { // given diff --git a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java index 27434cced..3afc81817 100644 --- a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java +++ b/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java @@ -1,6 +1,7 @@ package fr.xephi.authme.security.totp; import com.warrenstrange.googleauth.IGoogleAuthenticator; +import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; @@ -86,6 +87,25 @@ public class TotpAuthenticatorTest { verifyZeroInteractions(googleAuthenticator); } + @Test + public void shouldVerifyCode() { + // given + String totpKey = "ASLO43KDF2J"; + PlayerAuth auth = PlayerAuth.builder() + .name("Maya") + .totpKey(totpKey) + .build(); + String inputCode = "408435"; + given(totpAuthenticator.checkCode(totpKey, inputCode)).willReturn(true); + + // when + boolean result = totpAuthenticator.checkCode(auth, inputCode); + + // then + assertThat(result, equalTo(true)); + verify(googleAuthenticator).authorize(totpKey, 408435); + } + private final class TotpAuthenticatorTestImpl extends TotpAuthenticator { TotpAuthenticatorTestImpl(BukkitService bukkitService) { diff --git a/src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java b/src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java deleted file mode 100644 index 7d321cf19..000000000 --- a/src/test/java/fr/xephi/authme/security/totp/TotpServiceTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package fr.xephi.authme.security.totp; - -import fr.xephi.authme.data.auth.PlayerAuth; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; - -/** - * Test for {@link TotpService}. - */ -@RunWith(MockitoJUnitRunner.class) -public class TotpServiceTest { - - @InjectMocks - private TotpService totpService; - - @Mock - private TotpAuthenticator totpAuthenticator; - - @Test - public void shouldVerifyCode() { - // given - String totpKey = "ASLO43KDF2J"; - PlayerAuth auth = PlayerAuth.builder() - .name("Maya") - .totpKey(totpKey) - .build(); - String inputCode = "408435"; - given(totpAuthenticator.checkCode(totpKey, inputCode)).willReturn(true); - - // when - boolean result = totpService.verifyCode(auth, inputCode); - - // then - assertThat(result, equalTo(true)); - verify(totpAuthenticator).checkCode(totpKey, inputCode); - } - -} diff --git a/src/test/resources/fr/xephi/authme/message/messages_test2.yml b/src/test/resources/fr/xephi/authme/message/messages_test2.yml index e4a607239..f2871ddbb 100644 --- a/src/test/resources/fr/xephi/authme/message/messages_test2.yml +++ b/src/test/resources/fr/xephi/authme/message/messages_test2.yml @@ -4,3 +4,5 @@ unknown_user: 'Message from test2' login: 'test2 - login' not_logged_in: 'test2 - not logged in' wrong_pwd: 'test2 - wrong password' +misc: + two_factor_create: 'Old 2fa create text' From 4f9a869a46012682ad57058af86a4db0fe5cbd3d Mon Sep 17 00:00:00 2001 From: rafael59r2 Date: Sun, 13 May 2018 14:15:01 +0100 Subject: [PATCH 134/155] Translation messages_pt.yml (#1569) --- src/main/resources/messages/messages_pt.yml | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index b7cabdc8b..2a7355416 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -79,7 +79,7 @@ on_join_validation: country_banned: 'O seu país está banido deste servidor' not_owner_error: 'Não é o proprietário da conta. Por favor, escolha outro nome!' invalid_name_case: 'Deve se juntar usando nome de usuário %valid, não %invalid.' - # TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.' + quick_command: 'Você usou o comando demasiado rapido por favor re-entre no servidor e aguarde antes de digitar qualquer comando.' # Email email: @@ -99,8 +99,8 @@ email: send_failure: 'Não foi possivel enviar o email. Por favor contate um administrador.' change_password_expired: 'Você não pode mais alterar a sua password usando este comando.' email_cooldown_error: '&cUm email já foi enviado recentemente.Por favor, espere %time antes de enviar novamente' - # TODO add_not_allowed: '&cAdding email was not allowed' - # TODO change_not_allowed: '&cChanging email was not allowed' + add_not_allowed: '&cAdicionar e-mail não é permitido' + change_not_allowed: '&cAlterar e-mail não é permitido' # Password recovery by email recovery: @@ -119,18 +119,18 @@ captcha: usage_captcha: '&cPrecisa digitar um captcha, escreva: /captcha %captcha_code' wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha %captcha_code' valid_captcha: '&cO seu captcha é válido!' - # TODO captcha_for_registration: 'To register you have to solve a captcha first, please use the command: /captcha %captcha_code' - # TODO register_captcha_valid: '&2Valid captcha! You may now register with /register' + captcha_for_registration: 'Para se registar tem de resolver o captcha primeiro, por favor use: /captcha %captcha_code' + register_captcha_valid: '&2Captcha Valido! Agora você pode te registar com /register' # Verification code verification: - # TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.' - # TODO command_usage: '&cUsage: /verification ' - # TODO incorrect_code: '&cIncorrect code, please type "/verification " into the chat, using the code you received by email' - # TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!' - # TODO already_verified: '&2You can already execute every sensitive command within the current session!' - # TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!' - # TODO email_needed: '&3To verify your identity you need to link an email address with your account!!' + code_required: '&3Este codigo é sensivel e requer uma verificação por e-mail! Verifique na sua caixa de entrada e siga as instruções do e-mail.' + command_usage: '&cUso: /verification ' + incorrect_code: '&cCodigo incorreto, por favor digite "/verification " no chat, utilizando o codigo que recebeu no e-mail.' + success: '&2Sua identidade foi verificada! Agora você pode executar todos os comandos nesta sessão!' + already_verified: '&2Você já pode digitar todos os comandos sensiveis nesta sessão!' + code_expired: '&3Seu codigo expirou! Execute outro comando sensivel para obter um novo codigo!' + email_needed: '&3Para confirmar a sua identidade necessita de associar um endereço de e-mail!!' # Time units time: From 729c567dd56e77050a804c51756cd549689f42ba Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 13 May 2018 18:49:40 +0200 Subject: [PATCH 135/155] #1141 Check that user is logged in before changing TOTP key - Use PlayerCache to check that user is logged in where appropriate - Add log statements --- .../executable/totp/AddTotpCommand.java | 8 +- .../executable/totp/ConfirmTotpCommand.java | 27 +++- .../executable/totp/RemoveTotpCommand.java | 25 ++- .../executable/totp/TotpCodeCommand.java | 5 + .../fr/xephi/authme/data/auth/PlayerAuth.java | 4 + .../executable/totp/AddTotpCommandTest.java | 14 +- .../totp/ConfirmTotpCommandTest.java | 47 ++++-- .../totp/RemoveTotpCommandTest.java | 149 ++++++++++++++++++ 8 files changed, 244 insertions(+), 35 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java index a52741b22..e8a78bd1d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java @@ -2,7 +2,7 @@ package fr.xephi.authme.command.executable.totp; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.security.totp.GenerateTotpService; @@ -21,16 +21,16 @@ public class AddTotpCommand extends PlayerCommand { private GenerateTotpService generateTotpService; @Inject - private DataSource dataSource; + private PlayerCache playerCache; @Inject private Messages messages; @Override protected void runCommand(Player player, List arguments) { - PlayerAuth auth = dataSource.getAuth(player.getName()); + PlayerAuth auth = playerCache.getAuth(player.getName()); if (auth == null) { - messages.send(player, MessageKey.REGISTER_MESSAGE); + messages.send(player, MessageKey.NOT_LOGGED_IN); } else if (auth.getTotpKey() == null) { TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player); messages.send(player, MessageKey.TWO_FACTOR_CREATE, diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java index 9dcd99a65..1ab8192be 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java @@ -1,7 +1,9 @@ package fr.xephi.authme.command.executable.totp; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; @@ -20,6 +22,9 @@ public class ConfirmTotpCommand extends PlayerCommand { @Inject private GenerateTotpService generateTotpService; + @Inject + private PlayerCache playerCache; + @Inject private DataSource dataSource; @@ -28,17 +33,17 @@ public class ConfirmTotpCommand extends PlayerCommand { @Override protected void runCommand(Player player, List arguments) { - PlayerAuth auth = dataSource.getAuth(player.getName()); + PlayerAuth auth = playerCache.getAuth(player.getName()); if (auth == null) { - messages.send(player, MessageKey.REGISTER_MESSAGE); + messages.send(player, MessageKey.NOT_LOGGED_IN); } else if (auth.getTotpKey() != null) { messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); } else { - verifyTotpCodeConfirmation(player, arguments.get(0)); + verifyTotpCodeConfirmation(player, auth, arguments.get(0)); } } - private void verifyTotpCodeConfirmation(Player player, String inputTotpCode) { + private void verifyTotpCodeConfirmation(Player player, PlayerAuth auth, String inputTotpCode) { final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player); if (totpDetails == null) { messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE); @@ -46,11 +51,21 @@ public class ConfirmTotpCommand extends PlayerCommand { boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, inputTotpCode); if (isCodeValid) { generateTotpService.removeGenerateTotpKey(player); - dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey()); - messages.send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS); + insertTotpKeyIntoDatabase(player, auth, totpDetails); } else { messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE); } } } + + private void insertTotpKeyIntoDatabase(Player player, PlayerAuth auth, TotpGenerationResult totpDetails) { + if (dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey())) { + messages.send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS); + auth.setTotpKey(totpDetails.getTotpKey()); + playerCache.updatePlayer(auth); + ConsoleLogger.info("Player '" + player.getName() + "' has successfully added a TOTP key to their account"); + } else { + messages.send(player, MessageKey.ERROR); + } + } } diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java index 1ad42cb8a..ebcf554c2 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java @@ -1,7 +1,9 @@ package fr.xephi.authme.command.executable.totp; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; @@ -19,6 +21,9 @@ public class RemoveTotpCommand extends PlayerCommand { @Inject private DataSource dataSource; + @Inject + private PlayerCache playerCache; + @Inject private TotpAuthenticator totpAuthenticator; @@ -27,16 +32,28 @@ public class RemoveTotpCommand extends PlayerCommand { @Override protected void runCommand(Player player, List arguments) { - PlayerAuth auth = dataSource.getAuth(player.getName()); - if (auth.getTotpKey() == null) { + PlayerAuth auth = playerCache.getAuth(player.getName()); + if (auth == null) { + messages.send(player, MessageKey.NOT_LOGGED_IN); + } else if (auth.getTotpKey() == null) { messages.send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR); } else { if (totpAuthenticator.checkCode(auth, arguments.get(0))) { - dataSource.removeTotpKey(auth.getNickname()); - messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS); + removeTotpKeyFromDatabase(player, auth); } else { messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE); } } } + + private void removeTotpKeyFromDatabase(Player player, PlayerAuth auth) { + if (dataSource.removeTotpKey(auth.getNickname())) { + auth.setTotpKey(null); + playerCache.updatePlayer(auth); + messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS); + ConsoleLogger.info("Player '" + player.getName() + "' removed their TOTP key"); + } else { + messages.send(player, MessageKey.ERROR); + } + } } diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java index 3ddbb9649..398759028 100644 --- a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java @@ -1,5 +1,6 @@ package fr.xephi.authme.command.executable.totp; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -56,6 +57,8 @@ public class TotpCodeCommand extends PlayerCommand { if (limbo != null && limbo.getState() == LimboPlayerState.TOTP_REQUIRED) { processCode(player, auth, arguments.get(0)); } else { + ConsoleLogger.debug(() -> "Aborting TOTP check for player '" + player.getName() + + "'. Invalid limbo state: " + (limbo == null ? "no limbo" : limbo.getState())); messages.send(player, MessageKey.LOGIN_MESSAGE); } } @@ -63,8 +66,10 @@ public class TotpCodeCommand extends PlayerCommand { private void processCode(Player player, PlayerAuth auth, String inputCode) { boolean isCodeValid = totpAuthenticator.checkCode(auth, inputCode); if (isCodeValid) { + ConsoleLogger.debug("Successfully checked TOTP code for `{0}`", player.getName()); asynchronousLogin.performLogin(player, auth); } else { + ConsoleLogger.debug("Input TOTP code was invalid for player `{0}`", player.getName()); messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE); } } diff --git a/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java b/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java index 534c0c019..53d74dfba 100644 --- a/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java +++ b/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java @@ -165,6 +165,10 @@ public class PlayerAuth { return totpKey; } + public void setTotpKey(String totpKey) { + this.totpKey = totpKey; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof PlayerAuth)) { diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java index 8791226a5..ff8608b37 100644 --- a/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.command.executable.totp; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.security.totp.GenerateTotpService; @@ -32,21 +32,21 @@ public class AddTotpCommandTest { @Mock private GenerateTotpService generateTotpService; @Mock - private DataSource dataSource; + private PlayerCache playerCache; @Mock private Messages messages; @Test - public void shouldHandleNonExistentUser() { + public void shouldHandleNonLoggedInUser() { // given Player player = mockPlayerWithName("bob"); - given(dataSource.getAuth("bob")).willReturn(null); + given(playerCache.getAuth("bob")).willReturn(null); // when addTotpCommand.runCommand(player, Collections.emptyList()); // then - verify(messages).send(player, MessageKey.REGISTER_MESSAGE); + verify(messages).send(player, MessageKey.NOT_LOGGED_IN); verifyZeroInteractions(generateTotpService); } @@ -56,7 +56,7 @@ public class AddTotpCommandTest { Player player = mockPlayerWithName("arend"); PlayerAuth auth = PlayerAuth.builder().name("arend") .totpKey("TOTP2345").build(); - given(dataSource.getAuth("arend")).willReturn(auth); + given(playerCache.getAuth("arend")).willReturn(auth); // when addTotpCommand.runCommand(player, Collections.emptyList()); @@ -71,7 +71,7 @@ public class AddTotpCommandTest { // given Player player = mockPlayerWithName("charles"); PlayerAuth auth = PlayerAuth.builder().name("charles").build(); - given(dataSource.getAuth("charles")).willReturn(auth); + given(playerCache.getAuth("charles")).willReturn(auth); TotpGenerationResult generationResult = new TotpGenerationResult( "777Key214", "http://example.org/qr-code/link"); diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java index 0d921d37a..17a011ee8 100644 --- a/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java @@ -1,12 +1,15 @@ package fr.xephi.authme.command.executable.totp; +import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.security.totp.GenerateTotpService; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import org.bukkit.entity.Player; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -15,7 +18,10 @@ import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -37,8 +43,15 @@ public class ConfirmTotpCommandTest { @Mock private DataSource dataSource; @Mock + private PlayerCache playerCache; + @Mock private Messages messages; + @BeforeClass + public static void setUpLogger() { + TestHelper.setupLogger(); + } + @Test public void shouldAddTotpCodeToUserAfterSuccessfulConfirmation() { // given @@ -46,10 +59,12 @@ public class ConfirmTotpCommandTest { String playerName = "George"; given(player.getName()).willReturn(playerName); PlayerAuth auth = PlayerAuth.builder().name(playerName).build(); - given(dataSource.getAuth(playerName)).willReturn(auth); - given(generateTotpService.getGeneratedTotpKey(player)).willReturn(new TotpGenerationResult("totp-key", "url-not-relevant")); + given(playerCache.getAuth(playerName)).willReturn(auth); + String generatedTotpKey = "totp-key"; + given(generateTotpService.getGeneratedTotpKey(player)).willReturn(new TotpGenerationResult(generatedTotpKey, "url-not-relevant")); String totpCode = "954321"; given(generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, totpCode)).willReturn(true); + given(dataSource.setTotpKey(anyString(), anyString())).willReturn(true); // when command.runCommand(player, Collections.singletonList(totpCode)); @@ -57,8 +72,10 @@ public class ConfirmTotpCommandTest { // then verify(generateTotpService).isTotpCodeCorrectForGeneratedTotpKey(player, totpCode); verify(generateTotpService).removeGenerateTotpKey(player); - verify(dataSource).setTotpKey(playerName, "totp-key"); + verify(dataSource).setTotpKey(playerName, generatedTotpKey); + verify(playerCache).updatePlayer(auth); verify(messages).send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS); + assertThat(auth.getTotpKey(), equalTo(generatedTotpKey)); } @Test @@ -68,7 +85,7 @@ public class ConfirmTotpCommandTest { String playerName = "George"; given(player.getName()).willReturn(playerName); PlayerAuth auth = PlayerAuth.builder().name(playerName).build(); - given(dataSource.getAuth(playerName)).willReturn(auth); + given(playerCache.getAuth(playerName)).willReturn(auth); given(generateTotpService.getGeneratedTotpKey(player)).willReturn(new TotpGenerationResult("totp-key", "url-not-relevant")); String totpCode = "754321"; given(generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, totpCode)).willReturn(false); @@ -79,8 +96,9 @@ public class ConfirmTotpCommandTest { // then verify(generateTotpService).isTotpCodeCorrectForGeneratedTotpKey(player, totpCode); verify(generateTotpService, never()).removeGenerateTotpKey(any(Player.class)); - verify(dataSource, only()).getAuth(playerName); + verify(playerCache, only()).getAuth(playerName); verify(messages).send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE); + verifyZeroInteractions(dataSource); } @Test @@ -90,7 +108,7 @@ public class ConfirmTotpCommandTest { String playerName = "George"; given(player.getName()).willReturn(playerName); PlayerAuth auth = PlayerAuth.builder().name(playerName).build(); - given(dataSource.getAuth(playerName)).willReturn(auth); + given(playerCache.getAuth(playerName)).willReturn(auth); given(generateTotpService.getGeneratedTotpKey(player)).willReturn(null); // when @@ -98,8 +116,9 @@ public class ConfirmTotpCommandTest { // then verify(generateTotpService, only()).getGeneratedTotpKey(player); - verify(dataSource, only()).getAuth(playerName); + verify(playerCache, only()).getAuth(playerName); verify(messages).send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE); + verifyZeroInteractions(dataSource); } @Test @@ -109,14 +128,14 @@ public class ConfirmTotpCommandTest { String playerName = "George"; given(player.getName()).willReturn(playerName); PlayerAuth auth = PlayerAuth.builder().name(playerName).totpKey("A987234").build(); - given(dataSource.getAuth(playerName)).willReturn(auth); + given(playerCache.getAuth(playerName)).willReturn(auth); // when command.runCommand(player, Collections.singletonList("871634")); // then - verify(dataSource, only()).getAuth(playerName); - verifyZeroInteractions(generateTotpService); + verify(playerCache, only()).getAuth(playerName); + verifyZeroInteractions(generateTotpService, dataSource); verify(messages).send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED); } @@ -126,14 +145,14 @@ public class ConfirmTotpCommandTest { Player player = mock(Player.class); String playerName = "George"; given(player.getName()).willReturn(playerName); - given(dataSource.getAuth(playerName)).willReturn(null); + given(playerCache.getAuth(playerName)).willReturn(null); // when command.runCommand(player, Collections.singletonList("984685")); // then - verify(dataSource, only()).getAuth(playerName); - verifyZeroInteractions(generateTotpService); - verify(messages).send(player, MessageKey.REGISTER_MESSAGE); + verify(playerCache, only()).getAuth(playerName); + verifyZeroInteractions(generateTotpService, dataSource); + verify(messages).send(player, MessageKey.NOT_LOGGED_IN); } } diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java new file mode 100644 index 000000000..18903972d --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java @@ -0,0 +1,149 @@ +package fr.xephi.authme.command.executable.totp; + +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.data.auth.PlayerCache; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.security.totp.TotpAuthenticator; +import org.bukkit.entity.Player; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static java.util.Collections.singletonList; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link RemoveTotpCommand}. + */ +@RunWith(MockitoJUnitRunner.class) +public class RemoveTotpCommandTest { + + @InjectMocks + private RemoveTotpCommand command; + + @Mock + private DataSource dataSource; + @Mock + private PlayerCache playerCache; + @Mock + private TotpAuthenticator totpAuthenticator; + @Mock + private Messages messages; + + @BeforeClass + public static void setUpLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldRemoveTotpKey() { + // given + String name = "aws"; + PlayerAuth auth = PlayerAuth.builder().name(name).totpKey("some-totp-key").build(); + given(playerCache.getAuth(name)).willReturn(auth); + String inputCode = "93847"; + given(totpAuthenticator.checkCode(auth, inputCode)).willReturn(true); + given(dataSource.removeTotpKey(name)).willReturn(true); + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + + // when + command.runCommand(player, singletonList(inputCode)); + + // then + verify(dataSource).removeTotpKey(name); + verify(messages, only()).send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS); + verify(playerCache).updatePlayer(auth); + assertThat(auth.getTotpKey(), nullValue()); + } + + @Test + public void shouldHandleDatabaseError() { + // given + String name = "aws"; + PlayerAuth auth = PlayerAuth.builder().name(name).totpKey("some-totp-key").build(); + given(playerCache.getAuth(name)).willReturn(auth); + String inputCode = "93847"; + given(totpAuthenticator.checkCode(auth, inputCode)).willReturn(true); + given(dataSource.removeTotpKey(name)).willReturn(false); + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + + // when + command.runCommand(player, singletonList(inputCode)); + + // then + verify(dataSource).removeTotpKey(name); + verify(messages, only()).send(player, MessageKey.ERROR); + verify(playerCache, only()).getAuth(name); + } + + @Test + public void shouldHandleInvalidCode() { + // given + String name = "cesar"; + PlayerAuth auth = PlayerAuth.builder().name(name).totpKey("some-totp-key").build(); + given(playerCache.getAuth(name)).willReturn(auth); + String inputCode = "93847"; + given(totpAuthenticator.checkCode(auth, inputCode)).willReturn(false); + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + + // when + command.runCommand(player, singletonList(inputCode)); + + // then + verifyZeroInteractions(dataSource); + verify(messages, only()).send(player, MessageKey.TWO_FACTOR_INVALID_CODE); + verify(playerCache, only()).getAuth(name); + } + + @Test + public void shouldHandleUserWithoutTotpKey() { + // given + String name = "cesar"; + PlayerAuth auth = PlayerAuth.builder().name(name).build(); + given(playerCache.getAuth(name)).willReturn(auth); + String inputCode = "654684"; + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + + // when + command.runCommand(player, singletonList(inputCode)); + + // then + verifyZeroInteractions(dataSource, totpAuthenticator); + verify(messages, only()).send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR); + verify(playerCache, only()).getAuth(name); + } + + @Test + public void shouldHandleNonLoggedInUser() { + // given + String name = "cesar"; + given(playerCache.getAuth(name)).willReturn(null); + String inputCode = "654684"; + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + + // when + command.runCommand(player, singletonList(inputCode)); + + // then + verifyZeroInteractions(dataSource, totpAuthenticator); + verify(messages, only()).send(player, MessageKey.NOT_LOGGED_IN); + verify(playerCache, only()).getAuth(name); + } +} From c96e28f726ef36453849c42524e0323acec6716b Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 13 May 2018 22:52:41 +0200 Subject: [PATCH 136/155] Add debug logging for teleports (relates to #1521) --- .../fr/xephi/authme/service/TeleportationService.java | 11 +++++++++-- .../java/fr/xephi/authme/settings/SpawnLoader.java | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/TeleportationService.java b/src/main/java/fr/xephi/authme/service/TeleportationService.java index 10f9e1178..1588c4404 100644 --- a/src/main/java/fr/xephi/authme/service/TeleportationService.java +++ b/src/main/java/fr/xephi/authme/service/TeleportationService.java @@ -1,5 +1,6 @@ package fr.xephi.authme.service; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.limbo.LimboPlayer; @@ -63,12 +64,13 @@ public class TeleportationService implements Reloadable { public void teleportOnJoin(final Player player) { if (!settings.getProperty(RestrictionSettings.NO_TELEPORT) && settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN)) { + ConsoleLogger.debug("Teleport on join for player `{0}`", player.getName()); teleportToSpawn(player, playerCache.isAuthenticated(player.getName())); } } /** - * Returns the player's custom on join location + * Returns the player's custom on join location. * * @param player the player to process * @@ -82,10 +84,11 @@ public class TeleportationService implements Reloadable { SpawnTeleportEvent event = new SpawnTeleportEvent(player, location, playerCache.isAuthenticated(player.getName())); bukkitService.callEvent(event); - if(!isEventValid(event)) { + if (!isEventValid(event)) { return null; } + ConsoleLogger.debug("Returning custom location for >1.9 join event for player `{0}`", player.getName()); return location; } return null; @@ -107,6 +110,7 @@ public class TeleportationService implements Reloadable { } if (!player.hasPlayedBefore() || !dataSource.isAuthAvailable(player.getName())) { + ConsoleLogger.debug("Attempting to teleport player `{0}` to first spawn", player.getName()); performTeleportation(player, new FirstSpawnTeleportEvent(player, firstSpawn)); } } @@ -130,12 +134,15 @@ public class TeleportationService implements Reloadable { // The world in LimboPlayer is from where the player comes, before any teleportation by AuthMe if (mustForceSpawnAfterLogin(worldName)) { + ConsoleLogger.debug("Teleporting `{0}` to spawn because of 'force-spawn after login'", player.getName()); teleportToSpawn(player, true); } else if (settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN)) { if (settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION) && auth.getQuitLocY() != 0) { Location location = buildLocationFromAuth(player, auth); + ConsoleLogger.debug("Teleporting `{0}` after login, based on the player auth", player.getName()); teleportBackFromSpawn(player, location); } else if (limbo != null && limbo.getLocation() != null) { + ConsoleLogger.debug("Teleporting `{0}` after login, based on the limbo player", player.getName()); teleportBackFromSpawn(player, limbo.getLocation()); } } diff --git a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java index ea235b3cc..d2f2edbf7 100644 --- a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java +++ b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java @@ -198,9 +198,11 @@ public class SpawnLoader implements Reloadable { // ignore } if (spawnLoc != null) { + ConsoleLogger.debug("Spawn location determined as `{0}` for world `{1}`", spawnLoc, world.getName()); return spawnLoc; } } + ConsoleLogger.debug("Fall back to default world spawn location. World: `{0}`", world.getName()); return world.getSpawnLocation(); // return default location } From 54feb1097e170de2164d95413832680c7dcaa4bc Mon Sep 17 00:00:00 2001 From: rafael59r2 Date: Sun, 13 May 2018 21:56:25 +0100 Subject: [PATCH 137/155] Update messages_pt.yml (#1570) --- src/main/resources/messages/messages_pt.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index d6e265053..5d5b0ce9b 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -145,12 +145,12 @@ time: # Two-factor authentication two_factor: code_created: '&2O seu código secreto é o %code. Você pode verificá-lo a partir daqui %url' - # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' - # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' - # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' - # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' - # TODO enable_success: 'Successfully enabled two-factor authentication for your account' - # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' - # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' - # TODO removed_success: 'Successfully removed two-factor auth from your account' - # TODO invalid_code: 'Invalid code!' + confirmation_required: 'Por favor confirme seu codigo de 2 etapas com /2fa confirm ' + code_required: 'Por favor submita seu codigo de duas etapas com /2fa code ' + already_enabled: 'Autenticação de duas etapas já se encontra habilitada na sua conta!' + enable_error_no_code: 'Nenhuma chave 2fa foi gerada por você ou expirou. Por favor digite /2fa add' + enable_success: 'Autenticação de duas etapas habilitada com sucesso na sua conta' + enable_error_wrong_code: 'Codigo errado ou o codigo expirou. Por favor digite /2fa add' + not_enabled_error: 'Autenticação de duas etapas não está habilitada na sua conta. Digite /2fa add' + removed_success: 'Autenticação de duas etapas removida com sucesso da sua conta' + invalid_code: 'Codigo invalido!' From 3c0caf2ac3f57bd8f0e7643d5884da6d7ecbb82e Mon Sep 17 00:00:00 2001 From: Maxetto Date: Mon, 14 May 2018 00:53:46 +0200 Subject: [PATCH 138/155] [Messages_IT] Add 2FA messages --- src/main/resources/messages/messages_it.yml | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index d46a244fd..9e9721b96 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -7,7 +7,7 @@ # Registration registration: disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.' - name_taken: '&cHai già eseguito la registrazione, non puoi eseguirla nuovamente.' + name_taken: '&cHai già eseguito la registrazione!' register_request: '&3Per favore, esegui la registrazione con il comando: /register ' command_usage: '&cUtilizzo: /register ' reg_only: '&4Puoi giocare in questo server solo dopo aver eseguito la registrazione attraverso il sito web! Per favore, vai su http://esempio.it per procedere!' @@ -39,7 +39,7 @@ error: no_permission: '&4Non hai il permesso di eseguire questa operazione.' unexpected_error: '&4Qualcosa è andato storto, riporta questo errore ad un amministratore!' max_registration: '&cHai raggiunto il numero massimo di registrazioni (%reg_count/%max_acc %reg_names) per questo indirizzo IP!' - logged_in: '&cHai già eseguito l''autenticazione, non è necessario eseguirla nuovamente!' + logged_in: '&cHai già eseguito l''autenticazione!' kick_for_vip: '&3Un utente VIP è entrato mentre il server era pieno e ha preso il tuo posto!' tempban_max_logins: '&cSei stato temporaneamente bandito per aver fallito l''autenticazione troppe volte.' @@ -146,12 +146,12 @@ time: # Two-factor authentication two_factor: code_created: '&2Il tuo codice segreto è: &f%code%%nl%&2Puoi anche scannerizzare il codice QR da qui: &f%url' - # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' - # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' - # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' - # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' - # TODO enable_success: 'Successfully enabled two-factor authentication for your account' - # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' - # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' - # TODO removed_success: 'Successfully removed two-factor auth from your account' - # TODO invalid_code: 'Invalid code!' + confirmation_required: 'Per favore conferma il tuo codice con: /2fa confirm ' + code_required: 'Per favore inserisci il tuo codice per l''autenticazione a 2 fattori con: /2fa code ' + already_enabled: 'Hai già abilitato l''autenticazione a 2 fattori!' + enable_error_no_code: 'Non hai ancora generato un codice per l''autenticazione a 2 fattori oppure il tuo codice è scaduto. Per favore scrivi: /2fa add' + enable_success: 'Autenticazione a 2 fattori abilitata correttamente' + enable_error_wrong_code: 'Hai inserito un codice sbagliato o scaduto. Per favore scrivi: /2fa add' + not_enabled_error: 'L''autenticazione a 2 fattori non è ancora abilitata per il tuo account. Scrivi: /2fa add' + removed_success: 'Autenticazione a 2 fattori rimossa correttamente' + invalid_code: 'Il codice inserito non è valido, riprova!' From 80dce1a92f620ac4586200bd17f83505a6bed2c8 Mon Sep 17 00:00:00 2001 From: RikoDEV Date: Mon, 14 May 2018 21:08:20 +0200 Subject: [PATCH 139/155] Update messages_pl.yml (#1571) * Update messages_pl.yml --- src/main/resources/messages/messages_pl.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index b923533bd..78f63a831 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -145,12 +145,12 @@ time: # Two-factor authentication two_factor: code_created: '&2Twój sekretny kod to %code. Możesz zeskanować go tutaj: %url' - # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' - # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' - # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' - # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' - # TODO enable_success: 'Successfully enabled two-factor authentication for your account' - # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' - # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' - # TODO removed_success: 'Successfully removed two-factor auth from your account' - # TODO invalid_code: 'Invalid code!' + confirmation_required: 'Musisz potwierdzić swój kod komendą /2fa confirm ' + code_required: 'Wpisz swój kod weryfikacji dwuetapowej przy pomocy komendy /2fa code ' + already_enabled: '&aWeryfikacja dwuetapowa jest już włączona dla Twojego konta.' + enable_error_no_code: '&cKod weryfikacji dwuetapowej nie został dla Ciebie wygenerowany lub wygasł. Wpisz komende /2fa add' + enable_success: '&aWeryfikacja dwuetapowa została włączona dla Twojego konta.' + enable_error_wrong_code: '&cWpisany kod jest nieprawidłowy lub wygasły. Wpisz ponownie /2fa add' + not_enabled_error: 'Weryfikacja dwuetapowa nie jest włączona dla twojego konta. Wpisz komende /2fa add' + removed_success: '&aPomyślnie usunięto weryfikacje dwuetapową z Twojego konta.' + invalid_code: '&cWpisany kod jest nieprawidłowy, spróbuj jeszcze raz.' From 8e4288f911629e9ae7abeefd65b06fdf1700ac52 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 20 May 2018 13:10:26 +0200 Subject: [PATCH 140/155] Minor code householding --- .../executable/authme/PurgeCommand.java | 7 +++---- .../authme/data/limbo/LimboServiceHelper.java | 4 ---- .../xephi/authme/security/crypts/Pbkdf2.java | 10 +++++----- .../authme/security/crypts/Pbkdf2Django.java | 10 +++++----- .../security/totp/TotpAuthenticator.java | 10 +++------- .../authme/listener/OnJoinVerifierTest.java | 13 +++---------- .../process/login/AsynchronousLoginTest.java | 9 ++++----- .../authme/service/AntiBotServiceTest.java | 4 ++-- .../service/BukkitServiceTestHelper.java | 18 ++++++++++++++++++ .../WelcomeMessageConfigurationTest.java | 9 +++++---- 10 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java index 58b235ed0..1538061e9 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java @@ -1,5 +1,6 @@ package fr.xephi.authme.command.executable.authme; +import com.google.common.primitives.Ints; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.task.purge.PurgeService; import org.bukkit.ChatColor; @@ -26,10 +27,8 @@ public class PurgeCommand implements ExecutableCommand { String daysStr = arguments.get(0); // Convert the days string to an integer value, and make sure it's valid - int days; - try { - days = Integer.parseInt(daysStr); - } catch (NumberFormatException ex) { + Integer days = Ints.tryParse(daysStr); + if (days == null) { sender.sendMessage(ChatColor.RED + "The value you've entered is invalid!"); return; } diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java b/src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java index b13a260d9..4d63a2d98 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java @@ -3,7 +3,6 @@ package fr.xephi.authme.data.limbo; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.LimboSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.Location; @@ -20,9 +19,6 @@ import static fr.xephi.authme.util.Utils.isCollectionEmpty; */ class LimboServiceHelper { - @Inject - private SpawnLoader spawnLoader; - @Inject private PermissionsManager permissionsManager; diff --git a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java index 5367a2a12..d9695abc5 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security.crypts; +import com.google.common.primitives.Ints; import de.rtner.misc.BinTools; import de.rtner.security.auth.spi.PBKDF2Engine; import de.rtner.security.auth.spi.PBKDF2Parameters; @@ -38,13 +39,12 @@ public class Pbkdf2 extends HexSaltedMethod { if (line.length != 4) { return false; } - int iterations; - try { - iterations = Integer.parseInt(line[1]); - } catch (NumberFormatException e) { - ConsoleLogger.logException("Cannot read number of rounds for Pbkdf2", e); + Integer iterations = Ints.tryParse(line[1]); + if (iterations == null) { + ConsoleLogger.warning("Cannot read number of rounds for Pbkdf2: '" + line[1] + "'"); return false; } + String salt = line[2]; byte[] derivedKey = BinTools.hex2bin(line[3]); PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "UTF-8", salt.getBytes(), iterations, derivedKey); diff --git a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java index f5a0abb63..e32930db1 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security.crypts; +import com.google.common.primitives.Ints; import de.rtner.security.auth.spi.PBKDF2Engine; import de.rtner.security.auth.spi.PBKDF2Parameters; import fr.xephi.authme.ConsoleLogger; @@ -27,13 +28,12 @@ public class Pbkdf2Django extends HexSaltedMethod { if (line.length != 4) { return false; } - int iterations; - try { - iterations = Integer.parseInt(line[1]); - } catch (NumberFormatException e) { - ConsoleLogger.logException("Could not read number of rounds for Pbkdf2Django:", e); + Integer iterations = Ints.tryParse(line[1]); + if (iterations == null) { + ConsoleLogger.warning("Cannot read number of rounds for Pbkdf2Django: '" + line[1] + "'"); return false; } + String salt = line[2]; byte[] derivedKey = Base64.getDecoder().decode(line[3]); PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), iterations, derivedKey); diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java index eb922cf43..4905a521e 100644 --- a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java +++ b/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security.totp; +import com.google.common.primitives.Ints; import com.warrenstrange.googleauth.GoogleAuthenticator; import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; @@ -43,13 +44,8 @@ public class TotpAuthenticator { * @return true if code is valid, false otherwise */ public boolean checkCode(String totpKey, String inputCode) { - try { - Integer totpCode = Integer.valueOf(inputCode); - return authenticator.authorize(totpKey, totpCode); - } catch (NumberFormatException e) { - // ignore - } - return false; + Integer totpCode = Ints.tryParse(inputCode); + return totpCode != null && authenticator.authorize(totpKey, totpCode); } public TotpGenerationResult generateTotpKey(Player player) { diff --git a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java index 2f6121a90..114d42101 100644 --- a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java +++ b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java @@ -30,10 +30,10 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; +import static fr.xephi.authme.service.BukkitServiceTestHelper.returnGivenOnlinePlayers; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @@ -123,7 +123,7 @@ public class OnJoinVerifierTest { List onlinePlayers = Arrays.asList(mock(Player.class), mock(Player.class)); given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true); given(permissionsManager.hasPermission(onlinePlayers.get(1), PlayerStatePermission.IS_VIP)).willReturn(false); - returnOnlineListFromBukkitServer(onlinePlayers); + returnGivenOnlinePlayers(bukkitService, onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); given(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP)).willReturn("kick for vip"); @@ -147,7 +147,7 @@ public class OnJoinVerifierTest { given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(true); List onlinePlayers = Collections.singletonList(mock(Player.class)); given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true); - returnOnlineListFromBukkitServer(onlinePlayers); + returnGivenOnlinePlayers(bukkitService, onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); given(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)).willReturn("kick full server"); @@ -501,13 +501,6 @@ public class OnJoinVerifierTest { onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, false); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void returnOnlineListFromBukkitServer(Collection onlineList) { - // Note ljacqu 20160529: The compiler gets lost in generics because Collection is returned - // from getOnlinePlayers(). We need to uncheck onlineList to a simple Collection or it will refuse to compile. - given(bukkitService.getOnlinePlayers()).willReturn((Collection) onlineList); - } - private void expectValidationExceptionWith(MessageKey messageKey, String... args) { expectedException.expect(exceptionWithData(messageKey, args)); } diff --git a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java index 9b57fb318..f19810172 100644 --- a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java +++ b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java @@ -21,13 +21,13 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Arrays; -import java.util.Collection; +import java.util.List; +import static fr.xephi.authme.service.BukkitServiceTestHelper.returnGivenOnlinePlayers; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -241,7 +241,6 @@ public class AsynchronousLoginTest { return player; } - @SuppressWarnings({ "unchecked", "rawtypes" }) private void mockOnlinePlayersInBukkitService() { // 127.0.0.4: albania (online), brazil (offline) Player playerA = mockPlayer("albania"); @@ -266,8 +265,8 @@ public class AsynchronousLoginTest { Player playerF = mockPlayer("france"); TestHelper.mockPlayerIp(playerF, "192.168.0.0"); - Collection onlinePlayers = Arrays.asList(playerA, playerB, playerC, playerD, playerE, playerF); - given(bukkitService.getOnlinePlayers()).willReturn(onlinePlayers); + List onlinePlayers = Arrays.asList(playerA, playerB, playerC, playerD, playerE, playerF); + returnGivenOnlinePlayers(bukkitService, onlinePlayers); } } diff --git a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java index 62dda8e50..f992e2554 100644 --- a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java @@ -19,6 +19,7 @@ import org.mockito.Mock; import java.util.Arrays; import java.util.List; +import static fr.xephi.authme.service.BukkitServiceTestHelper.returnGivenOnlinePlayers; import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncDelayedTaskWithDelay; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -154,11 +155,10 @@ public class AntiBotServiceTest { } @Test - @SuppressWarnings({"unchecked", "rawtypes"}) public void shouldInformPlayersOnActivation() { // given - listening antibot List players = Arrays.asList(mock(Player.class), mock(Player.class)); - given(bukkitService.getOnlinePlayers()).willReturn((List) players); + returnGivenOnlinePlayers(bukkitService, players); given(permissionsManager.hasPermission(players.get(0), AdminPermission.ANTIBOT_MESSAGES)).willReturn(false); given(permissionsManager.hasPermission(players.get(1), AdminPermission.ANTIBOT_MESSAGES)).willReturn(true); diff --git a/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java b/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java index 9807e4f5b..7d57869dd 100644 --- a/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java +++ b/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java @@ -1,7 +1,12 @@ package fr.xephi.authme.service; +import org.bukkit.entity.Player; + +import java.util.Collection; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doAnswer; /** @@ -81,4 +86,17 @@ public final class BukkitServiceTestHelper { return null; }).when(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), anyLong()); } + + /** + * Sets a BukkitService mock to return the given players when its method + * {@link BukkitService#getOnlinePlayers()} is invoked. + * + * @param bukkitService the mock to set behavior on + * @param players the players to return + */ + @SuppressWarnings("unchecked") + public static void returnGivenOnlinePlayers(BukkitService bukkitService, Collection players) { + // The compiler gets lost in generics because Collection is returned from getOnlinePlayers() + given(bukkitService.getOnlinePlayers()).willReturn((Collection) players); + } } diff --git a/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java b/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java index c650dd6f5..2109edbd9 100644 --- a/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java +++ b/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java @@ -24,6 +24,7 @@ import java.nio.file.Files; import java.util.Arrays; import java.util.List; +import static fr.xephi.authme.service.BukkitServiceTestHelper.returnGivenOnlinePlayers; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; @@ -68,7 +69,7 @@ public class WelcomeMessageConfigurationTest { } @Test - public void shouldLoadWelcomeMessage() throws IOException { + public void shouldLoadWelcomeMessage() { // given String welcomeMessage = "This is my welcome message for testing\nBye!"; setWelcomeMessageAndReload(welcomeMessage); @@ -84,7 +85,7 @@ public class WelcomeMessageConfigurationTest { } @Test - public void shouldReplaceNameAndIpAndCountry() throws IOException { + public void shouldReplaceNameAndIpAndCountry() { // given String welcomeMessage = "Hello {PLAYER}, your IP is {IP}\nYour country is {COUNTRY}.\nWelcome to {SERVER}!"; setWelcomeMessageAndReload(welcomeMessage); @@ -108,11 +109,11 @@ public class WelcomeMessageConfigurationTest { } @Test - public void shouldApplyOtherReplacements() throws IOException { + public void shouldApplyOtherReplacements() { // given String welcomeMessage = "{ONLINE}/{MAXPLAYERS} online\n{LOGINS} logged in\nYour world is {WORLD}\nServer: {VERSION}"; setWelcomeMessageAndReload(welcomeMessage); - given(bukkitService.getOnlinePlayers()).willReturn((List) Arrays.asList(mock(Player.class), mock(Player.class))); + returnGivenOnlinePlayers(bukkitService, Arrays.asList(mock(Player.class), mock(Player.class))); given(server.getMaxPlayers()).willReturn(20); given(playerCache.getLogged()).willReturn(1); given(server.getBukkitVersion()).willReturn("Bukkit-456.77.8"); From 61420429962cecbaec748888c5a5d236983901ee Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 21 May 2018 08:45:18 +0200 Subject: [PATCH 141/155] #1417 Add permission node to allow chat before login --- docs/permission_nodes.md | 5 ++-- .../xephi/authme/listener/PlayerListener.java | 25 ++++++++++++------ .../permission/PlayerStatePermission.java | 9 +++++-- src/main/resources/plugin.yml | 3 +++ .../authme/listener/PlayerListenerTest.java | 26 +++++++++++++++++++ 5 files changed, 56 insertions(+), 12 deletions(-) diff --git a/docs/permission_nodes.md b/docs/permission_nodes.md index 828d23b53..643a327b0 100644 --- a/docs/permission_nodes.md +++ b/docs/permission_nodes.md @@ -1,5 +1,5 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. @@ -30,6 +30,7 @@ The following are the permission nodes that are currently supported by the lates - **authme.admin.switchantibot** – Administrator command to toggle the AntiBot protection status. - **authme.admin.unregister** – Administrator command to unregister an existing user. - **authme.admin.updatemessages** – Permission to use the update messages command. +- **authme.allowchatbeforelogin** – Permission to send chat messages before being logged in. - **authme.allowmultipleaccounts** – Permission to be able to register multiple accounts. - **authme.bypassantibot** – Permission node to bypass AntiBot protection. - **authme.bypasscountrycheck** – Permission to bypass the GeoIp country code check. @@ -69,4 +70,4 @@ The following are the permission nodes that are currently supported by the lates --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:00:13 CEST 2018 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Mon May 21 08:43:08 CEST 2018 diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 5b6327127..660119ed8 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -7,6 +7,7 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.permission.handlers.PermissionLoadUserException; import fr.xephi.authme.process.Management; import fr.xephi.authme.service.AntiBotService; @@ -65,7 +66,7 @@ public class PlayerListener implements Listener { @Inject private Settings settings; @Inject - private Messages m; + private Messages messages; @Inject private DataSource dataSource; @Inject @@ -107,12 +108,12 @@ public class PlayerListener implements Listener { final Player player = event.getPlayer(); if (!quickCommandsProtectionManager.isAllowed(player.getName())) { event.setCancelled(true); - player.kickPlayer(m.retrieveSingle(player, MessageKey.QUICK_COMMAND_PROTECTION_KICK)); + player.kickPlayer(messages.retrieveSingle(player, MessageKey.QUICK_COMMAND_PROTECTION_KICK)); return; } if (listenerService.shouldCancelEvent(player)) { event.setCancelled(true); - m.send(player, MessageKey.DENIED_COMMAND); + messages.send(player, MessageKey.DENIED_COMMAND); } } @@ -123,10 +124,18 @@ public class PlayerListener implements Listener { } final Player player = event.getPlayer(); - if (listenerService.shouldCancelEvent(player)) { + final boolean mayPlayerSendChat = !listenerService.shouldCancelEvent(player) + || permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN); + if (mayPlayerSendChat) { + removeUnauthorizedRecipients(event); + } else { event.setCancelled(true); - m.send(player, MessageKey.DENIED_CHAT); - } else if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) { + messages.send(player, MessageKey.DENIED_CHAT); + } + } + + private void removeUnauthorizedRecipients(AsyncPlayerChatEvent event) { + if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) { event.getRecipients().removeIf(listenerService::shouldCancelEvent); if (event.getRecipients().isEmpty()) { event.setCancelled(true); @@ -274,7 +283,7 @@ public class PlayerListener implements Listener { try { runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress()); } catch (FailedVerificationException e) { - event.setKickMessage(m.retrieveSingle(name, e.getReason(), e.getArgs())); + event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs())); event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); } } @@ -302,7 +311,7 @@ public class PlayerListener implements Listener { try { runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress()); } catch (FailedVerificationException e) { - event.setKickMessage(m.retrieveSingle(player, e.getReason(), e.getArgs())); + event.setKickMessage(messages.retrieveSingle(player, e.getReason(), e.getArgs())); event.setResult(PlayerLoginEvent.Result.KICK_OTHER); } } diff --git a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java index 2a0517225..667b55d56 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java @@ -34,7 +34,12 @@ public enum PlayerStatePermission implements PermissionNode { /** * Permission to bypass the GeoIp country code check. */ - BYPASS_COUNTRY_CHECK("authme.bypasscountrycheck", DefaultPermission.NOT_ALLOWED); + BYPASS_COUNTRY_CHECK("authme.bypasscountrycheck", DefaultPermission.NOT_ALLOWED), + + /** + * Permission to send chat messages before being logged in. + */ + ALLOW_CHAT_BEFORE_LOGIN("authme.allowchatbeforelogin", DefaultPermission.NOT_ALLOWED); /** * The permission node. @@ -42,7 +47,7 @@ public enum PlayerStatePermission implements PermissionNode { private String node; /** - * The default permission level + * The default permission level. */ private DefaultPermission defaultPermission; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1be18288c..90e31c921 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -161,6 +161,9 @@ permissions: authme.admin.updatemessages: description: Permission to use the update messages command. default: op + authme.allowchatbeforelogin: + description: Permission to send chat messages before being logged in. + default: false authme.allowmultipleaccounts: description: Permission to be able to register multiple accounts. default: op diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index 42d14e463..59d837a9c 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -6,6 +6,8 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.Management; import fr.xephi.authme.service.AntiBotService; import fr.xephi.authme.service.BukkitService; @@ -60,6 +62,7 @@ import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceTo import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -113,6 +116,8 @@ public class PlayerListenerTest { private JoinMessageService joinMessageService; @Mock private QuickCommandsProtectionManager quickCommandsProtectionManager; + @Mock + private PermissionsManager permissionsManager; /** * #831: If a player is kicked because of "logged in from another location", the kick @@ -289,12 +294,14 @@ public class PlayerListenerTest { given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); AsyncPlayerChatEvent event = newAsyncChatEvent(); given(listenerService.shouldCancelEvent(event.getPlayer())).willReturn(true); + given(permissionsManager.hasPermission(event.getPlayer(), PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN)).willReturn(false); // when listener.onPlayerChat(event); // then verify(listenerService).shouldCancelEvent(event.getPlayer()); + verify(permissionsManager).hasPermission(event.getPlayer(), PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN); verify(event).setCancelled(true); verify(messages).send(event.getPlayer(), MessageKey.DENIED_CHAT); } @@ -356,6 +363,25 @@ public class PlayerListenerTest { assertThat(event.getRecipients(), empty()); } + @Test + public void shouldAllowChatForBypassPermission() { + // given + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + AsyncPlayerChatEvent event = newAsyncChatEvent(); + given(listenerService.shouldCancelEvent(event.getPlayer())).willReturn(true); + given(permissionsManager.hasPermission(event.getPlayer(), PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.HIDE_CHAT)).willReturn(false); + + // when + listener.onPlayerChat(event); + + // then + assertThat(event.isCancelled(), equalTo(false)); + verify(listenerService).shouldCancelEvent(event.getPlayer()); + verify(permissionsManager).hasPermission(event.getPlayer(), PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN); + assertThat(event.getRecipients(), hasSize(3)); + } + @Test public void shouldAllowUnlimitedMovement() { // given From c4b02d74b768e77fa9470a8cda501f7469b52f45 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 21 May 2018 09:01:00 +0200 Subject: [PATCH 142/155] Fix generic type in PlayerAuth matcher --- src/test/java/fr/xephi/authme/AuthMeMatchers.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/fr/xephi/authme/AuthMeMatchers.java b/src/test/java/fr/xephi/authme/AuthMeMatchers.java index 33768883d..653fb04c9 100644 --- a/src/test/java/fr/xephi/authme/AuthMeMatchers.java +++ b/src/test/java/fr/xephi/authme/AuthMeMatchers.java @@ -44,8 +44,8 @@ public final class AuthMeMatchers { }; } - public static Matcher hasAuthBasicData(String name, String realName, - String email, String lastIp) { + public static Matcher hasAuthBasicData(String name, String realName, + String email, String lastIp) { return new TypeSafeMatcher() { @Override public boolean matchesSafely(PlayerAuth item) { From 68b896cfc3e8c5df7284776baee52855750a0e59 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 21 May 2018 09:10:27 +0200 Subject: [PATCH 143/155] Move salt column configuration to the other column configs --- docs/config.md | 8 ++++---- .../authme/settings/SettingsMigrationService.java | 14 ++++++++++++++ .../settings/properties/DatabaseSettings.java | 2 +- .../settings/SettingsMigrationServiceTest.java | 2 ++ .../fr/xephi/authme/settings/config-old.yml | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/config.md b/docs/config.md index 51ee86a9c..9444bb83e 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,5 +1,5 @@ - + ## AuthMe Configuration The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, @@ -37,6 +37,8 @@ DataSource: mySQLRealName: 'realname' # Column for storing players passwords mySQLColumnPassword: 'password' + # Column for storing players passwords salts + mySQLColumnSalt: '' # Column for storing players emails mySQLColumnEmail: 'email' # Column for storing if a player is logged in or not @@ -71,8 +73,6 @@ DataSource: # You should set this at least 30 seconds less than mysql server wait_timeout maxLifetime: 1800 ExternalBoardOptions: - # Column for storing players passwords salts - mySQLColumnSalt: '' # Column for storing players groups mySQLColumnGroup: '' # -1 means disabled. If you want that only activated players @@ -562,4 +562,4 @@ To change settings on a running server, save your changes to config.yml and use --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:00:10 CEST 2018 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Mon May 21 09:08:25 CEST 2018 diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index c5ab3fd7a..d95f73ce5 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -10,6 +10,7 @@ import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.process.register.RegisterSecondaryArgument; import fr.xephi.authme.process.register.RegistrationType; import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.SecuritySettings; @@ -74,6 +75,7 @@ public class SettingsMigrationService extends PlainMigrationService { | convertToRegistrationType(resource) | mergeAndMovePermissionGroupSettings(resource) | moveDeprecatedHashAlgorithmIntoLegacySection(resource) + | moveSaltColumnConfigWithOtherColumnConfigs(resource) || hasDeprecatedProperties(resource); } @@ -313,6 +315,18 @@ public class SettingsMigrationService extends PlainMigrationService { return false; } + /** + * Moves the property for the password salt column name to the same path as all other column name properties. + * + * @param resource The property resource + * @return True if the configuration has changed, false otherwise + */ + private static boolean moveSaltColumnConfigWithOtherColumnConfigs(PropertyResource resource) { + Property oldProperty = newProperty("ExternalBoardOptions.mySQLColumnSalt", + DatabaseSettings.MYSQL_COL_SALT.getDefaultValue()); + return moveProperty(oldProperty, DatabaseSettings.MYSQL_COL_SALT, resource); + } + /** * Retrieves the old config to run a command when alt accounts are detected and sets them to this instance * for further processing. diff --git a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java index 8ebccf941..0818c2693 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java @@ -65,7 +65,7 @@ public final class DatabaseSettings implements SettingsHolder { @Comment("Column for storing players passwords salts") public static final Property MYSQL_COL_SALT = - newProperty("ExternalBoardOptions.mySQLColumnSalt", ""); + newProperty("DataSource.mySQLColumnSalt", ""); @Comment("Column for storing players emails") public static final Property MYSQL_COL_EMAIL = diff --git a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java index de53ec169..51cef1936 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java +++ b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; import static fr.xephi.authme.TestHelper.getJarFile; +import static fr.xephi.authme.settings.properties.DatabaseSettings.MYSQL_COL_SALT; import static fr.xephi.authme.settings.properties.PluginSettings.ENABLE_PERMISSION_CHECK; import static fr.xephi.authme.settings.properties.PluginSettings.LOG_LEVEL; import static fr.xephi.authme.settings.properties.PluginSettings.REGISTERED_GROUP; @@ -128,6 +129,7 @@ public class SettingsMigrationServiceTest { assertThat(settings.getProperty(UNREGISTERED_GROUP), equalTo("")); assertThat(settings.getProperty(PASSWORD_HASH), equalTo(HashAlgorithm.SHA256)); assertThat(settings.getProperty(LEGACY_HASHES), contains(HashAlgorithm.PBKDF2, HashAlgorithm.WORDPRESS, HashAlgorithm.SHA512)); + assertThat(settings.getProperty(MYSQL_COL_SALT), equalTo("salt_col_name")); // Check migration of old setting to email.html assertThat(Files.readLines(new File(dataFolder, "email.html"), StandardCharsets.UTF_8), diff --git a/src/test/resources/fr/xephi/authme/settings/config-old.yml b/src/test/resources/fr/xephi/authme/settings/config-old.yml index 8c1dc7f3e..2e34180cb 100644 --- a/src/test/resources/fr/xephi/authme/settings/config-old.yml +++ b/src/test/resources/fr/xephi/authme/settings/config-old.yml @@ -275,7 +275,7 @@ settings: applyBlindEffect: false ExternalBoardOptions: # MySQL column for the salt , needed for some forum/cms support - mySQLColumnSalt: '' + mySQLColumnSalt: 'salt_col_name' # MySQL column for the group, needed for some forum/cms support mySQLColumnGroup: '' # -1 mean disabled. If u want that only From 768ef9179aa278ebe60cc1aa23717a54c7948ad2 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 21 May 2018 13:07:13 +0200 Subject: [PATCH 144/155] Update datasource columns version - Fixes #1551 Bad closing of resources in case of an exception - Facilitates initialization of SQL handler implementation --- pom.xml | 2 +- .../columnshandler/AuthMeColumnsHandler.java | 13 +++--- .../columnshandler/ConnectionSupplier.java | 20 --------- .../MySqlPreparedStatementGenerator.java | 44 ------------------- 4 files changed, 8 insertions(+), 71 deletions(-) delete mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java delete mode 100644 src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java diff --git a/pom.xml b/pom.xml index c4cfe5f8d..535b41b23 100644 --- a/pom.xml +++ b/pom.xml @@ -800,7 +800,7 @@ ch.jalu datasourcecolumns - 0.1-SNAPSHOT + 0.1.1-SNAPSHOT true diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java index bb0d80b77..50575b4e5 100644 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java +++ b/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java @@ -5,9 +5,8 @@ import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.data.UpdateValues; import ch.jalu.datasourcecolumns.predicate.Predicate; import ch.jalu.datasourcecolumns.sqlimplementation.PredicateSqlGenerator; -import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementGenerator; -import ch.jalu.datasourcecolumns.sqlimplementation.ResultSetValueRetriever; import ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandler; +import ch.jalu.datasourcecolumns.sqlimplementation.statementgenerator.ConnectionSupplier; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -16,6 +15,8 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.List; +import static ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandlerConfig.forConnectionPool; +import static ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandlerConfig.forSingleConnection; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; /** @@ -43,8 +44,9 @@ public final class AuthMeColumnsHandler { String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); SqlColumnsHandler sqlColHandler = new SqlColumnsHandler<>( - PreparedStatementGenerator.fromConnection(connection), columnContext, tableName, nameColumn, - new ResultSetValueRetriever<>(columnContext), new PredicateSqlGenerator<>(columnContext, true)); + forSingleConnection(connection, tableName, nameColumn, columnContext) + .setPredicateSqlGenerator(new PredicateSqlGenerator<>(columnContext, true)) + ); return new AuthMeColumnsHandler(sqlColHandler); } @@ -61,8 +63,7 @@ public final class AuthMeColumnsHandler { String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); SqlColumnsHandler sqlColHandler = new SqlColumnsHandler<>( - new MySqlPreparedStatementGenerator(connectionSupplier), columnContext, tableName, nameColumn, - new ResultSetValueRetriever<>(columnContext), new PredicateSqlGenerator<>(columnContext)); + forConnectionPool(connectionSupplier, tableName, nameColumn, columnContext)); return new AuthMeColumnsHandler(sqlColHandler); } diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java deleted file mode 100644 index 77fbe8f3a..000000000 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/ConnectionSupplier.java +++ /dev/null @@ -1,20 +0,0 @@ -package fr.xephi.authme.datasource.columnshandler; - -import java.sql.Connection; -import java.sql.SQLException; - -/** - * Supplier of connections to a database. - */ -@FunctionalInterface -public interface ConnectionSupplier { - - /** - * Returns a connection to the database. - * - * @return the connection - * @throws SQLException . - */ - Connection get() throws SQLException; - -} diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java b/src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java deleted file mode 100644 index c20357ae4..000000000 --- a/src/main/java/fr/xephi/authme/datasource/columnshandler/MySqlPreparedStatementGenerator.java +++ /dev/null @@ -1,44 +0,0 @@ -package fr.xephi.authme.datasource.columnshandler; - -import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementGenerator; -import ch.jalu.datasourcecolumns.sqlimplementation.PreparedStatementResult; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -/** - * Implementation of {@link PreparedStatementGenerator} for MySQL which ensures that the connection - * taken from the connection pool is also closed after the prepared statement has been executed. - */ -class MySqlPreparedStatementGenerator implements PreparedStatementGenerator { - - private final ConnectionSupplier connectionSupplier; - - MySqlPreparedStatementGenerator(ConnectionSupplier connectionSupplier) { - this.connectionSupplier = connectionSupplier; - } - - @Override - public PreparedStatementResult create(String sql) throws SQLException { - Connection connection = connectionSupplier.get(); - return new MySqlPreparedStatementResult(connection, connection.prepareStatement(sql)); - } - - /** Prepared statement result which also closes the associated connection. */ - private static final class MySqlPreparedStatementResult extends PreparedStatementResult { - - private final Connection connection; - - MySqlPreparedStatementResult(Connection connection, PreparedStatement preparedStatement) { - super(preparedStatement); - this.connection = connection; - } - - @Override - public void close() throws SQLException { - super.close(); - connection.close(); - } - } -} From b9943675ba02d21de5a443e92b0377a30a1f1e4b Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 21 May 2018 13:29:34 +0200 Subject: [PATCH 145/155] #1557 Disallow player from using /email setpassword more than once --- .../authme/command/CommandInitializer.java | 4 +- ...mand.java => EmailSetPasswordCommand.java} | 5 +- .../service/PasswordRecoveryService.java | 17 ++++--- ....java => EmailSetPasswordCommandTest.java} | 11 +++-- .../service/PasswordRecoveryServiceTest.java | 49 +++++++++++++++++++ 5 files changed, 72 insertions(+), 14 deletions(-) rename src/main/java/fr/xephi/authme/command/executable/email/{SetPasswordCommand.java => EmailSetPasswordCommand.java} (89%) rename src/test/java/fr/xephi/authme/command/executable/email/{SetPasswordCommandTest.java => EmailSetPasswordCommandTest.java} (90%) diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index 6dbbcea59..ba48e0112 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -33,9 +33,9 @@ import fr.xephi.authme.command.executable.changepassword.ChangePasswordCommand; import fr.xephi.authme.command.executable.email.AddEmailCommand; import fr.xephi.authme.command.executable.email.ChangeEmailCommand; import fr.xephi.authme.command.executable.email.EmailBaseCommand; +import fr.xephi.authme.command.executable.email.EmailSetPasswordCommand; import fr.xephi.authme.command.executable.email.ProcessCodeCommand; import fr.xephi.authme.command.executable.email.RecoverEmailCommand; -import fr.xephi.authme.command.executable.email.SetPasswordCommand; import fr.xephi.authme.command.executable.email.ShowEmailCommand; import fr.xephi.authme.command.executable.login.LoginCommand; import fr.xephi.authme.command.executable.logout.LogoutCommand; @@ -540,7 +540,7 @@ public class CommandInitializer { .detailedDescription("Set a new password after successfully recovering your account.") .withArgument("password", "New password", MANDATORY) .permission(PlayerPermission.RECOVER_EMAIL) - .executableCommand(SetPasswordCommand.class) + .executableCommand(EmailSetPasswordCommand.class) .register(); return emailBase; diff --git a/src/main/java/fr/xephi/authme/command/executable/email/SetPasswordCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommand.java similarity index 89% rename from src/main/java/fr/xephi/authme/command/executable/email/SetPasswordCommand.java rename to src/main/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommand.java index d5d084aa6..376e8db29 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/SetPasswordCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommand.java @@ -18,7 +18,7 @@ import java.util.List; /** * Command for changing password following successful recovery. */ -public class SetPasswordCommand extends PlayerCommand { +public class EmailSetPasswordCommand extends PlayerCommand { @Inject private DataSource dataSource; @@ -45,11 +45,14 @@ public class SetPasswordCommand extends PlayerCommand { if (!result.hasError()) { HashedPassword hashedPassword = passwordSecurity.computeHash(password, name); dataSource.updatePassword(name, hashedPassword); + recoveryService.removeFromSuccessfulRecovery(player); ConsoleLogger.info("Player '" + name + "' has changed their password from recovery"); commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS); } else { commonService.send(player, result.getMessageKey(), result.getArgs()); } + } else { + commonService.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED); } } } diff --git a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java index f24b45f35..200aa11c7 100644 --- a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java +++ b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java @@ -121,6 +121,16 @@ public class PasswordRecoveryService implements Reloadable, HasCleanup { commonService.send(player, MessageKey.RECOVERY_CHANGE_PASSWORD); } + /** + * Removes a player from the list of successful recovers so that he can + * no longer use the /email setpassword command. + * + * @param player The player to remove. + */ + public void removeFromSuccessfulRecovery(Player player) { + successfulRecovers.remove(player.getName()); + } + /** * Check if a player is able to have emails sent. * @@ -149,12 +159,7 @@ public class PasswordRecoveryService implements Reloadable, HasCleanup { String playerAddress = PlayerUtils.getPlayerIp(player); String storedAddress = successfulRecovers.get(name); - if (storedAddress == null || !playerAddress.equals(storedAddress)) { - messages.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED); - return false; - } - - return true; + return storedAddress != null && playerAddress.equals(storedAddress); } @Override diff --git a/src/test/java/fr/xephi/authme/command/executable/email/SetPasswordCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/executable/email/SetPasswordCommandTest.java rename to src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java index 33acaf3f6..bf7fba782 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/SetPasswordCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java @@ -24,13 +24,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; /** - * Tests for {@link SetPasswordCommand}. + * Tests for {@link EmailSetPasswordCommand}. */ @RunWith(MockitoJUnitRunner.class) -public class SetPasswordCommandTest { +public class EmailSetPasswordCommandTest { @InjectMocks - private SetPasswordCommand command; + private EmailSetPasswordCommand command; @Mock private DataSource dataSource; @@ -70,6 +70,7 @@ public class SetPasswordCommandTest { // then verify(validationService).validatePassword("abc123", name); verify(dataSource).updatePassword(name, hashedPassword); + verify(recoveryService).removeFromSuccessfulRecovery(player); verify(commonService).send(player, MessageKey.PASSWORD_CHANGED_SUCCESS); } @@ -101,7 +102,7 @@ public class SetPasswordCommandTest { command.runCommand(player, Collections.singletonList("abc123")); // then - verifyZeroInteractions(validationService); - verifyZeroInteractions(dataSource); + verifyZeroInteractions(validationService, dataSource); + verify(commonService).send(player, MessageKey.CHANGE_PASSWORD_EXPIRED); } } diff --git a/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java b/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java index 48973bbfa..f0fc9b87c 100644 --- a/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java @@ -3,6 +3,7 @@ package fr.xephi.authme.service; import ch.jalu.injector.testing.BeforeInjecting; import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; +import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.message.MessageKey; @@ -14,6 +15,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -70,4 +73,50 @@ public class PasswordRecoveryServiceTest { verify(emailService).sendRecoveryCode(name, email, code); verify(commonService).send(player, MessageKey.RECOVERY_CODE_SENT); } + + @Test + public void shouldKeepTrackOfSuccessfulRecoversByIp() { + // given + Player bobby = mock(Player.class); + TestHelper.mockPlayerIp(bobby, "192.168.8.8"); + given(bobby.getName()).willReturn("bobby"); + + Player bobby2 = mock(Player.class); + TestHelper.mockPlayerIp(bobby2, "127.0.0.1"); + given(bobby2.getName()).willReturn("bobby"); + + Player other = mock(Player.class); + TestHelper.mockPlayerIp(other, "192.168.8.8"); + given(other.getName()).willReturn("other"); + + // when + recoveryService.addSuccessfulRecovery(bobby); + + // then + assertThat(recoveryService.canChangePassword(bobby), equalTo(true)); + assertThat(recoveryService.canChangePassword(bobby2), equalTo(false)); + assertThat(recoveryService.canChangePassword(other), equalTo(false)); + } + + @Test + public void shouldRemovePlayerFromSuccessfulRecovers() { + // given + Player bobby = mock(Player.class); + TestHelper.mockPlayerIp(bobby, "192.168.8.8"); + given(bobby.getName()).willReturn("bobby"); + recoveryService.addSuccessfulRecovery(bobby); + + Player other = mock(Player.class); + TestHelper.mockPlayerIp(other, "8.8.8.8"); + given(other.getName()).willReturn("other"); + recoveryService.addSuccessfulRecovery(other); + + // when + recoveryService.removeFromSuccessfulRecovery(other); + + + // then + assertThat(recoveryService.canChangePassword(bobby), equalTo(true)); + assertThat(recoveryService.canChangePassword(other), equalTo(false)); + } } From 14d3d1ad91fd1f8afb0afbf2eba6eb7eecf0d150 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 29 May 2018 00:51:25 +0200 Subject: [PATCH 146/155] Update dependencies --- pom.xml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 535b41b23..3f0fbfa11 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,10 @@ 1.12.2-R0.1-SNAPSHOT + + 3.3.9 + + @@ -388,7 +392,7 @@ com.google.code.gson gson - 2.8.2 + 2.8.5 true @@ -396,7 +400,7 @@ com.google.guava guava - 24.1-jre + 25.1-jre true @@ -474,7 +478,7 @@ com.warrenstrange googleauth - 1.1.2 + 1.1.5 true @@ -554,7 +558,7 @@ me.lucko.luckperms luckperms-api - 4.1 + 4.2 provided @@ -824,7 +828,7 @@ org.mockito mockito-core test - 2.18.0 + 2.18.3 hamcrest-core @@ -837,7 +841,7 @@ org.xerial sqlite-jdbc - 3.21.0.1 + 3.23.1 test From 5058747b1034b156a8a4a9582fbf4038b8953f6e Mon Sep 17 00:00:00 2001 From: games647 Date: Sat, 2 Jun 2018 21:50:02 +0200 Subject: [PATCH 147/155] Download database only if there is a newer one uploaded (Fixes #1581) --- .../fr/xephi/authme/service/GeoIpService.java | 178 +++++++++++------- 1 file changed, 109 insertions(+), 69 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index b973b6337..5f1fe86db 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -21,8 +21,9 @@ import fr.xephi.authme.util.InternetProtocolUtils; import java.io.BufferedInputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; -import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; @@ -33,6 +34,9 @@ import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; import java.time.Duration; import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.Objects; import java.util.Optional; import java.util.zip.GZIPInputStream; @@ -55,6 +59,10 @@ public class GeoIpService { private static final int UPDATE_INTERVAL_DAYS = 30; + // The server for MaxMind doesn't seem to understand RFC1123, + // but every HTTP implementation have to support RFC 1023 + private static final String TIME_RFC_1023 = "EEE, dd-MMM-yy HH:mm:ss zzz"; + private final Path dataFile; private final BukkitService bukkitService; @@ -104,7 +112,7 @@ public class GeoIpService { // don't fire the update task - we are up to date return true; } else { - ConsoleLogger.debug("GEO Ip database is older than " + UPDATE_INTERVAL_DAYS + " Days"); + ConsoleLogger.debug("GEO IP database is older than " + UPDATE_INTERVAL_DAYS + " Days"); } } catch (IOException ioEx) { ConsoleLogger.logException("Failed to load GeoLiteAPI database", ioEx); @@ -113,53 +121,90 @@ public class GeoIpService { } // File is outdated or doesn't exist - let's try to download the data file! - startDownloadTask(); + // use bukkit's cached threads + bukkitService.runTaskAsynchronously(this::updateDatabase); return false; } /** - * Create a thread which will attempt to download new data from the GeoLite website. + * Tries to update the database by downloading a new version from the website. */ - private void startDownloadTask() { + private void updateDatabase() { downloading = true; - // use bukkit's cached threads - bukkitService.runTaskAsynchronously(() -> { - ConsoleLogger.info("Downloading GEO IP database, because the old database is outdated or doesn't exist"); + ConsoleLogger.info("Downloading GEO IP database, because the old database is older than " + + UPDATE_INTERVAL_DAYS + " days or doesn't exist"); - Path tempFile = null; - try { - // download database to temporarily location - tempFile = Files.createTempFile(ARCHIVE_FILE, null); - try (OutputStream out = Files.newOutputStream(tempFile)) { - Resources.copy(new URL(ARCHIVE_URL), out); - } - - // MD5 checksum verification - String targetChecksum = Resources.toString(new URL(CHECKSUM_URL), StandardCharsets.UTF_8); - if (!verifyChecksum(Hashing.md5(), tempFile, targetChecksum)) { - return; - } - - // tar extract database and copy to target destination - if (!extractDatabase(tempFile, dataFile)) { - ConsoleLogger.warning("Cannot find database inside downloaded GEO IP file at " + tempFile); - return; - } - - ConsoleLogger.info("Successfully downloaded new GEO IP database to " + dataFile); - - //only set this value to false on success otherwise errors could lead to endless download triggers - downloading = false; - } catch (IOException ioEx) { - ConsoleLogger.logException("Could not download GeoLiteAPI database", ioEx); - } finally { - // clean up - if (tempFile != null) { - FileUtils.delete(tempFile.toFile()); - } + Path tempFile = null; + try { + // download database to temporarily location + tempFile = Files.createTempFile(ARCHIVE_FILE, null); + if (!downloadDatabaseArchive(tempFile)) { + ConsoleLogger.info("There is no newer GEO IP database uploaded. Using the old one for now."); + return; } - }); + + // MD5 checksum verification + String expectedChecksum = Resources.toString(new URL(CHECKSUM_URL), StandardCharsets.UTF_8); + verifyChecksum(Hashing.md5(), tempFile, expectedChecksum); + + // tar extract database and copy to target destination + extractDatabase(tempFile, dataFile); + + //only set this value to false on success otherwise errors could lead to endless download triggers + ConsoleLogger.info("Successfully downloaded new GEO IP database to " + dataFile); + downloading = false; + } catch (IOException ioEx) { + ConsoleLogger.logException("Could not download GeoLiteAPI database", ioEx); + } finally { + // clean up + if (tempFile != null) { + FileUtils.delete(tempFile.toFile()); + } + } + } + + /** + * Downloads the archive to the destination file if it's newer than the locally version. + * + * @param lastModified modification timestamp of the already present file + * @param destination save file + * @return false if we already have the newest version, true if successful + * @throws IOException if failed during downloading and writing to destination file + */ + private boolean downloadDatabaseArchive(Instant lastModified, Path destination) throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL(ARCHIVE_URL).openConnection(); + if (lastModified != null) { + // Only download if we actually need a newer version - this field is specified in GMT zone + ZonedDateTime zonedTime = lastModified.atZone(ZoneId.of("GMT")); + String timeFormat = DateTimeFormatter.ofPattern(TIME_RFC_1023).format(zonedTime); + connection.addRequestProperty("If-Modified-Since", timeFormat); + } + + if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) { + //we already have the newest version + connection.getInputStream().close(); + return false; + } + + Files.copy(connection.getInputStream(), destination, StandardCopyOption.REPLACE_EXISTING); + return true; + } + + /** + * Downloads the archive to the destination file if it's newer than the locally version. + * + * @param destination save file + * @return false if we already have the newest version, true if successful + * @throws IOException if failed during downloading and writing to destination file + */ + private boolean downloadDatabaseArchive(Path destination) throws IOException { + Instant lastModified = null; + if (Files.exists(dataFile)) { + lastModified = Files.getLastModifiedTime(dataFile).toInstant(); + } + + return downloadDatabaseArchive(lastModified, destination); } /** @@ -168,19 +213,15 @@ public class GeoIpService { * @param function the checksum function like MD5, SHA256 used to generate the checksum from the file * @param file the file we want to calculate the checksum from * @param expectedChecksum the expected checksum - * @return true if equal, false otherwise - * @throws IOException on I/O error reading the file + * @throws IOException on I/O error reading the file or the checksum verification failed */ - private boolean verifyChecksum(HashFunction function, Path file, String expectedChecksum) throws IOException { + private void verifyChecksum(HashFunction function, Path file, String expectedChecksum) throws IOException { HashCode actualHash = function.hashBytes(Files.readAllBytes(file)); HashCode expectedHash = HashCode.fromString(expectedChecksum); - if (Objects.equals(actualHash, expectedHash)) { - return true; + if (!Objects.equals(actualHash, expectedHash)) { + throw new IOException("GEO IP Checksum verification failed. " + + "Expected: " + expectedChecksum + "Actual:" + actualHash); } - - ConsoleLogger.warning("GEO IP checksum verification failed"); - ConsoleLogger.warning("Expected: " + expectedHash + " Actual: " + actualHash); - return false; } /** @@ -188,38 +229,37 @@ public class GeoIpService { * * @param tarInputFile gzipped tar input file where the database is * @param outputFile destination file for the database - * @return true if the database was found, false otherwise - * @throws IOException on I/O error reading the tar archive or writing the output + * @throws IOException on I/O error reading the tar archive, or writing the output + * @throws FileNotFoundException if the database cannot be found inside the archive */ - private boolean extractDatabase(Path tarInputFile, Path outputFile) throws IOException { + private void extractDatabase(Path tarInputFile, Path outputFile) throws FileNotFoundException, IOException { // .gz -> gzipped file try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(tarInputFile)); TarInputStream tarIn = new TarInputStream(new GZIPInputStream(in))) { - TarEntry entry; - while ((entry = tarIn.getNextEntry()) != null) { - if (!entry.isDirectory()) { - // filename including folders (absolute path inside the archive) - String filename = entry.getName(); - if (filename.endsWith(DATABASE_EXT)) { - // found the database file - Files.copy(tarIn, outputFile, StandardCopyOption.REPLACE_EXISTING); - - // update the last modification date to be same as in the archive - Files.setLastModifiedTime(outputFile, FileTime.from(entry.getModTime().toInstant())); - return true; - } + for (TarEntry entry = tarIn.getNextEntry(); entry != null; entry = tarIn.getNextEntry()) { + // filename including folders (absolute path inside the archive) + String filename = entry.getName(); + if (entry.isDirectory() || !filename.endsWith(DATABASE_EXT)) { + continue; } + + // found the database file and copy file + Files.copy(tarIn, outputFile, StandardCopyOption.REPLACE_EXISTING); + + // update the last modification date to be same as in the archive + Files.setLastModifiedTime(outputFile, FileTime.from(entry.getModTime().toInstant())); + return; } } - return false; + throw new FileNotFoundException("Cannot find database inside downloaded GEO IP file at " + tarInputFile); } /** * Get the country code of the given IP address. * * @param ip textual IP address to lookup. - * @return two-character ISO 3166-1 alpha code for the country. + * @return two-character ISO 3166-1 alpha code for the country or "--" if it cannot be fetched. */ public String getCountryCode(String ip) { return getCountry(ip).map(Country::getIsoCode).orElse("--"); @@ -229,7 +269,7 @@ public class GeoIpService { * Get the country name of the given IP address. * * @param ip textual IP address to lookup. - * @return The name of the country. + * @return The name of the country or "N/A" if it cannot be fetched. */ public String getCountryName(String ip) { return getCountry(ip).map(Country::getName).orElse("N/A"); @@ -255,7 +295,7 @@ public class GeoIpService { try { InetAddress address = InetAddress.getByName(ip); - //Reader.getCountry() can be null for unknown addresses + // Reader.getCountry() can be null for unknown addresses return Optional.ofNullable(databaseReader.getCountry(address)).map(CountryResponse::getCountry); } catch (UnknownHostException e) { // Ignore invalid ip addresses From f39141ed537fae44af24ee10d133d67d496f8d9a Mon Sep 17 00:00:00 2001 From: games647 Date: Sat, 2 Jun 2018 21:50:43 +0200 Subject: [PATCH 148/155] Fix race condition starting multiple database downloads (Related #1581) --- src/main/java/fr/xephi/authme/service/GeoIpService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index 5f1fe86db..64e10d60e 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -120,6 +120,9 @@ public class GeoIpService { } } + //set the downloading flag in order to fix race conditions outside + downloading = true; + // File is outdated or doesn't exist - let's try to download the data file! // use bukkit's cached threads bukkitService.runTaskAsynchronously(this::updateDatabase); @@ -130,8 +133,6 @@ public class GeoIpService { * Tries to update the database by downloading a new version from the website. */ private void updateDatabase() { - downloading = true; - ConsoleLogger.info("Downloading GEO IP database, because the old database is older than " + UPDATE_INTERVAL_DAYS + " days or doesn't exist"); From 135e323358761d2d78aaf68aac66117c1733f63d Mon Sep 17 00:00:00 2001 From: games647 Date: Sun, 3 Jun 2018 09:47:52 +0200 Subject: [PATCH 149/155] Set the downloading flag in order to mark it as successful (Related #1581) --- src/main/java/fr/xephi/authme/service/GeoIpService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index 64e10d60e..363264bc2 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -141,7 +141,8 @@ public class GeoIpService { // download database to temporarily location tempFile = Files.createTempFile(ARCHIVE_FILE, null); if (!downloadDatabaseArchive(tempFile)) { - ConsoleLogger.info("There is no newer GEO IP database uploaded. Using the old one for now."); + ConsoleLogger.info("There is no newer GEO IP database uploaded to MaxMind. Using the old one for now."); + downloading = false; return; } From 0a3b66bc7d1110d74ddb07a1873d1ea647198e5e Mon Sep 17 00:00:00 2001 From: games647 Date: Sun, 3 Jun 2018 13:34:51 +0200 Subject: [PATCH 150/155] Start a reading instance after downloading (Related #1581) --- .../fr/xephi/authme/service/GeoIpService.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index 363264bc2..be2746878 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -106,8 +106,7 @@ public class GeoIpService { try { FileTime lastModifiedTime = Files.getLastModifiedTime(dataFile); if (Duration.between(lastModifiedTime.toInstant(), Instant.now()).toDays() <= UPDATE_INTERVAL_DAYS) { - databaseReader = new Reader(dataFile.toFile(), FileMode.MEMORY, new CHMCache()); - ConsoleLogger.info(LICENSE); + startReading(); // don't fire the update task - we are up to date return true; @@ -142,7 +141,7 @@ public class GeoIpService { tempFile = Files.createTempFile(ARCHIVE_FILE, null); if (!downloadDatabaseArchive(tempFile)) { ConsoleLogger.info("There is no newer GEO IP database uploaded to MaxMind. Using the old one for now."); - downloading = false; + startReading(); return; } @@ -155,7 +154,7 @@ public class GeoIpService { //only set this value to false on success otherwise errors could lead to endless download triggers ConsoleLogger.info("Successfully downloaded new GEO IP database to " + dataFile); - downloading = false; + startReading(); } catch (IOException ioEx) { ConsoleLogger.logException("Could not download GeoLiteAPI database", ioEx); } finally { @@ -166,6 +165,14 @@ public class GeoIpService { } } + private void startReading() throws IOException { + databaseReader = new Reader(dataFile.toFile(), FileMode.MEMORY, new CHMCache()); + ConsoleLogger.info(LICENSE); + + // clear downloading flag, because we now have working reader instance + downloading = false; + } + /** * Downloads the archive to the destination file if it's newer than the locally version. * From 38fd133e82e58abc9acf790a99e32242ac04f231 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 19 Jun 2018 18:44:32 +0200 Subject: [PATCH 151/155] Update libraries --- README.md | 1 - pom.xml | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1688990cc..5b91291c6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ | **Code quality:** | [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) | | **Jenkins CI:** | [![Jenkins Status](https://img.shields.io/website-up-down-green-red/http/shields.io.svg?label=ci.codemc.org)](https://ci.codemc.org/) [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded) ![Build Tests](https://img.shields.io/jenkins/t/https/ci.codemc.org/job/AuthMe/job/AuthMeReloaded.svg) | | **Other CIs:** | [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) | -| **Dependencies:** | [![Dependency Status](https://beta.gemnasium.com/badges/github.com/AuthMe/AuthMeReloaded.svg)](https://beta.gemnasium.com/projects/github.com/AuthMe/AuthMeReloaded) | ## Description diff --git a/pom.xml b/pom.xml index 3f0fbfa11..c772c9f17 100644 --- a/pom.xml +++ b/pom.xml @@ -135,12 +135,12 @@ org.apache.maven.plugins maven-clean-plugin - 3.0.0 + 3.1.0 org.apache.maven.plugins maven-resources-plugin - 3.0.2 + 3.1.0 org.apache.maven.plugins @@ -186,12 +186,12 @@ org.apache.maven.plugins maven-jar-plugin - 3.0.2 + 3.1.0 org.apache.maven.plugins maven-javadoc-plugin - 3.0.0 + 3.0.1 attach-javadoc @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.1.1 package @@ -441,7 +441,7 @@ com.zaxxer HikariCP - 3.1.0 + 3.2.0 true @@ -828,7 +828,7 @@ org.mockito mockito-core test - 2.18.3 + 2.19.0 hamcrest-core From fc07ad3df1887cf6b562af0b2ebde772b112c8bf Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 25 Jun 2018 21:54:42 +0200 Subject: [PATCH 152/155] Update translations page --- docs/translations.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/translations.md b/docs/translations.md index 66f4c7937..c8ace37b1 100644 --- a/docs/translations.md +++ b/docs/translations.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code @@ -21,12 +21,12 @@ Code | Language | Translated |   [gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 48% | bar [hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 87% | bar [id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 47% | bar -[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 92% | bar +[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | bar [ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 89% | bar [lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 36% | bar [nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 80% | bar -[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 92% | bar -[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 80% | bar +[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | bar +[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 100% | bar [ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 80% | bar [ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 92% | bar [sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 80% | bar @@ -41,4 +41,4 @@ Code | Language | Translated |   --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:09:12 CEST 2018 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Mon Jun 25 21:53:35 CEST 2018 From 0227cb3f7410223514dacbf99f94ec19a689eb3a Mon Sep 17 00:00:00 2001 From: games647 Date: Wed, 4 Jul 2018 02:05:17 +0200 Subject: [PATCH 153/155] Add IPv6 support for isLocal checks (#1592) * Add IPv6 support for isLocal checks * Replace magic values like 127.0.0.1 and use our utility * Support for IPv6 local adresses in IPv6 only or dual stack environments * Loopback [::1] * Site-Local fc00::/7 * Link-local fe80::/10 * Introduce extra method for loopback addresses * Use public IP for passMaxLogin check * Use non-local IP addresses in test after change in verification --- .../authme/process/join/AsynchronousJoin.java | 4 +- .../process/login/AsynchronousLogin.java | 4 +- .../process/register/AsyncRegister.java | 4 +- .../authme/util/InternetProtocolUtils.java | 60 ++++++++++++++++--- .../process/login/AsynchronousLoginTest.java | 30 +++++----- .../util/InternetProtocolUtilsTest.java | 32 +++++++++- 6 files changed, 104 insertions(+), 30 deletions(-) diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index 7fb13bac6..9924eef8e 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -18,6 +18,7 @@ import fr.xephi.authme.settings.commandconfig.CommandManager; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.util.InternetProtocolUtils; import fr.xephi.authme.util.PlayerUtils; import org.bukkit.GameMode; import org.bukkit.Server; @@ -183,8 +184,7 @@ public class AsynchronousJoin implements AsynchronousProcess { private boolean validatePlayerCountForIp(final Player player, String ip) { if (service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP) > 0 && !service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) - && !"127.0.0.1".equalsIgnoreCase(ip) - && !"localhost".equalsIgnoreCase(ip) + && !InternetProtocolUtils.isLoopbackAddress(ip) && countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index 22c46dd95..2441ea283 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -30,6 +30,7 @@ import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.util.InternetProtocolUtils; import fr.xephi.authme.util.PlayerUtils; import fr.xephi.authme.util.Utils; import org.bukkit.ChatColor; @@ -325,8 +326,7 @@ public class AsynchronousLogin implements AsynchronousProcess { // Do not perform the check if player has multiple accounts permission or if IP is localhost if (service.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP) <= 0 || service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) - || "127.0.0.1".equalsIgnoreCase(ip) - || "localhost".equalsIgnoreCase(ip)) { + || InternetProtocolUtils.isLoopbackAddress(ip)) { return false; } 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 fa5d03613..2655c6819 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -16,6 +16,7 @@ import fr.xephi.authme.service.bungeecord.BungeeSender; import fr.xephi.authme.service.bungeecord.MessageType; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.util.InternetProtocolUtils; import fr.xephi.authme.util.PlayerUtils; import org.bukkit.entity.Player; @@ -119,8 +120,7 @@ public class AsyncRegister implements AsynchronousProcess { final int maxRegPerIp = service.getProperty(RestrictionSettings.MAX_REGISTRATION_PER_IP); final String ip = PlayerUtils.getPlayerIp(player); if (maxRegPerIp > 0 - && !"127.0.0.1".equalsIgnoreCase(ip) - && !"localhost".equalsIgnoreCase(ip) + && !InternetProtocolUtils.isLoopbackAddress(ip) && !service.hasPermission(player, ALLOW_MULTIPLE_ACCOUNTS)) { List otherAccounts = database.getAllAuthsByIp(ip); if (otherAccounts.size() >= maxRegPerIp) { diff --git a/src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java b/src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java index 548e1e913..039421548 100644 --- a/src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java +++ b/src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java @@ -1,16 +1,13 @@ package fr.xephi.authme.util; -import java.util.regex.Pattern; +import java.net.InetAddress; +import java.net.UnknownHostException; /** * Utility class about the InternetProtocol */ public final class InternetProtocolUtils { - private static final Pattern LOCAL_ADDRESS_PATTERN = - Pattern.compile("(^127\\.)|(^(0)?10\\.)|(^172\\.(0)?1[6-9]\\.)|(^172\\.(0)?2[0-9]\\.)" - + "|(^172\\.(0)?3[0-1]\\.)|(^169\\.254\\.)|(^192\\.168\\.)"); - // Utility class private InternetProtocolUtils() { } @@ -19,10 +16,57 @@ public final class InternetProtocolUtils { * Checks if the specified address is a private or loopback address * * @param address address to check - * - * @return true if the address is a local or loopback address, false otherwise + * @return true if the address is a local (site and link) or loopback address, false otherwise */ public static boolean isLocalAddress(String address) { - return LOCAL_ADDRESS_PATTERN.matcher(address).find(); + try { + InetAddress inetAddress = InetAddress.getByName(address); + + // Examples: 127.0.0.1, localhost or [::1] + return isLoopbackAddress(address) + // Example: 10.0.0.0, 172.16.0.0, 192.168.0.0, fec0::/10 (deprecated) + // Ref: https://en.wikipedia.org/wiki/IP_address#Private_addresses + || inetAddress.isSiteLocalAddress() + // Example: 169.254.0.0/16, fe80::/10 + // Ref: https://en.wikipedia.org/wiki/IP_address#Address_autoconfiguration + || inetAddress.isLinkLocalAddress() + // non deprecated unique site-local that java doesn't check yet -> fc00::/7 + || isIPv6UniqueSiteLocal(inetAddress); + } catch (UnknownHostException e) { + return false; + } + } + + /** + * Checks if the specified address is a loopback address. This can be one of the following: + *

      + *
    • 127.0.0.1
    • + *
    • localhost
    • + *
    • [::1]
    • + *
    + * + * @param address address to check + * @return true if the address is a loopback one + */ + public static boolean isLoopbackAddress(String address) { + try { + InetAddress inetAddress = InetAddress.getByName(address); + return inetAddress.isLoopbackAddress(); + } catch (UnknownHostException e) { + return false; + } + } + + private static boolean isLoopbackAddress(InetAddress address) { + return address.isLoopbackAddress(); + } + + private static boolean isIPv6UniqueSiteLocal(InetAddress address) { + // ref: https://en.wikipedia.org/wiki/Unique_local_address + + // currently undefined but could be used in the near future fc00::/8 + return (address.getAddress()[0] & 0xFF) == 0xFC + // in use for unique site-local fd00::/8 + || (address.getAddress()[0] & 0xFF) == 0xFD; } } diff --git a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java index f19810172..2c02e581f 100644 --- a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java +++ b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java @@ -124,7 +124,7 @@ public class AsynchronousLoginTest { public void shouldNotForceLoginUserWithAlreadyOnlineIp() { // given String name = "oscar"; - String ip = "127.0.12.245"; + String ip = "1.1.1.245"; Player player = mockPlayer(name); TestHelper.mockPlayerIp(player, ip); given(playerCache.isAuthenticated(name)).willReturn(false); @@ -147,7 +147,7 @@ public class AsynchronousLoginTest { public void shouldNotForceLoginForCanceledEvent() { // given String name = "oscar"; - String ip = "127.0.12.245"; + String ip = "1.1.1.245"; Player player = mockPlayer(name); TestHelper.mockPlayerIp(player, ip); given(playerCache.isAuthenticated(name)).willReturn(false); @@ -180,7 +180,7 @@ public class AsynchronousLoginTest { mockOnlinePlayersInBukkitService(); // when - boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "127.0.0.4"); + boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "1.1.1.1"); // then assertThat(result, equalTo(false)); @@ -195,7 +195,7 @@ public class AsynchronousLoginTest { given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(0); // when - boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "192.168.0.1"); + boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "2.2.2.2"); // then assertThat(result, equalTo(false)); @@ -210,7 +210,7 @@ public class AsynchronousLoginTest { given(commonService.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(true); // when - boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "127.0.0.4"); + boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "1.1.1.1"); // then assertThat(result, equalTo(false)); @@ -227,7 +227,7 @@ public class AsynchronousLoginTest { mockOnlinePlayersInBukkitService(); // when - boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "192.168.0.1"); + boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "2.2.2.2"); // then assertThat(result, equalTo(true)); @@ -242,28 +242,28 @@ public class AsynchronousLoginTest { } private void mockOnlinePlayersInBukkitService() { - // 127.0.0.4: albania (online), brazil (offline) + // 1.1.1.1: albania (online), brazil (offline) Player playerA = mockPlayer("albania"); - TestHelper.mockPlayerIp(playerA, "127.0.0.4"); + TestHelper.mockPlayerIp(playerA, "1.1.1.1"); given(dataSource.isLogged(playerA.getName())).willReturn(true); Player playerB = mockPlayer("brazil"); - TestHelper.mockPlayerIp(playerB, "127.0.0.4"); + TestHelper.mockPlayerIp(playerB, "1.1.1.1"); given(dataSource.isLogged(playerB.getName())).willReturn(false); - // 192.168.0.1: congo (online), denmark (offline), ecuador (online) + // 2.2.2.2: congo (online), denmark (offline), ecuador (online) Player playerC = mockPlayer("congo"); - TestHelper.mockPlayerIp(playerC, "192.168.0.1"); + TestHelper.mockPlayerIp(playerC, "2.2.2.2"); given(dataSource.isLogged(playerC.getName())).willReturn(true); Player playerD = mockPlayer("denmark"); - TestHelper.mockPlayerIp(playerD, "192.168.0.1"); + TestHelper.mockPlayerIp(playerD, "2.2.2.2"); given(dataSource.isLogged(playerD.getName())).willReturn(false); Player playerE = mockPlayer("ecuador"); - TestHelper.mockPlayerIp(playerE, "192.168.0.1"); + TestHelper.mockPlayerIp(playerE, "2.2.2.2"); given(dataSource.isLogged(playerE.getName())).willReturn(true); - // 192.168.0.0: france (offline) + // 3.3.3.3: france (offline) Player playerF = mockPlayer("france"); - TestHelper.mockPlayerIp(playerF, "192.168.0.0"); + TestHelper.mockPlayerIp(playerF, "3.3.3.3"); List onlinePlayers = Arrays.asList(playerA, playerB, playerC, playerD, playerE, playerF); returnGivenOnlinePlayers(bukkitService, onlinePlayers); diff --git a/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java b/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java index d45c0a578..02d8872a2 100644 --- a/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java @@ -3,8 +3,8 @@ package fr.xephi.authme.util; import fr.xephi.authme.TestHelper; import org.junit.Test; -import static org.junit.Assert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; /** * Test for {@link InternetProtocolUtils} @@ -13,14 +13,44 @@ public class InternetProtocolUtilsTest { @Test public void shouldCheckLocalAddress() { + // loopback + assertThat(InternetProtocolUtils.isLocalAddress("localhost"), equalTo(true)); assertThat(InternetProtocolUtils.isLocalAddress("127.0.0.1"), equalTo(true)); + assertThat(InternetProtocolUtils.isLocalAddress("::1"), equalTo(true)); + + // site local assertThat(InternetProtocolUtils.isLocalAddress("10.0.0.1"), equalTo(true)); assertThat(InternetProtocolUtils.isLocalAddress("172.0.0.1"), equalTo(false)); assertThat(InternetProtocolUtils.isLocalAddress("172.16.0.1"), equalTo(true)); assertThat(InternetProtocolUtils.isLocalAddress("192.168.0.1"), equalTo(true)); + + // deprecated site-local + // ref: https://en.wikipedia.org/wiki/IPv6_address#Default_address_selection + assertThat(InternetProtocolUtils.isLocalAddress("fec0::"), equalTo(true)); + + // unique site-local (not deprecated!) + // ref: https://en.wikipedia.org/wiki/Unique_local_address + assertThat(InternetProtocolUtils.isLocalAddress("fde4:8dba:82e1::"), equalTo(true)); + assertThat(InternetProtocolUtils.isLocalAddress("fc00::"), equalTo(true)); + assertThat(InternetProtocolUtils.isLocalAddress("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), equalTo(true)); + assertThat(InternetProtocolUtils.isLocalAddress("fe00::"), equalTo(false)); + + // link local + assertThat(InternetProtocolUtils.isLocalAddress("169.254.0.64"), equalTo(true)); + assertThat(InternetProtocolUtils.isLocalAddress("FE80:0000:0000:0000:C800:0EFF:FE74:0008"), equalTo(true)); + + // public assertThat(InternetProtocolUtils.isLocalAddress("94.32.34.5"), equalTo(false)); } + @Test + public void testIsLoopback() { + // loopback + assertThat(InternetProtocolUtils.isLoopbackAddress("localhost"), equalTo(true)); + assertThat(InternetProtocolUtils.isLoopbackAddress("127.0.0.1"), equalTo(true)); + assertThat(InternetProtocolUtils.isLoopbackAddress("::1"), equalTo(true)); + } + @Test public void shouldHavePrivateConstructor() { // given / when / then From 4c640fddd9b7c7194a94ac08e463a632e481df68 Mon Sep 17 00:00:00 2001 From: Pavel Leshchev Date: Wed, 4 Jul 2018 05:05:51 +0500 Subject: [PATCH 154/155] Add come translations (#1596) --- src/main/resources/messages/messages_ru.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index fd28978a4..c61d85691 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -145,11 +145,11 @@ time: # Two-factor authentication two_factor: code_created: '&2Ваш секретный код — %code. Просканируйте его здесь: %url' - # TODO confirmation_required: 'Please confirm your code with /2fa confirm ' + confirmation_required: 'Пожалуйста, подтвердите ваш код с помощью /2fa confirm <код>' # TODO code_required: 'Please submit your two-factor authentication code with /2fa code ' # TODO already_enabled: 'Two-factor authentication is already enabled for your account!' # TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add' - # TODO enable_success: 'Successfully enabled two-factor authentication for your account' + enable_success: 'Двухфакторная аутентификация для вашего аккаунта успешно подключена' # TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add' # TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add' # TODO removed_success: 'Successfully removed two-factor auth from your account' From 1d118afd17659287520417afe7887dec4fb85531 Mon Sep 17 00:00:00 2001 From: DNx Date: Sat, 7 Jul 2018 20:54:38 +0700 Subject: [PATCH 155/155] Fix #1587 AsyncPlayerPreLoginEvent#getAddress() sometimes return null if it unresolved. In that case we should pass it to PlayerLoginEvent to do the join verification process. --- .../fr/xephi/authme/listener/PlayerListener.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 660119ed8..b8c388e89 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -54,6 +54,8 @@ import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerShearEntityEvent; import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_MOVEMENT_RADIUS; import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT; @@ -95,6 +97,7 @@ public class PlayerListener implements Listener { private BungeeSender bungeeSender; private boolean isAsyncPlayerPreLoginEventCalled = false; + private List unresolvedPlayerHostname = new ArrayList<>(); @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { @@ -263,6 +266,11 @@ public class PlayerListener implements Listener { return; } + if (event.getAddress() == null) { + unresolvedPlayerHostname.add(event.getName()); + return; + } + final String name = event.getName(); if (validationService.isUnrestricted(name)) { @@ -307,7 +315,9 @@ public class PlayerListener implements Listener { return; } - if (!isAsyncPlayerPreLoginEventCalled || !settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT)) { + + if (!isAsyncPlayerPreLoginEventCalled || !settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT) + || unresolvedPlayerHostname.remove(name)) { try { runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress()); } catch (FailedVerificationException e) {