#1035 Migrate other accounts config from config.yml to commands.yml

This commit is contained in:
ljacqu 2018-01-21 18:58:20 +01:00
parent 456693ea59
commit 761ee2f05b
10 changed files with 158 additions and 40 deletions

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Wed Dec 13 23:12:29 CET 2017. See docs/config/config.tpl.md --> <!-- File auto-generated on Sun Jan 21 18:49:44 CET 2018. See docs/config/config.tpl.md -->
## AuthMe Configuration ## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
@ -214,11 +214,6 @@ settings:
# characters, which is what we recommend. See also http://asciitable.com # characters, which is what we recommend. See also http://asciitable.com
# You can test your regex with https://regex101.com # You can test your regex with https://regex101.com
allowedPasswordCharacters: '[!-~]*' allowedPasswordCharacters: '[!-~]*'
# Threshold of the other accounts command, a value less than 2 means disabled.
otherAccountsCmdThreshold: 0
# Command to run when a user has more accounts than the configured threshold.
# Available variables: %playername%, %playerip%
otherAccountsCmd: 'say The player %playername% with ip %playerip% has multiple accounts!'
GameMode: GameMode:
# Force survival gamemode when player joins? # Force survival gamemode when player joins?
ForceSurvivalMode: false ForceSurvivalMode: false
@ -444,6 +439,8 @@ Security:
captchaLength: 5 captchaLength: 5
# Minutes after which login attempts count is reset for a player # Minutes after which login attempts count is reset for a player
captchaCountReset: 60 captchaCountReset: 60
# Require captcha before a player may register?
requireForRegistration: false
tempban: tempban:
# Tempban a user's IP address if they enter the wrong password too many times # Tempban a user's IP address if they enter the wrong password too many times
enableTempban: false enableTempban: false
@ -558,4 +555,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 Wed Dec 13 23:12:29 CET 2017 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

View File

@ -15,8 +15,6 @@ import fr.xephi.authme.service.bungeecord.BungeeSender;
import fr.xephi.authme.settings.WelcomeMessageConfiguration; import fr.xephi.authme.settings.WelcomeMessageConfiguration;
import fr.xephi.authme.settings.commandconfig.CommandManager; import fr.xephi.authme.settings.commandconfig.CommandManager;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
@ -99,9 +97,6 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
bukkitService.callEvent(new LoginEvent(player)); bukkitService.callEvent(new LoginEvent(player));
player.saveData(); player.saveData();
// Run command if player has other accounts
runCommandOtherAccounts(authsWithSameIp, player);
// Login is done, display welcome message // Login is done, display welcome message
welcomeMessageConfiguration.sendWelcomeMessage(player); welcomeMessageConfiguration.sendWelcomeMessage(player);
@ -114,16 +109,4 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
// Send Bungee stuff. The service will check if it is enabled or not. // Send Bungee stuff. The service will check if it is enabled or not.
bungeeSender.connectPlayerOnLogin(player); bungeeSender.connectPlayerOnLogin(player);
} }
private void runCommandOtherAccounts(List<String> auths, Player player) {
int threshold = commonService.getProperty(RestrictionSettings.OTHER_ACCOUNTS_CMD_THRESHOLD);
String command = commonService.getProperty(RestrictionSettings.OTHER_ACCOUNTS_CMD);
if (threshold >= 2 && !command.isEmpty() && auths.size() >= threshold) {
bukkitService.dispatchConsoleCommand(command
.replace("%playername%", player.getName())
.replace("%playerip%", PlayerUtils.getPlayerIp(player))
);
}
}
} }

View File

@ -13,6 +13,7 @@ import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.StringUtils;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.File; import java.io.File;
@ -37,6 +38,13 @@ public class SettingsMigrationService extends PlainMigrationService {
private final File pluginFolder; private final File pluginFolder;
// Stores old "other accounts command" config if present.
// We need to store it in here for retrieval when we build the CommandConfig. Retrieving it from the config.yml is
// not possible since this migration service may trigger the config.yml to be resaved. As the old command settings
// don't exist in the code anymore, as soon as config.yml is resaved we lose this information.
private String oldOtherAccountsCommand;
private int oldOtherAccountsCommandThreshold;
@Inject @Inject
SettingsMigrationService(@DataFolder File pluginFolder) { SettingsMigrationService(@DataFolder File pluginFolder) {
this.pluginFolder = pluginFolder; this.pluginFolder = pluginFolder;
@ -51,6 +59,8 @@ public class SettingsMigrationService extends PlainMigrationService {
changes = true; changes = true;
} }
setOldOtherAccountsCommandFieldsIfSet(resource);
// Note ljacqu 20160211: Concatenating migration methods with | instead of the usual || // Note ljacqu 20160211: Concatenating migration methods with | instead of the usual ||
// ensures that all migrations will be performed // ensures that all migrations will be performed
return changes return changes
@ -75,7 +85,8 @@ public class SettingsMigrationService extends PlainMigrationService {
"Hooks.customAttributes", "Security.stop.kickPlayersBeforeStopping", "Hooks.customAttributes", "Security.stop.kickPlayersBeforeStopping",
"settings.restrictions.keepCollisionsDisabled", "settings.forceCommands", "settings.forceCommandsAsConsole", "settings.restrictions.keepCollisionsDisabled", "settings.forceCommands", "settings.forceCommandsAsConsole",
"settings.forceRegisterCommands", "settings.forceRegisterCommandsAsConsole", "settings.forceRegisterCommands", "settings.forceRegisterCommandsAsConsole",
"settings.sessions.sessionExpireOnIpChange"}; "settings.sessions.sessionExpireOnIpChange", "settings.restrictions.otherAccountsCmd",
"settings.restrictions.otherAccountsCmdThreshold"};
for (String deprecatedPath : deprecatedProperties) { for (String deprecatedPath : deprecatedProperties) {
if (resource.contains(deprecatedPath)) { if (resource.contains(deprecatedPath)) {
return true; return true;
@ -84,6 +95,20 @@ public class SettingsMigrationService extends PlainMigrationService {
return false; return false;
} }
// --------
// Old other accounts
// --------
public boolean hasOldOtherAccountsCommand() {
return !StringUtils.isEmpty(oldOtherAccountsCommand);
}
public String getOldOtherAccountsCommand() {
return oldOtherAccountsCommand;
}
public int getOldOtherAccountsCommandThreshold() {
return oldOtherAccountsCommandThreshold;
}
// -------- // --------
// Specific migrations // Specific migrations
@ -288,6 +313,22 @@ public class SettingsMigrationService extends PlainMigrationService {
return false; return false;
} }
/**
* Retrieves the old config to run a command when alt accounts are detected and sets them to this instance
* for further processing.
*
* @param resource The property resource
*/
private void setOldOtherAccountsCommandFieldsIfSet(PropertyResource resource) {
Property<String> commandProperty = newProperty("settings.restrictions.otherAccountsCmd", "");
Property<Integer> commandThresholdProperty = newProperty("settings.restrictions.otherAccountsCmdThreshold", 0);
if (commandProperty.isPresent(resource) && commandThresholdProperty.getValue(resource) >= 2) {
oldOtherAccountsCommand = commandProperty.getValue(resource);
oldOtherAccountsCommandThreshold = commandThresholdProperty.getValue(resource);
}
}
/** /**
* Checks for an old property path and moves it to a new path if it is present and the new path is not yet set. * Checks for an old property path and moves it to a new path if it is present and the new path is not yet set.
* *

View File

@ -5,8 +5,13 @@ import ch.jalu.configme.properties.Property;
import ch.jalu.configme.resource.PropertyResource; import ch.jalu.configme.resource.PropertyResource;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import fr.xephi.authme.settings.SettingsMigrationService;
import fr.xephi.authme.util.RandomStringUtils;
import javax.inject.Inject;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
/** /**
* Migrates the commands from their old location, in config.yml, to the dedicated commands configuration file. * Migrates the commands from their old location, in config.yml, to the dedicated commands configuration file.
@ -18,19 +23,42 @@ class CommandMigrationService implements MigrationService {
static final List<String> COMMAND_CONFIG_PROPERTIES = ImmutableList.of( static final List<String> COMMAND_CONFIG_PROPERTIES = ImmutableList.of(
"onJoin", "onLogin", "onSessionLogin", "onFirstLogin", "onRegister", "onUnregister", "onLogout"); "onJoin", "onLogin", "onSessionLogin", "onFirstLogin", "onRegister", "onUnregister", "onLogout");
@Inject
private SettingsMigrationService settingsMigrationService;
CommandMigrationService() { CommandMigrationService() {
} }
@Override @Override
public boolean checkAndMigrate(PropertyResource resource, List<Property<?>> properties) { public boolean checkAndMigrate(PropertyResource resource, List<Property<?>> properties) {
final CommandConfig commandConfig = CommandSettingsHolder.COMMANDS.getValue(resource); final CommandConfig commandConfig = CommandSettingsHolder.COMMANDS.getValue(resource);
if (isFileEmpty(resource)) { if (moveOtherAccountsConfig(commandConfig) || isFileEmpty(resource)) {
resource.setValue("", commandConfig); resource.setValue("", commandConfig);
return true; return true;
} }
return false; return false;
} }
private boolean moveOtherAccountsConfig(CommandConfig commandConfig) {
if (settingsMigrationService.hasOldOtherAccountsCommand()) {
OnLoginCommand command = new OnLoginCommand(
replaceOldPlaceholdersWithNew(settingsMigrationService.getOldOtherAccountsCommand()), Executor.CONSOLE);
command.setIfNumberOfAccountsAtLeast(
Optional.of(settingsMigrationService.getOldOtherAccountsCommandThreshold()));
Map<String, OnLoginCommand> onLoginCommands = commandConfig.getOnLogin();
onLoginCommands.put(RandomStringUtils.generate(10), command);
return true;
}
return false;
}
private static String replaceOldPlaceholdersWithNew(String oldOtherAccountsCommand) {
return oldOtherAccountsCommand
.replace("%playername%", "%p")
.replace("%playerip%", "%ip");
}
private static boolean isFileEmpty(PropertyResource resource) { private static boolean isFileEmpty(PropertyResource resource) {
return COMMAND_CONFIG_PROPERTIES.stream().anyMatch(property -> resource.getObject(property) == null); return COMMAND_CONFIG_PROPERTIES.stream().anyMatch(property -> resource.getObject(property) == null);
} }

View File

@ -7,8 +7,8 @@ import java.util.Optional;
*/ */
public class OnLoginCommand extends Command { public class OnLoginCommand extends Command {
private Optional<Integer> ifNumberOfAccountsAtLeast; private Optional<Integer> ifNumberOfAccountsAtLeast = Optional.empty();
private Optional<Integer> ifNumberOfAccountsLessThan; private Optional<Integer> ifNumberOfAccountsLessThan = Optional.empty();
/** /**
* Default constructor (for bean mapping). * Default constructor (for bean mapping).

View File

@ -180,18 +180,6 @@ public final class RestrictionSettings implements SettingsHolder {
public static final Property<List<String>> UNRESTRICTED_NAMES = public static final Property<List<String>> UNRESTRICTED_NAMES =
newLowercaseListProperty("settings.unrestrictions.UnrestrictedName"); newLowercaseListProperty("settings.unrestrictions.UnrestrictedName");
@Comment("Threshold of the other accounts command, a value less than 2 means disabled.")
public static final Property<Integer> OTHER_ACCOUNTS_CMD_THRESHOLD =
newProperty("settings.restrictions.otherAccountsCmdThreshold", 0);
@Comment({
"Command to run when a user has more accounts than the configured threshold.",
"Available variables: %playername%, %playerip%"
})
public static final Property<String> OTHER_ACCOUNTS_CMD =
newProperty("settings.restrictions.otherAccountsCmd",
"say The player %playername% with ip %playerip% has multiple accounts!");
private RestrictionSettings() { private RestrictionSettings() {
} }

View File

@ -97,6 +97,24 @@ public class SettingsMigrationServiceTest {
assertThat(migrationService.returnedValues, contains(true, false)); assertThat(migrationService.returnedValues, contains(true, false));
} }
@Test
public void shouldKeepOldOtherAccountsSettings() throws IOException {
// given
File dataFolder = temporaryFolder.newFolder();
File configFile = new File(dataFolder, "config.yml");
Files.copy(getJarFile(OLD_CONFIG_FILE), configFile);
PropertyResource resource = new YamlFileResource(configFile);
SettingsMigrationService migrationService = new SettingsMigrationService(dataFolder);
// when
migrationService.performMigrations(resource, AuthMeSettingsRetriever.buildConfigurationData().getProperties());
// then
assertThat(migrationService.hasOldOtherAccountsCommand(), equalTo(true));
assertThat(migrationService.getOldOtherAccountsCommand(), equalTo("msg admin %playername% has a lot of accounts!"));
assertThat(migrationService.getOldOtherAccountsCommandThreshold(), equalTo(5));
}
private void verifyHasUpToDateSettings(Settings settings, File dataFolder) throws IOException { private void verifyHasUpToDateSettings(Settings settings, File dataFolder) throws IOException {
assertThat(settings.getProperty(ALLOWED_NICKNAME_CHARACTERS), equalTo(ALLOWED_NICKNAME_CHARACTERS.getDefaultValue())); assertThat(settings.getProperty(ALLOWED_NICKNAME_CHARACTERS), equalTo(ALLOWED_NICKNAME_CHARACTERS.getDefaultValue()));
assertThat(settings.getProperty(DELAY_JOIN_MESSAGE), equalTo(true)); assertThat(settings.getProperty(DELAY_JOIN_MESSAGE), equalTo(true));

View File

@ -6,17 +6,26 @@ import ch.jalu.configme.configurationdata.ConfigurationDataBuilder;
import ch.jalu.configme.resource.PropertyResource; import ch.jalu.configme.resource.PropertyResource;
import ch.jalu.configme.resource.YamlFileResource; import ch.jalu.configme.resource.YamlFileResource;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.SettingsMigrationService;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.io.File; import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.collect.Sets.newHashSet;
import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
/** /**
* Test for {@link CommandMigrationService}. * Test for {@link CommandMigrationService}.
@ -27,6 +36,9 @@ public class CommandMigrationServiceTest {
@InjectMocks @InjectMocks
private CommandMigrationService commandMigrationService; private CommandMigrationService commandMigrationService;
@Mock
private SettingsMigrationService settingsMigrationService;
@BeforeClass @BeforeClass
public static void setUpLogger() { public static void setUpLogger() {
TestHelper.setupLogger(); TestHelper.setupLogger();
@ -90,4 +102,43 @@ public class CommandMigrationServiceTest {
// when / then // when / then
assertThat(CommandMigrationService.COMMAND_CONFIG_PROPERTIES, containsInAnyOrder(properties)); assertThat(CommandMigrationService.COMMAND_CONFIG_PROPERTIES, containsInAnyOrder(properties));
} }
@Test
public void shouldMigrateOldOtherAccountsCommand() {
// given
given(settingsMigrationService.hasOldOtherAccountsCommand()).willReturn(true);
given(settingsMigrationService.getOldOtherAccountsCommand())
.willReturn("helpop %playername% (%playerip%) has other accounts!");
given(settingsMigrationService.getOldOtherAccountsCommandThreshold()).willReturn(3);
File commandFile = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "settings/commandconfig/commands.complete.yml");
PropertyResource resource = new YamlFileResource(commandFile);
// when
commandMigrationService.checkAndMigrate(
resource, ConfigurationDataBuilder.collectData(CommandSettingsHolder.class).getProperties());
// then
Map<String, OnLoginCommand> onLoginCommands = CommandSettingsHolder.COMMANDS.getValue(resource).getOnLogin();
assertThat(onLoginCommands, aMapWithSize(6)); // 5 in the file + the newly migrated on
OnLoginCommand newCommand = getUnknownOnLoginCommand(onLoginCommands);
assertThat(newCommand.getCommand(), equalTo("helpop %p (%ip) has other accounts!"));
assertThat(newCommand.getExecutor(), equalTo(Executor.CONSOLE));
assertThat(newCommand.getIfNumberOfAccountsAtLeast().get(), equalTo(3));
assertThat(newCommand.getIfNumberOfAccountsLessThan().isPresent(), equalTo(false));
}
/*
* Returns the command under onLogin from commands.complete.yml that isn't present in the beginning.
*/
private static OnLoginCommand getUnknownOnLoginCommand(Map<String, OnLoginCommand> onLoginCommands) {
Set<String> knownKeys = newHashSet("welcome", "show_motd", "display_list", "warn_for_alts", "log_suspicious_user");
List<String> unknownKeys = onLoginCommands.keySet().stream()
.filter(key -> !knownKeys.contains(key))
.collect(Collectors.toList());
if (unknownKeys.size() == 1) {
return onLoginCommands.get(unknownKeys.get(0));
} else {
throw new IllegalStateException("Expected 1 unknown key but found " + unknownKeys.size() + ": " + unknownKeys);
}
}
} }

View File

@ -4,17 +4,20 @@ import ch.jalu.configme.configurationdata.ConfigurationDataBuilder;
import ch.jalu.configme.resource.PropertyResource; import ch.jalu.configme.resource.PropertyResource;
import ch.jalu.configme.resource.YamlFileResource; import ch.jalu.configme.resource.YamlFileResource;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.SettingsMigrationService;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.io.File; import java.io.File;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.verify;
/** /**
* Tests that commands.yml is well-formed. * Tests that commands.yml is well-formed.
@ -25,6 +28,9 @@ public class CommandYmlConsistencyTest {
@InjectMocks @InjectMocks
private CommandMigrationService commandMigrationService; private CommandMigrationService commandMigrationService;
@Mock
private SettingsMigrationService settingsMigrationService;
@Rule @Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder(); public TemporaryFolder temporaryFolder = new TemporaryFolder();
@ -40,5 +46,6 @@ public class CommandYmlConsistencyTest {
// then // then
assertThat(result, equalTo(false)); assertThat(result, equalTo(false));
verify(settingsMigrationService).hasOldOtherAccountsCommand();
} }
} }

View File

@ -154,6 +154,11 @@ settings:
noTeleport: false noTeleport: false
# Regex sintax for allowed Chars in passwords. # Regex sintax for allowed Chars in passwords.
allowedPasswordCharacters: '[\x21-\x7E]*' allowedPasswordCharacters: '[\x21-\x7E]*'
# Command to run when a user has more accounts than the configured threshold.
# Available variables: %playername%, %playerip%
otherAccountsCmd: 'msg admin %playername% has a lot of accounts!'
# Threshold of the other accounts command, a value less than 2 means disabled.
otherAccountsCmdThreshold: 5
GameMode: GameMode:
# ForceSurvivalMode to player when join ? # ForceSurvivalMode to player when join ?
ForceSurvivalMode: false ForceSurvivalMode: false