Sync repo files to b22

This commit is contained in:
HaHaWTH
2023-09-20 02:15:22 +08:00
parent ed320196fe
commit 3f6a7ce48f
478 changed files with 94676 additions and 0 deletions
@@ -0,0 +1,56 @@
package fr.xephi.authme.settings;
import ch.jalu.configme.properties.BaseProperty;
import ch.jalu.configme.properties.convertresult.ConvertErrorRecorder;
import ch.jalu.configme.resource.PropertyReader;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.collect.Sets.newHashSet;
/**
* Property whose value is a set of entries of a given enum.
*
* @param <E> the enum type
*/
public class EnumSetProperty<E extends Enum<E>> extends BaseProperty<Set<E>> {
private final Class<E> enumClass;
@SafeVarargs
public EnumSetProperty(Class<E> enumClass, String path, E... values) {
super(path, newHashSet(values));
this.enumClass = enumClass;
}
@Override
protected Set<E> getFromReader(PropertyReader reader, ConvertErrorRecorder errorRecorder) {
Object entry = reader.getObject(getPath());
if (entry instanceof Collection<?>) {
return ((Collection<?>) entry).stream()
.map(val -> toEnum(String.valueOf(val)))
.filter(e -> e != null)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
return null;
}
private E toEnum(String str) {
for (E e : enumClass.getEnumConstants()) {
if (str.equalsIgnoreCase(e.name())) {
return e;
}
}
return null;
}
@Override
public Object toExportValue(Set<E> value) {
return value.stream()
.map(Enum::name)
.collect(Collectors.toList());
}
}
@@ -0,0 +1,113 @@
package fr.xephi.authme.settings;
import ch.jalu.configme.SettingsManagerImpl;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.migration.MigrationService;
import ch.jalu.configme.resource.PropertyResource;
import com.google.common.io.Files;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static fr.xephi.authme.util.FileUtils.copyFileFromResource;
/**
* The AuthMe settings manager.
*/
public class Settings extends SettingsManagerImpl {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(Settings.class);
private final File pluginFolder;
private String passwordEmailMessage;
private String verificationEmailMessage;
private String recoveryCodeEmailMessage;
private String shutdownEmailMessage;
private String newPasswordEmailMessage;
/**
* Constructor.
*
* @param pluginFolder the AuthMe plugin folder
* @param resource the property resource to read and write properties to
* @param migrationService migration service to check the settings file with
* @param configurationData configuration data (properties and comments)
*/
public Settings(File pluginFolder, PropertyResource resource, MigrationService migrationService,
ConfigurationData configurationData) {
super(resource, configurationData, migrationService);
this.pluginFolder = pluginFolder;
loadSettingsFromFiles();
}
/**
* Return the text to use in email registrations.
*
* @return The email message
*/
public String getPasswordEmailMessage() {
return passwordEmailMessage;
}
/**
* Return the text for verification emails (before sensitive commands can be used).
*
* @return The email message
*/
public String getVerificationEmailMessage() {
return verificationEmailMessage;
}
/**
* Return the text to use when someone requests to receive a recovery code.
*
* @return The email message
*/
public String getRecoveryCodeEmailMessage() {
return recoveryCodeEmailMessage;
}
public String getShutdownEmailMessage() {return shutdownEmailMessage;}
public String getNewPasswordEmailMessage() {
return newPasswordEmailMessage;
}
private void loadSettingsFromFiles() {
newPasswordEmailMessage = readFile("new_email.html");
passwordEmailMessage = readFile("email.html");
verificationEmailMessage = readFile("verification_code_email.html");
recoveryCodeEmailMessage = readFile("recovery_code_email.html");
shutdownEmailMessage = readFile("shutdown.html");
String country = readFile("GeoLite2-Country.mmdb");
}
@Override
public void reload() {
super.reload();
loadSettingsFromFiles();
}
/**
* Reads a file from the plugin folder or copies it from the JAR to the plugin folder.
*
* @param filename the file to read
* @return the file's contents
*/
private String readFile(String filename) {
final File file = new File(pluginFolder, filename);
if (copyFileFromResource(file, filename)) {
try {
return Files.asCharSource(file, StandardCharsets.UTF_8).read();
} catch (IOException e) {
logger.logException("Failed to read file '" + filename + "':", e);
}
} else {
logger.warning("Failed to copy file '" + filename + "' from JAR");
}
return "";
}
}
@@ -0,0 +1,400 @@
package fr.xephi.authme.settings;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.migration.PlainMigrationService;
import ch.jalu.configme.properties.Property;
import ch.jalu.configme.properties.convertresult.PropertyValue;
import ch.jalu.configme.resource.PropertyReader;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.output.ConsoleLoggerFactory;
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;
import fr.xephi.authme.util.StringUtils;
import javax.inject.Inject;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static ch.jalu.configme.properties.PropertyInitializer.newListProperty;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
import static fr.xephi.authme.settings.properties.DatabaseSettings.MYSQL_POOL_SIZE;
import static fr.xephi.authme.settings.properties.RegistrationSettings.DELAY_JOIN_MESSAGE;
import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_JOIN_MESSAGE;
import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_LEAVE_MESSAGE;
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS;
import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN;
import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_ON_WORLDS;
/**
* Service for verifying that the configuration is up-to-date.
*/
public class SettingsMigrationService extends PlainMigrationService {
private static ConsoleLogger logger = ConsoleLoggerFactory.get(SettingsMigrationService.class);
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
SettingsMigrationService(@DataFolder File pluginFolder) {
this.pluginFolder = pluginFolder;
}
@Override
@SuppressWarnings("checkstyle:BooleanExpressionComplexity")
protected boolean performMigrations(PropertyReader reader, ConfigurationData configurationData) {
boolean changes = false;
if ("[a-zA-Z0-9_?]*".equals(reader.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) {
configurationData.setValue(ALLOWED_NICKNAME_CHARACTERS, "[a-zA-Z0-9_]*");
changes = true;
}
String driverClass = reader.getString("DataSource.mySQLDriverClassName");
if ("fr.xephi.authme.libs.org.mariadb.jdbc.Driver".equals(driverClass)) {
configurationData.setValue(DatabaseSettings.BACKEND, DataSourceType.MARIADB);
changes = true;
}
setOldOtherAccountsCommandFieldsIfSet(reader);
// Note ljacqu 20160211: Concatenating migration methods with | instead of the usual ||
// ensures that all migrations will be performed
return changes
| performMailTextToFileMigration(reader)
| migrateJoinLeaveMessages(reader, configurationData)
| migrateForceSpawnSettings(reader, configurationData)
| migratePoolSizeSetting(reader, configurationData)
| changeBooleanSettingToLogLevelProperty(reader, configurationData)
| hasOldHelpHeaderProperty(reader)
| hasSupportOldPasswordProperty(reader)
| convertToRegistrationType(reader, configurationData)
| mergeAndMovePermissionGroupSettings(reader, configurationData)
| moveDeprecatedHashAlgorithmIntoLegacySection(reader, configurationData)
| moveSaltColumnConfigWithOtherColumnConfigs(reader, configurationData)
|| hasDeprecatedProperties(reader);
}
private static boolean hasDeprecatedProperties(PropertyReader reader) {
String[] deprecatedProperties = {
"Converter.Rakamak.newPasswordHash", "Hooks.chestshop", "Hooks.legacyChestshop", "Hooks.notifications",
"Passpartu", "Performances", "settings.restrictions.enablePasswordVerifier", "Xenoforo.predefinedSalt",
"VeryGames", "settings.restrictions.allowAllCommandsIfRegistrationIsOptional", "DataSource.mySQLWebsite",
"Hooks.customAttributes", "Security.stop.kickPlayersBeforeStopping",
"settings.restrictions.keepCollisionsDisabled", "settings.forceCommands", "settings.forceCommandsAsConsole",
"settings.forceRegisterCommands", "settings.forceRegisterCommandsAsConsole",
"settings.sessions.sessionExpireOnIpChange", "settings.restrictions.otherAccountsCmd",
"settings.restrictions.otherAccountsCmdThreshold, DataSource.mySQLDriverClassName"};
for (String deprecatedPath : deprecatedProperties) {
if (reader.contains(deprecatedPath)) {
return true;
}
}
return false;
}
// --------
// Old other accounts
// --------
public boolean hasOldOtherAccountsCommand() {
return !StringUtils.isBlank(oldOtherAccountsCommand);
}
public String getOldOtherAccountsCommand() {
return oldOtherAccountsCommand;
}
public int getOldOtherAccountsCommandThreshold() {
return oldOtherAccountsCommandThreshold;
}
// --------
// Specific migrations
// --------
/**
* Check if {@code Email.mailText} is present and move it to the Email.html file if it doesn't exist yet.
*
* @param reader The property reader
* @return True if a migration has been completed, false otherwise
*/
private boolean performMailTextToFileMigration(PropertyReader reader) {
final String oldSettingPath = "Email.mailText";
final String oldMailText = reader.getString(oldSettingPath);
if (oldMailText == null) {
return false;
}
final File emailFile = new File(pluginFolder, "email.html");
final String mailText = oldMailText
.replace("<playername>", "<playername />").replace("%playername%", "<playername />")
.replace("<servername>", "<servername />").replace("%servername%", "<servername />")
.replace("<generatedpass>", "<generatedpass />").replace("%generatedpass%", "<generatedpass />")
.replace("<image>", "<image />").replace("%image%", "<image />");
if (!emailFile.exists()) {
try (FileWriter fw = new FileWriter(emailFile)) {
fw.write(mailText);
} catch (IOException e) {
logger.logException("Could not create email.html configuration file:", e);
}
}
return true;
}
/**
* Detect deprecated {@code settings.delayJoinLeaveMessages} and inform user of new "remove join messages"
* and "remove leave messages" settings.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean migrateJoinLeaveMessages(PropertyReader reader, ConfigurationData configData) {
Property<Boolean> oldDelayJoinProperty = newProperty("settings.delayJoinLeaveMessages", false);
boolean hasMigrated = moveProperty(oldDelayJoinProperty, DELAY_JOIN_MESSAGE, reader, configData);
if (hasMigrated) {
logger.info(String.format("Note that we now also have the settings %s and %s",
REMOVE_JOIN_MESSAGE.getPath(), REMOVE_LEAVE_MESSAGE.getPath()));
}
return hasMigrated;
}
/**
* Detects old "force spawn loc on join" and "force spawn on these worlds" settings and moves them
* to the new paths.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean migrateForceSpawnSettings(PropertyReader reader, ConfigurationData configData) {
Property<Boolean> oldForceLocEnabled = newProperty(
"settings.restrictions.ForceSpawnLocOnJoinEnabled", false);
Property<List<String>> oldForceWorlds = newListProperty(
"settings.restrictions.ForceSpawnOnTheseWorlds", "world", "world_nether", "world_the_ed");
return moveProperty(oldForceLocEnabled, FORCE_SPAWN_LOCATION_AFTER_LOGIN, reader, configData)
| moveProperty(oldForceWorlds, FORCE_SPAWN_ON_WORLDS, reader, configData);
}
/**
* Detects the old auto poolSize value and replaces it with the default value.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean migratePoolSizeSetting(PropertyReader reader, ConfigurationData configData) {
Integer oldValue = reader.getInt(MYSQL_POOL_SIZE.getPath());
if (oldValue == null || oldValue > 0) {
return false;
}
configData.setValue(MYSQL_POOL_SIZE, 10);
return true;
}
/**
* Changes the old boolean property "hide spam from console" to the new property specifying
* the log level.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean changeBooleanSettingToLogLevelProperty(PropertyReader reader,
ConfigurationData configData) {
final String oldPath = "Security.console.noConsoleSpam";
final Property<LogLevel> newProperty = PluginSettings.LOG_LEVEL;
if (!newProperty.isValidInResource(reader) && reader.contains(oldPath)) {
logger.info("Moving '" + oldPath + "' to '" + newProperty.getPath() + "'");
boolean oldValue = Optional.ofNullable(reader.getBoolean(oldPath)).orElse(false);
LogLevel level = oldValue ? LogLevel.INFO : LogLevel.FINE;
configData.setValue(newProperty, level);
return true;
}
return false;
}
private static boolean hasOldHelpHeaderProperty(PropertyReader reader) {
if (reader.contains("settings.helpHeader")) {
logger.warning("Help header setting is now in messages/help_xx.yml, "
+ "please check the file to set it again");
return true;
}
return false;
}
private static boolean hasSupportOldPasswordProperty(PropertyReader reader) {
String path = "settings.security.supportOldPasswordHash";
if (reader.contains(path)) {
logger.warning("Property '" + path + "' is no longer supported. "
+ "Use '" + SecuritySettings.LEGACY_HASHES.getPath() + "' instead.");
return true;
}
return false;
}
/**
* Converts old boolean configurations for registration to the new enum properties, if applicable.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean convertToRegistrationType(PropertyReader reader, ConfigurationData configData) {
String oldEmailRegisterPath = "settings.registration.enableEmailRegistrationSystem";
if (RegistrationSettings.REGISTRATION_TYPE.isValidInResource(reader)
|| !reader.contains(oldEmailRegisterPath)) {
return false;
}
boolean useEmail = newProperty(oldEmailRegisterPath, false).determineValue(reader).getValue();
RegistrationType registrationType = useEmail ? RegistrationType.EMAIL : RegistrationType.PASSWORD;
String useConfirmationPath = useEmail
? "settings.registration.doubleEmailCheck"
: "settings.restrictions.enablePasswordConfirmation";
boolean hasConfirmation = newProperty(useConfirmationPath, false).determineValue(reader).getValue();
RegisterSecondaryArgument secondaryArgument = hasConfirmation
? RegisterSecondaryArgument.CONFIRMATION
: RegisterSecondaryArgument.NONE;
logger.warning("Merging old registration settings into '"
+ RegistrationSettings.REGISTRATION_TYPE.getPath() + "'");
configData.setValue(RegistrationSettings.REGISTRATION_TYPE, registrationType);
configData.setValue(RegistrationSettings.REGISTER_SECOND_ARGUMENT, secondaryArgument);
return true;
}
/**
* Migrates old permission group settings to the new configurations.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean mergeAndMovePermissionGroupSettings(PropertyReader reader, ConfigurationData configData) {
boolean performedChanges;
// We have two old settings replaced by only one: move the first non-empty one
Property<String> oldUnloggedInGroup = newProperty("settings.security.unLoggedinGroup", "");
Property<String> oldRegisteredGroup = newProperty("GroupOptions.RegisteredPlayerGroup", "");
if (!oldUnloggedInGroup.determineValue(reader).getValue().isEmpty()) {
performedChanges = moveProperty(oldUnloggedInGroup, PluginSettings.REGISTERED_GROUP, reader, configData);
} else {
performedChanges = moveProperty(oldRegisteredGroup, PluginSettings.REGISTERED_GROUP, reader, configData);
}
// Move paths of other old options
performedChanges |= moveProperty(newProperty("GroupOptions.UnregisteredPlayerGroup", ""),
PluginSettings.UNREGISTERED_GROUP, reader, configData);
performedChanges |= moveProperty(newProperty("permission.EnablePermissionCheck", false),
PluginSettings.ENABLE_PERMISSION_CHECK, reader, configData);
return performedChanges;
}
/**
* If a deprecated hash is used, it is added to the legacy hashes option and the active hash
* is changed to SHA256.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean moveDeprecatedHashAlgorithmIntoLegacySection(PropertyReader reader,
ConfigurationData configData) {
HashAlgorithm currentHash = SecuritySettings.PASSWORD_HASH.determineValue(reader).getValue();
// Skip CUSTOM (has no class) and PLAINTEXT (is force-migrated later on in the startup process)
if (currentHash != HashAlgorithm.CUSTOM && currentHash != HashAlgorithm.PLAINTEXT) {
Class<?> encryptionClass = currentHash.getClazz();
if (encryptionClass.isAnnotationPresent(Deprecated.class)) {
configData.setValue(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256);
Set<HashAlgorithm> legacyHashes = SecuritySettings.LEGACY_HASHES.determineValue(reader).getValue();
legacyHashes.add(currentHash);
configData.setValue(SecuritySettings.LEGACY_HASHES, legacyHashes);
logger.warning("The hash algorithm '" + currentHash
+ "' is no longer supported for active use. New hashes will be in SHA256.");
return true;
}
}
return false;
}
/**
* Moves the property for the password salt column name to the same path as all other column name properties.
*
* @param reader The property reader
* @param configData Configuration data
* @return True if the configuration has changed, false otherwise
*/
private static boolean moveSaltColumnConfigWithOtherColumnConfigs(PropertyReader reader,
ConfigurationData configData) {
Property<String> oldProperty = newProperty("ExternalBoardOptions.mySQLColumnSalt",
DatabaseSettings.MYSQL_COL_SALT.getDefaultValue());
return moveProperty(oldProperty, DatabaseSettings.MYSQL_COL_SALT, reader, configData);
}
/**
* Retrieves the old config to run a command when alt accounts are detected and sets them to this instance
* for further processing.
*
* @param reader The property reader
*/
private void setOldOtherAccountsCommandFieldsIfSet(PropertyReader reader) {
Property<String> commandProperty = newProperty("settings.restrictions.otherAccountsCmd", "");
Property<Integer> commandThresholdProperty = newProperty("settings.restrictions.otherAccountsCmdThreshold", 0);
PropertyValue<String> commandPropValue = commandProperty.determineValue(reader);
int commandThreshold = commandThresholdProperty.determineValue(reader).getValue();
if (commandPropValue.isValidInResource() && commandThreshold >= 2) {
oldOtherAccountsCommand = commandPropValue.getValue();
oldOtherAccountsCommandThreshold = commandThreshold;
}
}
/**
* 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.
*
* @param oldProperty The old property (create a temporary {@link Property} object with the path)
* @param newProperty The new property to move the value to
* @param reader The property reader
* @param configData Configuration data
* @param <T> The type of the property
* @return True if a migration has been done, false otherwise
*/
protected static <T> boolean moveProperty(Property<T> oldProperty,
Property<T> newProperty,
PropertyReader reader,
ConfigurationData configData) {
PropertyValue<T> oldPropertyValue = oldProperty.determineValue(reader);
if (oldPropertyValue.isValidInResource()) {
if (reader.contains(newProperty.getPath())) {
logger.info("Detected deprecated property " + oldProperty.getPath());
} else {
logger.info("Renaming " + oldProperty.getPath() + " to " + newProperty.getPath());
configData.setValue(newProperty, oldPropertyValue.getValue());
}
return true;
}
return false;
}
}
@@ -0,0 +1,90 @@
package fr.xephi.authme.settings;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.ConsoleLoggerFactory;
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 javax.inject.Inject;
import java.util.Optional;
/**
* Logs warning messages in cases where the configured values suggest a misconfiguration.
* <p>
* Note that this class does not modify any settings and it is called after the settings have been fully loaded.
* For actual migrations (= verifications which trigger changes and a resave of the settings),
* see {@link SettingsMigrationService}.
*/
public class SettingsWarner {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(SettingsWarner.class);
@Inject
private Settings settings;
@Inject
private AuthMe authMe;
@Inject
private BukkitService bukkitService;
SettingsWarner() {
}
/**
* Logs warning when necessary to notify the user about misconfigurations.
*/
public void logWarningsForMisconfigurations() {
// Force single session disabled
if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) {
logger.warning("WARNING!!! By disabling ForceSingleSession, your server protection is inadequate!");
}
// Use TLS property only affects port 25
if (!settings.getProperty(EmailSettings.PORT25_USE_TLS)
&& settings.getProperty(EmailSettings.SMTP_PORT) != 25) {
logger.warning("Note: You have set Email.useTls to false but this only affects mail over port 25");
}
// Output hint if sessions are enabled that the timeout must be positive
if (settings.getProperty(PluginSettings.SESSIONS_ENABLED)
&& settings.getProperty(PluginSettings.SESSIONS_TIMEOUT) <= 0) {
logger.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 (isTrue(bukkitService.isBungeeCordConfiguredForSpigot())
&& !settings.getProperty(HooksSettings.BUNGEECORD)) {
logger.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!");
}
if (!isTrue(bukkitService.isBungeeCordConfiguredForSpigot())
&& settings.getProperty(HooksSettings.BUNGEECORD)) {
logger.warning("Note: Hooks.bungeecord is set to true but your server appears to be running in"
+ " non-bungeecord mode (see your spigot.yml). In order to prevent untrusted payload attack, "
+ "BungeeCord hook will be automatically disabled!");
}
// Check if argon2 library is present and can be loaded
if (settings.getProperty(SecuritySettings.PASSWORD_HASH).equals(HashAlgorithm.ARGON2)
&& !Argon2.isLibraryLoaded()) {
logger.warning("WARNING!!! You use Argon2 Hash Algorithm method but we can't find the Argon2 "
+ "library on your system! See https://github.com/AuthMe/AuthMeReloaded/wiki/Argon2-as-Password-Hash");
authMe.stopOrUnload();
}
}
private static boolean isTrue(Optional<Boolean> value) {
return value.isPresent() && value.get();
}
}
@@ -0,0 +1,378 @@
package fr.xephi.authme.settings;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.PluginHookService;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Locale;
/**
* Manager for spawn points. It loads spawn definitions from AuthMe and third-party plugins
* and is responsible for returning the correct spawn point as per the settings.
* <p>
* The spawn priority setting defines from which sources and in which order the spawn point
* should be taken from. In AuthMe, we can distinguish between the regular spawn and a "first spawn",
* to which players will be teleported who have joined for the first time.
*/
public class SpawnLoader implements Reloadable {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(SpawnLoader.class);
private final File authMeConfigurationFile;
private final Settings settings;
private final PluginHookService pluginHookService;
private FileConfiguration authMeConfiguration;
private String[] spawnPriority;
private Location essentialsSpawn;
private Location cmiSpawn;
/**
* Constructor.
*
* @param pluginFolder The AuthMe data folder
* @param settings The setting instance
* @param pluginHookService The plugin hooks instance
*/
@Inject
SpawnLoader(@DataFolder File pluginFolder, Settings settings, PluginHookService pluginHookService) {
File spawnFile = new File(pluginFolder, "spawn.yml");
FileUtils.copyFileFromResource(spawnFile, "spawn.yml");
this.authMeConfigurationFile = spawnFile;
this.settings = settings;
this.pluginHookService = pluginHookService;
reload();
}
/**
* (Re)loads the spawn file and relevant settings.
*/
@Override
public void reload() {
spawnPriority = settings.getProperty(RestrictionSettings.SPAWN_PRIORITY).split(",");
authMeConfiguration = YamlConfiguration.loadConfiguration(authMeConfigurationFile);
loadEssentialsSpawn();
}
/**
* Return the AuthMe spawn location.
*
* @return The location of the regular AuthMe spawn point
*/
public Location getSpawn() {
return getLocationFromConfiguration(authMeConfiguration, "spawn");
}
/**
* Set the AuthMe spawn point.
*
* @param location The location to use
*
* @return True upon success, false otherwise
*/
public boolean setSpawn(Location location) {
return setLocation("spawn", location);
}
/**
* Return the AuthMe first spawn location.
*
* @return The location of the AuthMe spawn point for first timers
*/
public Location getFirstSpawn() {
return getLocationFromConfiguration(authMeConfiguration, "firstspawn");
}
/**
* Set the AuthMe first spawn location.
*
* @param location The location to use
*
* @return True upon success, false otherwise
*/
public boolean setFirstSpawn(Location location) {
return setLocation("firstspawn", location);
}
/**
* Load the spawn point defined in EssentialsSpawn.
*/
public void loadEssentialsSpawn() {
// EssentialsSpawn cannot run without Essentials, so it's fine to get the Essentials data folder
File essentialsFolder = pluginHookService.getEssentialsDataFolder();
if (essentialsFolder == null) {
return;
}
File essentialsSpawnFile = new File(essentialsFolder, "spawn.yml");
if (essentialsSpawnFile.exists()) {
essentialsSpawn = getLocationFromConfiguration(
YamlConfiguration.loadConfiguration(essentialsSpawnFile), "spawns.default");
} else {
essentialsSpawn = null;
logger.info("Essentials spawn file not found: '" + essentialsSpawnFile.getAbsolutePath() + "'");
}
}
/**
* Unset the spawn point defined in EssentialsSpawn.
*/
public void unloadEssentialsSpawn() {
essentialsSpawn = null;
}
/**
* Load the spawn point defined in CMI.
*/
public void loadCmiSpawn() {
File cmiFolder = pluginHookService.getCmiDataFolder();
if (cmiFolder == null) {
return;
}
File cmiConfig = new File(cmiFolder, "config.yml");
if (cmiConfig.exists()) {
cmiSpawn = getLocationFromCmiConfiguration(YamlConfiguration.loadConfiguration(cmiConfig));
} else {
cmiSpawn = null;
logger.info("CMI config file not found: '" + cmiConfig.getAbsolutePath() + "'");
}
}
/**
* Unset the spawn point defined in CMI.
*/
public void unloadCmiSpawn() {
cmiSpawn = null;
}
/**
* Return the spawn location for the given player. The source of the spawn location varies
* depending on the spawn priority setting.
*
* @param player The player to retrieve the spawn point for
*
* @return The spawn location, or the default spawn location upon failure
*
* @see RestrictionSettings#SPAWN_PRIORITY
*/
public Location getSpawnLocation(Player player) {
if (player == null || player.getWorld() == null) {
return null;
}
World world = player.getWorld();
Location spawnLoc = null;
for (String priority : spawnPriority) {
switch (priority.toLowerCase(Locale.ROOT).trim()) {
case "default":
if (world.getSpawnLocation() != null) {
if (!isValidSpawnPoint(world.getSpawnLocation())) {
for (World spawnWorld : Bukkit.getWorlds()) {
if (isValidSpawnPoint(spawnWorld.getSpawnLocation())) {
world = spawnWorld;
break;
}
}
logger.warning("Seems like AuthMe is unable to find a proper spawn location. "
+ "Set a location with the command '/authme setspawn'");
}
spawnLoc = world.getSpawnLocation();
}
break;
case "multiverse":
if (settings.getProperty(HooksSettings.MULTIVERSE)) {
spawnLoc = pluginHookService.getMultiverseSpawn(world);
}
break;
case "essentials":
spawnLoc = essentialsSpawn;
break;
case "cmi":
spawnLoc = cmiSpawn;
break;
case "authme":
spawnLoc = getSpawn();
break;
default:
// ignore
}
if (spawnLoc != null) {
logger.debug("Spawn location determined as `{0}` for world `{1}`", spawnLoc, world.getName());
return spawnLoc;
}
}
logger.debug("Fall back to default world spawn location. World: `{0}`", world.getName());
return world.getSpawnLocation(); // return default location
}
/**
* Checks if a given location is a valid spawn point [!= (0,0,0)].
*
* @param location The location to check
*
* @return True upon success, false otherwise
*/
private boolean isValidSpawnPoint(Location location) {
if (location.getX() == 0 && location.getY() == 0 && location.getZ() == 0) {
return false;
}
return true;
}
/**
* Save the location under the given prefix.
*
* @param prefix The prefix to save the spawn under
* @param location The location to persist
*
* @return True upon success, false otherwise
*/
private boolean setLocation(String prefix, Location location) {
if (location != null && location.getWorld() != null) {
authMeConfiguration.set(prefix + ".world", location.getWorld().getName());
authMeConfiguration.set(prefix + ".x", location.getX());
authMeConfiguration.set(prefix + ".y", location.getY());
authMeConfiguration.set(prefix + ".z", location.getZ());
authMeConfiguration.set(prefix + ".yaw", location.getYaw());
authMeConfiguration.set(prefix + ".pitch", location.getPitch());
return saveAuthMeConfig();
}
return false;
}
private boolean saveAuthMeConfig() {
try {
authMeConfiguration.save(authMeConfigurationFile);
return true;
} catch (IOException e) {
logger.logException("Could not save spawn config (" + authMeConfigurationFile + ")", e);
}
return false;
}
/**
* Return player's location if player is alive, or player's spawn location if dead.
*
* @param player player to retrieve
*
* @return location of the given player if alive, spawn location if dead.
*/
public Location getPlayerLocationOrSpawn(Player player) {
if (player.getHealth() <= 0.0) {
return getSpawnLocation(player);
}
return player.getLocation();
}
/**
* Build a {@link Location} object from the given path in the file configuration.
*
* @param configuration The file configuration to read from
* @param pathPrefix The path to get the spawn point from
*
* @return Location corresponding to the values in the path
*/
private static Location getLocationFromConfiguration(FileConfiguration configuration, String pathPrefix) {
if (containsAllSpawnFields(configuration, pathPrefix)) {
String prefix = pathPrefix + ".";
String worldName = configuration.getString(prefix + "world");
World world = Bukkit.getWorld(worldName);
if (!StringUtils.isBlank(worldName) && world != null) {
return new Location(world, configuration.getDouble(prefix + "x"),
configuration.getDouble(prefix + "y"), configuration.getDouble(prefix + "z"),
getFloat(configuration, prefix + "yaw"), getFloat(configuration, prefix + "pitch"));
}
}
return null;
}
/**
* Build a {@link Location} object based on the CMI configuration.
*
* @param configuration The CMI file configuration to read from
*
* @return Location corresponding to the values in the path
*/
private static Location getLocationFromCmiConfiguration(FileConfiguration configuration) {
final String pathPrefix = "Spawn.Main";
if (isLocationCompleteInCmiConfig(configuration, pathPrefix)) {
String prefix = pathPrefix + ".";
String worldName = configuration.getString(prefix + "World");
World world = Bukkit.getWorld(worldName);
if (!StringUtils.isBlank(worldName) && world != null) {
return new Location(world, configuration.getDouble(prefix + "X"),
configuration.getDouble(prefix + "Y"), configuration.getDouble(prefix + "Z"),
getFloat(configuration, prefix + "Yaw"), getFloat(configuration, prefix + "Pitch"));
}
}
return null;
}
/**
* Return whether the file configuration contains all fields necessary to define a spawn
* under the given path.
*
* @param configuration The file configuration to use
* @param pathPrefix The path to verify
*
* @return True if all spawn fields are present, false otherwise
*/
private static boolean containsAllSpawnFields(FileConfiguration configuration, String pathPrefix) {
String[] fields = {"world", "x", "y", "z", "yaw", "pitch"};
for (String field : fields) {
if (!configuration.contains(pathPrefix + "." + field)) {
return false;
}
}
return true;
}
/**
* Return whether the CMI file configuration contains all spawn fields under the given path.
*
* @param cmiConfiguration The file configuration from CMI
* @param pathPrefix The path to verify
*
* @return True if all spawn fields are present, false otherwise
*/
private static boolean isLocationCompleteInCmiConfig(FileConfiguration cmiConfiguration, String pathPrefix) {
String[] fields = {"World", "X", "Y", "Z", "Yaw", "Pitch"};
for (String field : fields) {
if (!cmiConfiguration.contains(pathPrefix + "." + field)) {
return false;
}
}
return true;
}
/**
* Retrieve a property as a float from the given file configuration.
*
* @param configuration The file configuration to use
* @param path The path of the property to retrieve
*
* @return The float
*/
private static float getFloat(FileConfiguration configuration, String path) {
Object value = configuration.get(path);
// This behavior is consistent with FileConfiguration#getDouble
return (value instanceof Number) ? ((Number) value).floatValue() : 0;
}
}
@@ -0,0 +1,67 @@
package fr.xephi.authme.settings.commandconfig;
/**
* Command to be run.
*/
public class Command {
/** The command to execute. */
private String command;
/** The executor of the command. */
private Executor executor = Executor.PLAYER;
/** Delay before executing the command (in ticks) */
private long delay = 0;
/**
* Default constructor (for bean mapping).
*/
public Command() {
}
/**
* Creates a copy of this Command object, setting the given command text on the copy.
*
* @param command the command text to use in the copy
* @return copy of the source with the new command
*/
public Command copyWithCommand(String command) {
Command copy = new Command();
setValuesToCopyWithNewCommand(copy, command);
return copy;
}
protected void setValuesToCopyWithNewCommand(Command copy, String newCommand) {
copy.command = newCommand;
copy.executor = this.executor;
copy.delay = this.delay;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public Executor getExecutor() {
return executor;
}
public void setExecutor(Executor executor) {
this.executor = executor;
}
public long getDelay() {
return delay;
}
public void setDelay(long delay) {
this.delay = delay;
}
@Override
public String toString() {
return command + " (" + executor + ")";
}
}
@@ -0,0 +1,76 @@
package fr.xephi.authme.settings.commandconfig;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Command configuration.
*
* @see CommandManager
*/
public class CommandConfig {
private Map<String, Command> onJoin = new LinkedHashMap<>();
private Map<String, OnLoginCommand> onLogin = new LinkedHashMap<>();
private Map<String, Command> onSessionLogin = new LinkedHashMap<>();
private Map<String, OnLoginCommand> onFirstLogin = new LinkedHashMap<>();
private Map<String, Command> onRegister = new LinkedHashMap<>();
private Map<String, Command> onUnregister = new LinkedHashMap<>();
private Map<String, Command> onLogout = new LinkedHashMap<>();
public Map<String, Command> getOnJoin() {
return onJoin;
}
public void setOnJoin(Map<String, Command> onJoin) {
this.onJoin = onJoin;
}
public Map<String, OnLoginCommand> getOnLogin() {
return onLogin;
}
public void setOnLogin(Map<String, OnLoginCommand> onLogin) {
this.onLogin = onLogin;
}
public Map<String, Command> getOnSessionLogin() {
return onSessionLogin;
}
public void setOnSessionLogin(Map<String, Command> onSessionLogin) {
this.onSessionLogin = onSessionLogin;
}
public Map<String, OnLoginCommand> getOnFirstLogin() {
return onFirstLogin;
}
public void setOnFirstLogin(Map<String, OnLoginCommand> onFirstLogin) {
this.onFirstLogin = onFirstLogin;
}
public Map<String, Command> getOnRegister() {
return onRegister;
}
public void setOnRegister(Map<String, Command> onRegister) {
this.onRegister = onRegister;
}
public Map<String, Command> getOnUnregister() {
return onUnregister;
}
public void setOnUnregister(Map<String, Command> onUnregister) {
this.onUnregister = onUnregister;
}
public Map<String, Command> getOnLogout() {
return onLogout;
}
public void setOnLogout(Map<String, Command> onLogout) {
this.onLogout = onLogout;
}
}
@@ -0,0 +1,192 @@
package fr.xephi.authme.settings.commandconfig;
import ch.jalu.configme.SettingsManager;
import ch.jalu.configme.SettingsManagerBuilder;
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;
import fr.xephi.authme.util.lazytags.WrappedTagReplacer;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import static fr.xephi.authme.util.lazytags.TagBuilder.createTag;
/**
* Manages configurable commands to be run when various events occur.
*/
public class CommandManager implements Reloadable {
private final File dataFolder;
private final BukkitService bukkitService;
private final GeoIpService geoIpService;
private final CommandMigrationService commandMigrationService;
private final List<Tag<Player>> availableTags = buildAvailableTags();
private WrappedTagReplacer<Command, Player> onJoinCommands;
private WrappedTagReplacer<OnLoginCommand, Player> onLoginCommands;
private WrappedTagReplacer<Command, Player> onSessionLoginCommands;
private WrappedTagReplacer<OnLoginCommand, Player> onFirstLoginCommands;
private WrappedTagReplacer<Command, Player> onRegisterCommands;
private WrappedTagReplacer<Command, Player> onUnregisterCommands;
private WrappedTagReplacer<Command, Player> onLogoutCommands;
@Inject
CommandManager(@DataFolder File dataFolder, BukkitService bukkitService, GeoIpService geoIpService,
CommandMigrationService commandMigrationService) {
this.dataFolder = dataFolder;
this.bukkitService = bukkitService;
this.geoIpService = geoIpService;
this.commandMigrationService = commandMigrationService;
reload();
}
/**
* Runs the configured commands for when a player has joined.
*
* @param player the joining player
*/
public void runCommandsOnJoin(Player player) {
executeCommands(player, onJoinCommands.getAdaptedItems(player));
}
/**
* Runs the configured commands for when a player has successfully registered.
*
* @param player the player who has registered
*/
public void runCommandsOnRegister(Player player) {
executeCommands(player, onRegisterCommands.getAdaptedItems(player));
}
/**
* Runs the configured commands for when a player has logged in successfully.
*
* @param player the player that logged in
* @param otherAccounts account names whose IP is the same as the player's
*/
public void runCommandsOnLogin(Player player, List<String> otherAccounts) {
final int numberOfOtherAccounts = otherAccounts.size();
executeCommands(player, onLoginCommands.getAdaptedItems(player),
cmd -> shouldCommandBeRun(cmd, numberOfOtherAccounts));
}
/**
* Runs the configured commands for when a player has logged in successfully due to session.
*
* @param player the player that logged in
*/
public void runCommandsOnSessionLogin(Player player) {
executeCommands(player, onSessionLoginCommands.getAdaptedItems(player));
}
/**
* Runs the configured commands for when a player logs in the first time.
*
* @param player the player that has logged in for the first time
* @param otherAccounts account names whose IP is the same as the player's
*/
public void runCommandsOnFirstLogin(Player player, List<String> otherAccounts) {
final int numberOfOtherAccounts = otherAccounts.size();
executeCommands(player, onFirstLoginCommands.getAdaptedItems(player),
cmd -> shouldCommandBeRun(cmd, numberOfOtherAccounts));
}
/**
* Runs the configured commands for when a player has been unregistered.
*
* @param player the player that has been unregistered
*/
public void runCommandsOnUnregister(Player player) {
executeCommands(player, onUnregisterCommands.getAdaptedItems(player));
}
/**
* Runs the configured commands for when a player logs out (by command or by quitting the server).
*
* @param player the player that is no longer logged in
*/
public void runCommandsOnLogout(Player player) {
executeCommands(player, onLogoutCommands.getAdaptedItems(player));
}
private void executeCommands(Player player, List<Command> commands) {
executeCommands(player, commands, c -> true);
}
private <T extends Command> void executeCommands(Player player, List<T> commands, Predicate<T> predicate) {
for (T cmd : commands) {
if (predicate.test(cmd)) {
long delay = cmd.getDelay();
if (delay > 0) {
bukkitService.scheduleSyncDelayedTask(() -> dispatchCommand(player, cmd), delay);
} else {
dispatchCommand(player, cmd);
}
}
}
}
private void dispatchCommand(Player player, Command command) {
if (Executor.CONSOLE.equals(command.getExecutor())) {
bukkitService.dispatchConsoleCommand(command.getCommand());
} else {
bukkitService.dispatchCommand(player, command.getCommand());
}
}
private static boolean shouldCommandBeRun(OnLoginCommand command, int numberOfOtherAccounts) {
return (!command.getIfNumberOfAccountsAtLeast().isPresent()
|| command.getIfNumberOfAccountsAtLeast().get() <= numberOfOtherAccounts)
&& (!command.getIfNumberOfAccountsLessThan().isPresent()
|| command.getIfNumberOfAccountsLessThan().get() > numberOfOtherAccounts);
}
@Override
public void reload() {
File file = new File(dataFolder, "commands.yml");
FileUtils.copyFileFromResource(file, "commands.yml");
SettingsManager settingsManager = SettingsManagerBuilder
.withResource(YamlFileResourceProvider.loadFromFile(file))
.configurationData(CommandSettingsHolder.class)
.migrationService(commandMigrationService)
.create();
CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS);
onJoinCommands = newReplacer(commandConfig.getOnJoin());
onLoginCommands = newOnLoginCmdReplacer(commandConfig.getOnLogin());
onFirstLoginCommands = newOnLoginCmdReplacer(commandConfig.getOnFirstLogin());
onSessionLoginCommands = newReplacer(commandConfig.getOnSessionLogin());
onRegisterCommands = newReplacer(commandConfig.getOnRegister());
onUnregisterCommands = newReplacer(commandConfig.getOnUnregister());
onLogoutCommands = newReplacer(commandConfig.getOnLogout());
}
private WrappedTagReplacer<Command, Player> newReplacer(Map<String, Command> commands) {
return new WrappedTagReplacer<>(availableTags, commands.values(), Command::getCommand,
Command::copyWithCommand);
}
private WrappedTagReplacer<OnLoginCommand, Player> newOnLoginCmdReplacer(Map<String, OnLoginCommand> commands) {
return new WrappedTagReplacer<>(availableTags, commands.values(), Command::getCommand,
OnLoginCommand::copyWithCommand);
}
private List<Tag<Player>> buildAvailableTags() {
return Arrays.asList(
createTag("%p", Player::getName),
createTag("%nick", Player::getDisplayName),
createTag("%ip", PlayerUtils::getPlayerIp),
createTag("%country", pl -> geoIpService.getCountryName(PlayerUtils.getPlayerIp(pl))));
}
}
@@ -0,0 +1,66 @@
package fr.xephi.authme.settings.commandconfig;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.migration.MigrationService;
import ch.jalu.configme.resource.PropertyReader;
import com.google.common.annotations.VisibleForTesting;
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.Map;
import java.util.Optional;
/**
* Migrates the commands from their old location, in config.yml, to the dedicated commands configuration file.
*/
class CommandMigrationService implements MigrationService {
/** List of all properties in {@link CommandConfig}. */
@VisibleForTesting
static final List<String> COMMAND_CONFIG_PROPERTIES = ImmutableList.of(
"onJoin", "onLogin", "onSessionLogin", "onFirstLogin", "onRegister", "onUnregister", "onLogout");
@Inject
private SettingsMigrationService settingsMigrationService;
CommandMigrationService() {
}
@Override
public boolean checkAndMigrate(PropertyReader reader, ConfigurationData configurationData) {
final CommandConfig commandConfig = CommandSettingsHolder.COMMANDS.determineValue(reader).getValue();
if (moveOtherAccountsConfig(commandConfig) || isAnyCommandMissing(reader)) {
configurationData.setValue(CommandSettingsHolder.COMMANDS, commandConfig);
return true;
}
return false;
}
private boolean moveOtherAccountsConfig(CommandConfig commandConfig) {
if (settingsMigrationService.hasOldOtherAccountsCommand()) {
OnLoginCommand command = new OnLoginCommand();
command.setCommand(replaceOldPlaceholdersWithNew(settingsMigrationService.getOldOtherAccountsCommand()));
command.setExecutor(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 isAnyCommandMissing(PropertyReader reader) {
return COMMAND_CONFIG_PROPERTIES.stream().anyMatch(property -> reader.getObject(property) == null);
}
}
@@ -0,0 +1,77 @@
package fr.xephi.authme.settings.commandconfig;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.configurationdata.CommentsConfiguration;
import ch.jalu.configme.properties.BeanProperty;
import ch.jalu.configme.properties.Property;
/**
* Settings holder class for the commands.yml settings.
*/
public final class CommandSettingsHolder implements SettingsHolder {
public static final Property<CommandConfig> COMMANDS =
new BeanProperty<>(CommandConfig.class, "", new CommandConfig());
private CommandSettingsHolder() {
}
@Override
public void registerComments(CommentsConfiguration conf) {
String[] rootComments = {
"This configuration file allows you to execute commands on various events.",
"Supported placeholders in commands:",
" %p is replaced with the player name.",
" %nick is replaced with the player's nick name",
" %ip is replaced with the player's IP address",
" %country is replaced with the player's country",
"",
"For example, if you want to send a welcome message to a player who just registered:",
"onRegister:",
" welcome:",
" command: 'msg %p Welcome to the server!'",
" executor: CONSOLE",
"",
"This will make the console execute the msg command to the player.",
"Each command under an event has a name you can choose freely (e.g. 'welcome' as above),",
"after which a mandatory 'command' field defines the command to run,",
"and 'executor' defines who will run the command (either PLAYER or CONSOLE). Longer example:",
"onLogin:",
" welcome:",
" command: 'msg %p Welcome back!'",
" executor: PLAYER",
" broadcast:",
" command: 'broadcast %p has joined, welcome back!'",
" executor: CONSOLE",
"",
"You can also add delay to command. It will run after the specified ticks. Example:",
"onLogin:",
" rules:",
" command: 'rules'",
" executor: PLAYER",
" delay: 200",
"",
"Supported command events: onLogin, onSessionLogin, onFirstLogin, onJoin, onLogout, onRegister, "
+ "onUnregister",
"",
"For onLogin and onFirstLogin, you can use 'ifNumberOfAccountsLessThan' and 'ifNumberOfAccountsAtLeast'",
"to specify limits to how many accounts a player can have (matched by IP) for a command to be run:",
"onLogin:",
" warnOnManyAccounts:",
" command: 'say Uh oh! %p has many alt accounts!'",
" executor: CONSOLE",
" ifNumberOfAccountsAtLeast: 5"
};
conf.setComment("", rootComments);
conf.setComment("onFirstLogin",
"Commands to run for players logging in whose 'last login date' was empty");
conf.setComment("onUnregister",
"Commands to run whenever a player is unregistered (by himself, or by an admin)");
conf.setComment("onLogout",
"These commands are called whenever a logged in player uses /logout or quits.",
"The commands are not run if a player that was not logged in quits the server.",
"Note: if your server crashes, these commands won't be run, so don't rely on them to undo",
"'onLogin' commands that would be dangerous for non-logged in players to have!");
}
}
@@ -0,0 +1,14 @@
package fr.xephi.authme.settings.commandconfig;
/**
* The executor of the command.
*/
public enum Executor {
/** The player of the event. */
PLAYER,
/** The console user. */
CONSOLE
}
@@ -0,0 +1,49 @@
package fr.xephi.authme.settings.commandconfig;
import java.util.Optional;
/**
* Configurable command for when a player logs in.
*/
public class OnLoginCommand extends Command {
private Optional<Integer> ifNumberOfAccountsAtLeast = Optional.empty();
private Optional<Integer> ifNumberOfAccountsLessThan = Optional.empty();
/**
* Default constructor (for bean mapping).
*/
public OnLoginCommand() {
}
/**
* Creates a copy of this object, using the given command as new {@link Command#command command}.
*
* @param command the command text to use in the copy
* @return copy of the source with the new command
*/
@Override
public OnLoginCommand copyWithCommand(String command) {
OnLoginCommand copy = new OnLoginCommand();
setValuesToCopyWithNewCommand(copy, command);
copy.ifNumberOfAccountsAtLeast = this.ifNumberOfAccountsAtLeast;
copy.ifNumberOfAccountsLessThan = this.ifNumberOfAccountsLessThan;
return copy;
}
public Optional<Integer> getIfNumberOfAccountsAtLeast() {
return ifNumberOfAccountsAtLeast;
}
public void setIfNumberOfAccountsAtLeast(Optional<Integer> ifNumberOfAccountsAtLeast) {
this.ifNumberOfAccountsAtLeast = ifNumberOfAccountsAtLeast;
}
public Optional<Integer> getIfNumberOfAccountsLessThan() {
return ifNumberOfAccountsLessThan;
}
public void setIfNumberOfAccountsLessThan(Optional<Integer> ifNumberOfAccountsLessThan) {
this.ifNumberOfAccountsLessThan = ifNumberOfAccountsLessThan;
}
}
@@ -0,0 +1,28 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.configurationdata.ConfigurationDataBuilder;
import ch.jalu.configme.properties.Property;
/**
* Utility class responsible for retrieving all {@link Property} fields from {@link SettingsHolder} classes.
*/
public final class AuthMeSettingsRetriever {
private AuthMeSettingsRetriever() {
}
/**
* Builds the configuration data for all property fields in AuthMe {@link SettingsHolder} classes.
*
* @return configuration data
*/
public static ConfigurationData buildConfigurationData() {
return ConfigurationDataBuilder.createConfiguration(
DatabaseSettings.class, PluginSettings.class, RestrictionSettings.class,
EmailSettings.class, HooksSettings.class, ProtectionSettings.class,
PurgeSettings.class, SecuritySettings.class, RegistrationSettings.class,
LimboSettings.class, BackupSettings.class, ConverterSettings.class);
}
}
@@ -0,0 +1,29 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class BackupSettings implements SettingsHolder {
@Comment("General configuration for backups: if false, no backups are possible")
public static final Property<Boolean> ENABLED =
newProperty("BackupSystem.ActivateBackup", false);
@Comment("Create backup at every start of server")
public static final Property<Boolean> ON_SERVER_START =
newProperty("BackupSystem.OnServerStart", false);
@Comment("Create backup at every stop of server")
public static final Property<Boolean> ON_SERVER_STOP =
newProperty("BackupSystem.OnServerStop", true);
@Comment("Windows only: MySQL installation path")
public static final Property<String> MYSQL_WINDOWS_PATH =
newProperty("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
private BackupSettings() {
}
}
@@ -0,0 +1,56 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.configurationdata.CommentsConfiguration;
import ch.jalu.configme.properties.Property;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class ConverterSettings implements SettingsHolder {
@Comment("Rakamak file name")
public static final Property<String> RAKAMAK_FILE_NAME =
newProperty("Converter.Rakamak.fileName", "users.rak");
@Comment("Rakamak use IP?")
public static final Property<Boolean> RAKAMAK_USE_IP =
newProperty("Converter.Rakamak.useIP", false);
@Comment("Rakamak IP file name")
public static final Property<String> RAKAMAK_IP_FILE_NAME =
newProperty("Converter.Rakamak.ipFileName", "UsersIp.rak");
@Comment("CrazyLogin database file name")
public static final Property<String> CRAZYLOGIN_FILE_NAME =
newProperty("Converter.CrazyLogin.fileName", "accounts.db");
@Comment("LoginSecurity: convert from SQLite; if false we use MySQL")
public static final Property<Boolean> LOGINSECURITY_USE_SQLITE =
newProperty("Converter.loginSecurity.useSqlite", true);
@Comment("LoginSecurity MySQL: database host")
public static final Property<String> LOGINSECURITY_MYSQL_HOST =
newProperty("Converter.loginSecurity.mySql.host", "");
@Comment("LoginSecurity MySQL: database name")
public static final Property<String> LOGINSECURITY_MYSQL_DATABASE =
newProperty("Converter.loginSecurity.mySql.database", "");
@Comment("LoginSecurity MySQL: database user")
public static final Property<String> LOGINSECURITY_MYSQL_USER =
newProperty("Converter.loginSecurity.mySql.user", "");
@Comment("LoginSecurity MySQL: password for database user")
public static final Property<String> LOGINSECURITY_MYSQL_PASSWORD =
newProperty("Converter.loginSecurity.mySql.password", "");
private ConverterSettings() {
}
@Override
public void registerComments(CommentsConfiguration conf) {
conf.setComment("Converter",
"Converter settings: see https://github.com/AuthMe/AuthMeReloaded/wiki/Converters");
}
}
@@ -0,0 +1,157 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.datasource.DataSourceType;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class DatabaseSettings implements SettingsHolder {
@Comment({"What type of database do you want to use?",
"Valid values: SQLITE, MARIADB, MYSQL, POSTGRESQL"})
public static final Property<DataSourceType> BACKEND =
newProperty(DataSourceType.class, "DataSource.backend", DataSourceType.SQLITE);
@Comment({"Enable the database caching system, should be disabled on bungeecord environments",
"or when a website integration is being used."})
public static final Property<Boolean> USE_CACHING =
newProperty("DataSource.caching", true);
@Comment("Database host address")
public static final Property<String> MYSQL_HOST =
newProperty("DataSource.mySQLHost", "127.0.0.1");
@Comment("Database port")
public static final Property<String> MYSQL_PORT =
newProperty("DataSource.mySQLPort", "3306");
@Comment("Connect to MySQL database over SSL")
public static final Property<Boolean> MYSQL_USE_SSL =
newProperty("DataSource.mySQLUseSSL", true);
@Comment({"Verification of server's certificate.",
"We would not recommend to set this option to false.",
"Set this option to false at your own risk if and only if you know what you're doing"})
public static final Property<Boolean> MYSQL_CHECK_SERVER_CERTIFICATE =
newProperty( "DataSource.mySQLCheckServerCertificate", true );
@Comment({"Authorize client to retrieve RSA server public key.",
"Advanced option, ignore if you don't know what it means."})
public static final Property<Boolean> MYSQL_ALLOW_PUBLIC_KEY_RETRIEVAL =
newProperty( "DataSource.mySQLAllowPublicKeyRetrieval", true );
@Comment("Username to connect to the MySQL database")
public static final Property<String> MYSQL_USERNAME =
newProperty("DataSource.mySQLUsername", "authme");
@Comment("Password to connect to the MySQL database")
public static final Property<String> MYSQL_PASSWORD =
newProperty("DataSource.mySQLPassword", "12345");
@Comment("Database Name, use with converters or as SQLITE database name")
public static final Property<String> MYSQL_DATABASE =
newProperty("DataSource.mySQLDatabase", "authme");
@Comment("Table of the database")
public static final Property<String> MYSQL_TABLE =
newProperty("DataSource.mySQLTablename", "authme");
@Comment("Column of IDs to sort data")
public static final Property<String> MYSQL_COL_ID =
newProperty("DataSource.mySQLColumnId", "id");
@Comment("Column for storing or checking players nickname")
public static final Property<String> MYSQL_COL_NAME =
newProperty("DataSource.mySQLColumnName", "username");
@Comment("Column for storing or checking players RealName")
public static final Property<String> MYSQL_COL_REALNAME =
newProperty("DataSource.mySQLRealName", "realname");
@Comment("Column for storing players passwords")
public static final Property<String> MYSQL_COL_PASSWORD =
newProperty("DataSource.mySQLColumnPassword", "password");
@Comment("Column for storing players passwords salts")
public static final Property<String> MYSQL_COL_SALT =
newProperty("DataSource.mySQLColumnSalt", "");
@Comment("Column for storing players emails")
public static final Property<String> MYSQL_COL_EMAIL =
newProperty("DataSource.mySQLColumnEmail", "email");
@Comment("Column for storing if a player is logged in or not")
public static final Property<String> MYSQL_COL_ISLOGGED =
newProperty("DataSource.mySQLColumnLogged", "isLogged");
@Comment("Column for storing if a player has a valid session or not")
public static final Property<String> MYSQL_COL_HASSESSION =
newProperty("DataSource.mySQLColumnHasSession", "hasSession");
@Comment("Column for storing a player's TOTP key (for two-factor authentication)")
public static final Property<String> MYSQL_COL_TOTP_KEY =
newProperty("DataSource.mySQLtotpKey", "totp");
@Comment("Column for storing the player's last IP")
public static final Property<String> MYSQL_COL_LAST_IP =
newProperty("DataSource.mySQLColumnIp", "ip");
@Comment("Column for storing players lastlogins")
public static final Property<String> MYSQL_COL_LASTLOGIN =
newProperty("DataSource.mySQLColumnLastLogin", "lastlogin");
@Comment("Column storing the registration date")
public static final Property<String> MYSQL_COL_REGISTER_DATE =
newProperty("DataSource.mySQLColumnRegisterDate", "regdate");
@Comment("Column for storing the IP address at the time of registration")
public static final Property<String> MYSQL_COL_REGISTER_IP =
newProperty("DataSource.mySQLColumnRegisterIp", "regip");
@Comment("Column for storing player LastLocation - X")
public static final Property<String> MYSQL_COL_LASTLOC_X =
newProperty("DataSource.mySQLlastlocX", "x");
@Comment("Column for storing player LastLocation - Y")
public static final Property<String> MYSQL_COL_LASTLOC_Y =
newProperty("DataSource.mySQLlastlocY", "y");
@Comment("Column for storing player LastLocation - Z")
public static final Property<String> MYSQL_COL_LASTLOC_Z =
newProperty("DataSource.mySQLlastlocZ", "z");
@Comment("Column for storing player LastLocation - World Name")
public static final Property<String> MYSQL_COL_LASTLOC_WORLD =
newProperty("DataSource.mySQLlastlocWorld", "world");
@Comment("Column for storing player LastLocation - Yaw")
public static final Property<String> MYSQL_COL_LASTLOC_YAW =
newProperty("DataSource.mySQLlastlocYaw", "yaw");
@Comment("Column for storing player LastLocation - Pitch")
public static final Property<String> MYSQL_COL_LASTLOC_PITCH =
newProperty("DataSource.mySQLlastlocPitch", "pitch");
@Comment("Column for storing players uuids (optional)")
public static final Property<String> MYSQL_COL_PLAYER_UUID =
newProperty( "DataSource.mySQLPlayerUUID", "" );
@Comment("Column for storing players groups")
public static final Property<String> MYSQL_COL_GROUP =
newProperty("ExternalBoardOptions.mySQLColumnGroup", "");
@Comment("Overrides the size of the DB Connection Pool, default = 10")
public static final Property<Integer> MYSQL_POOL_SIZE =
newProperty("DataSource.poolSize", 10);
@Comment({"The maximum lifetime of a connection in the pool, default = 1800 seconds",
"You should set this at least 30 seconds less than mysql server wait_timeout"})
public static final Property<Integer> MYSQL_CONNECTION_MAX_LIFETIME =
newProperty("DataSource.maxLifetime", 1800);
private DatabaseSettings() {
}
}
@@ -0,0 +1,76 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class EmailSettings implements SettingsHolder {
@Comment("Email SMTP server host")
public static final Property<String> SMTP_HOST =
newProperty("Email.mailSMTP", "smtp.163.com");
@Comment("Email SMTP server port")
public static final Property<Integer> SMTP_PORT =
newProperty("Email.mailPort", 465);
@Comment("Only affects port 25: enable TLS/STARTTLS?")
public static final Property<Boolean> PORT25_USE_TLS =
newProperty("Email.useTls", true);
@Comment("Email account which sends the mails")
public static final Property<String> MAIL_ACCOUNT =
newProperty("Email.mailAccount", "");
@Comment("Email account password")
public static final Property<String> MAIL_PASSWORD =
newProperty("Email.mailPassword", "");
@Comment("Email address, fill when mailAccount is not the email address of the account")
public static final Property<String> MAIL_ADDRESS =
newProperty("Email.mailAddress", "");
@Comment("Custom sender name, replacing the mailAccount name in the email")
public static final Property<String> MAIL_SENDER_NAME =
newProperty("Email.mailSenderName", "");
@Comment("Recovery password length")
public static final Property<Integer> RECOVERY_PASSWORD_LENGTH =
newProperty("Email.RecoveryPasswordLength", 12);
@Comment("Mail Subject")
public static final Property<String> RECOVERY_MAIL_SUBJECT =
newProperty("Email.mailSubject", "Your new AuthMe password");
@Comment("Like maxRegPerIP but with email")
public static final Property<Integer> MAX_REG_PER_EMAIL =
newProperty("Email.maxRegPerEmail", 1);
@Comment("Recall players to add an email?")
public static final Property<Boolean> RECALL_PLAYERS =
newProperty("Email.recallPlayers", false);
@Comment("Delay in minute for the recall scheduler")
public static final Property<Integer> DELAY_RECALL =
newProperty("Email.delayRecall", 5);
@Comment("Send the new password drawn in an image?")
public static final Property<Boolean> PASSWORD_AS_IMAGE =
newProperty("Email.generateImage", false);
@Comment("The OAuth2 token")
public static final Property<String> OAUTH2_TOKEN =
newProperty("Email.emailOauth2Token", "");
@Comment("Email notifications when the server shuts down")
public static final Property<Boolean> SHUTDOWN_MAIL =
newProperty("Email.shutDownEmail", false);
@Comment("Email notification address when the server is shut down")
public static final Property<String> SHUTDOWN_MAIL_ADDRESS =
newProperty("Email.shutDownEmailAddress", "your@mail.com");
private EmailSettings() {
}
}
@@ -0,0 +1,86 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import java.util.List;
import static ch.jalu.configme.properties.PropertyInitializer.newListProperty;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class HooksSettings implements SettingsHolder {
@Comment("Do we need to hook with multiverse for spawn checking?")
public static final Property<Boolean> MULTIVERSE =
newProperty("Hooks.multiverse", true);
@Comment("Do we need to hook with BungeeCord?")
public static final Property<Boolean> BUNGEECORD =
newProperty("Hooks.bungeecord", false);
@Comment("Allow FloodGatePlayer Join Without checkIsValidName()")
public static final Property<Boolean> HOOK_FLOODGATE_PLAYER =
newProperty("Hooks.floodgate", false);
@Comment("Send player to this BungeeCord server after register/login")
public static final Property<String> BUNGEECORD_SERVER =
newProperty("Hooks.sendPlayerTo", "");
@Comment("Do we need to disable Essentials SocialSpy on join?")
public static final Property<Boolean> DISABLE_SOCIAL_SPY =
newProperty("Hooks.disableSocialSpy", false);
@Comment("Do we need to force /motd Essentials command on join?")
public static final Property<Boolean> USE_ESSENTIALS_MOTD =
newProperty("Hooks.useEssentialsMotd", false);
@Comment({
"-1 means disabled. If you want that only activated players",
"can log into your server, you can set here the group number",
"of unactivated users, needed for some forum/CMS support"})
public static final Property<Integer> NON_ACTIVATED_USERS_GROUP =
newProperty("ExternalBoardOptions.nonActivedUserGroup", -1);
@Comment("Other MySQL columns where we need to put the username (case-sensitive)")
public static final Property<List<String>> MYSQL_OTHER_USERNAME_COLS =
newListProperty("ExternalBoardOptions.mySQLOtherUsernameColumns");
@Comment("How much log2 rounds needed in BCrypt (do not change if you do not know what it does)")
public static final Property<Integer> BCRYPT_LOG2_ROUND =
newProperty("ExternalBoardOptions.bCryptLog2Round", 12);
@Comment("phpBB table prefix defined during the phpBB installation process")
public static final Property<String> PHPBB_TABLE_PREFIX =
newProperty("ExternalBoardOptions.phpbbTablePrefix", "phpbb_");
@Comment("phpBB activated group ID; 2 is the default registered group defined by phpBB")
public static final Property<Integer> PHPBB_ACTIVATED_GROUP_ID =
newProperty("ExternalBoardOptions.phpbbActivatedGroupId", 2);
@Comment("IP Board table prefix defined during the IP Board installation process")
public static final Property<String> IPB_TABLE_PREFIX =
newProperty("ExternalBoardOptions.IPBTablePrefix", "ipb_");
@Comment("IP Board default group ID; 3 is the default registered group defined by IP Board")
public static final Property<Integer> IPB_ACTIVATED_GROUP_ID =
newProperty("ExternalBoardOptions.IPBActivatedGroupId", 3);
@Comment("Xenforo table prefix defined during the Xenforo installation process")
public static final Property<String> XF_TABLE_PREFIX =
newProperty("ExternalBoardOptions.XFTablePrefix", "xf_");
@Comment("XenForo default group ID; 2 is the default registered group defined by Xenforo")
public static final Property<Integer> XF_ACTIVATED_GROUP_ID =
newProperty("ExternalBoardOptions.XFActivatedGroupId", 2);
@Comment("Wordpress prefix defined during WordPress installation")
public static final Property<String> WORDPRESS_TABLE_PREFIX =
newProperty("ExternalBoardOptions.wordpressTablePrefix", "wp_");
private HooksSettings() {
}
}
@@ -0,0 +1,83 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.configurationdata.CommentsConfiguration;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.data.limbo.AllowFlightRestoreType;
import fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType;
import fr.xephi.authme.data.limbo.persistence.LimboPersistenceType;
import fr.xephi.authme.data.limbo.persistence.SegmentSize;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
/**
* Settings for the LimboPlayer feature.
*/
public final class LimboSettings implements SettingsHolder {
@Comment({
"Besides storing the data in memory, you can define if/how the data should be persisted",
"on disk. This is useful in case of a server crash, so next time the server starts we can",
"properly restore things like OP status, ability to fly, and walk/fly speed.",
"DISABLED: no disk storage,",
"INDIVIDUAL_FILES: each player data in its own file,",
"DISTRIBUTED_FILES: distributes players into different files based on their UUID, see below"
})
public static final Property<LimboPersistenceType> LIMBO_PERSISTENCE_TYPE =
newProperty(LimboPersistenceType.class, "limbo.persistence.type", LimboPersistenceType.INDIVIDUAL_FILES);
@Comment({
"This setting only affects DISTRIBUTED_FILES persistence. The distributed file",
"persistence attempts to reduce the number of files by distributing players into various",
"buckets based on their UUID. This setting defines into how many files the players should",
"be distributed. Possible values: ONE, FOUR, EIGHT, SIXTEEN, THIRTY_TWO, SIXTY_FOUR,",
"ONE_TWENTY for 128, TWO_FIFTY for 256.",
"For example, if you expect 100 non-logged in players, setting to SIXTEEN will average",
"6.25 players per file (100 / 16).",
"Note: if you change this setting all data will be migrated. If you have a lot of data,",
"change this setting only on server restart, not with /authme reload."
})
public static final Property<SegmentSize> DISTRIBUTION_SIZE =
newProperty(SegmentSize.class, "limbo.persistence.distributionSize", SegmentSize.SIXTEEN);
@Comment({
"Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE, NOTHING.",
"RESTORE sets back the old property from the player. NOTHING will prevent AuthMe",
"from modifying the 'allow flight' property on the player."
})
public static final Property<AllowFlightRestoreType> RESTORE_ALLOW_FLIGHT =
newProperty(AllowFlightRestoreType.class, "limbo.restoreAllowFlight", AllowFlightRestoreType.RESTORE);
@Comment({
"Restore fly speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.",
"RESTORE: restore the speed the player had;",
"DEFAULT: always set to default speed;",
"MAX_RESTORE: take the maximum of the player's current speed and the previous one",
"RESTORE_NO_ZERO: Like 'restore' but sets speed to default if the player's speed was 0"
})
public static final Property<WalkFlySpeedRestoreType> RESTORE_FLY_SPEED =
newProperty(WalkFlySpeedRestoreType.class, "limbo.restoreFlySpeed", WalkFlySpeedRestoreType.RESTORE_NO_ZERO);
@Comment({
"Restore walk speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.",
"See above for a description of the values."
})
public static final Property<WalkFlySpeedRestoreType> RESTORE_WALK_SPEED =
newProperty(WalkFlySpeedRestoreType.class, "limbo.restoreWalkSpeed", WalkFlySpeedRestoreType.RESTORE_NO_ZERO);
private LimboSettings() {
}
@Override
public void registerComments(CommentsConfiguration conf) {
String[] limboExplanation = {
"Before a user logs in, various properties are temporarily removed from the player,",
"such as OP status, ability to fly, and walk/fly speed.",
"Once the user is logged in, we add back the properties we previously saved.",
"In this section, you may define how these properties should be handled.",
"Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Limbo-players"
};
conf.setComment("limbo", limboExplanation);
}
}
@@ -0,0 +1,91 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.output.LogLevel;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class PluginSettings implements SettingsHolder {
@Comment({
"Do you want to enable the session feature?",
"If enabled, when a player authenticates successfully,",
"his IP and his nickname is saved.",
"The next time the player joins the server, if his IP",
"is the same as last time and the timeout hasn't",
"expired, he will not need to authenticate."
})
public static final Property<Boolean> SESSIONS_ENABLED =
newProperty("settings.sessions.enabled", true);
@Comment({
"After how many minutes should a session expire?",
"A player's session ends after the timeout or if his IP has changed"
})
public static final Property<Integer> SESSIONS_TIMEOUT =
newProperty("settings.sessions.timeout", 43200);
@Comment({
"Message language, available languages:",
"https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md"
})
public static final Property<String> MESSAGES_LANGUAGE =
newProperty("settings.messagesLanguage", "zhcn");
@Comment({
"Enables switching a player to defined permission groups before they log in.",
"See below for a detailed explanation."
})
public static final Property<Boolean> ENABLE_PERMISSION_CHECK =
newProperty("GroupOptions.enablePermissionCheck", false);
@Comment({
"This is a very important option: if a registered player joins the server",
"AuthMe will switch him to unLoggedInGroup. This should prevent all major exploits.",
"You can set up your permission plugin with this special group to have no permissions,",
"or only permission to chat (or permission to send private messages etc.).",
"The better way is to set up this group with few permissions, so if a player",
"tries to exploit an account they can do only what you've defined for the group.",
"After login, the player will be moved to his correct permissions group!",
"Please note that the group name is case-sensitive, so 'admin' is different from 'Admin'",
"Otherwise your group will be wiped and the player will join in the default group []!",
"Example: registeredPlayerGroup: 'NotLogged'"
})
public static final Property<String> REGISTERED_GROUP =
newProperty("GroupOptions.registeredPlayerGroup", "");
@Comment({
"Similar to above, unregistered players can be set to the following",
"permissions group"
})
public static final Property<String> UNREGISTERED_GROUP =
newProperty("GroupOptions.unregisteredPlayerGroup", "");
@Comment("Forces authme to hook into Vault instead of a specific permission handler system.")
public static final Property<Boolean> FORCE_VAULT_HOOK =
newProperty("settings.forceVaultHook", false);
@Comment({
"Log level: INFO, FINE, DEBUG. Use INFO for general messages,",
"FINE for some additional detailed ones (like password failed),",
"and DEBUG for debugging"
})
public static final Property<LogLevel> LOG_LEVEL =
newProperty(LogLevel.class, "settings.logLevel", LogLevel.FINE);
@Comment({
"By default we schedule async tasks when talking to the database. If you want",
"typical communication with the database to happen synchronously, set this to false"
})
public static final Property<Boolean> USE_ASYNC_TASKS =
newProperty("settings.useAsyncTasks", true);
@Comment("The name of the server, used in some placeholders.")
public static final Property<String> SERVER_NAME = newProperty("settings.serverName", "Your Minecraft Server");
private PluginSettings() {
}
}
@@ -0,0 +1,66 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import java.util.List;
import static ch.jalu.configme.properties.PropertyInitializer.newListProperty;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class ProtectionSettings implements SettingsHolder {
@Comment("Enable some servers protection (country based login, antibot)")
public static final Property<Boolean> ENABLE_PROTECTION =
newProperty("Protection.enableProtection", true);
@Comment("Apply the protection also to registered usernames")
public static final Property<Boolean> ENABLE_PROTECTION_REGISTERED =
newProperty("Protection.enableProtectionRegistered", true);
@Comment({
"Countries allowed to join the server and register. For country codes, see",
"https://dev.maxmind.com/geoip/legacy/codes/iso3166/",
"Use \"LOCALHOST\" for local addresses.",
"PLEASE USE QUOTES!"})
public static final Property<List<String>> COUNTRIES_WHITELIST =
newListProperty("Protection.countries", "CN", "LOCALHOST");
@Comment({
"Countries not allowed to join the server and register",
"PLEASE USE QUOTES!"})
public static final Property<List<String>> COUNTRIES_BLACKLIST =
newListProperty("Protection.countriesBlacklist", "A1");
@Comment("Do we need to enable automatic antibot system?")
public static final Property<Boolean> ENABLE_ANTIBOT =
newProperty("Protection.enableAntiBot", true);
@Comment("The interval in seconds")
public static final Property<Integer> ANTIBOT_INTERVAL =
newProperty("Protection.antiBotInterval", 5);
@Comment({
"Max number of players allowed to login in the interval",
"before the AntiBot system is enabled automatically"})
public static final Property<Integer> ANTIBOT_SENSIBILITY =
newProperty("Protection.antiBotSensibility", 10);
@Comment("Duration in minutes of the antibot automatic system")
public static final Property<Integer> ANTIBOT_DURATION =
newProperty("Protection.antiBotDuration", 10);
@Comment("Delay in seconds before the antibot activation")
public static final Property<Integer> ANTIBOT_DELAY =
newProperty("Protection.antiBotDelay", 60);
@Comment("Kicks the player that issued a command before the defined time after the join process")
public static final Property<Integer> QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS =
newProperty("Protection.quickCommands.denyCommandsBeforeMilliseconds", 3000);
private ProtectionSettings() {
}
}
@@ -0,0 +1,46 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class PurgeSettings implements SettingsHolder {
@Comment("If enabled, AuthMe automatically purges old, unused accounts")
public static final Property<Boolean> USE_AUTO_PURGE =
newProperty("Purge.useAutoPurge", false);
@Comment("Number of days after which an account should be purged")
public static final Property<Integer> DAYS_BEFORE_REMOVE_PLAYER =
newProperty("Purge.daysBeforeRemovePlayer", 60);
@Comment("Do we need to remove the player.dat file during purge process?")
public static final Property<Boolean> REMOVE_PLAYER_DAT =
newProperty("Purge.removePlayerDat", false);
@Comment("Do we need to remove the Essentials/userdata/player.yml file during purge process?")
public static final Property<Boolean> REMOVE_ESSENTIALS_FILES =
newProperty("Purge.removeEssentialsFile", false);
@Comment("World in which the players.dat are stored")
public static final Property<String> DEFAULT_WORLD =
newProperty("Purge.defaultWorld", "world");
@Comment("Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge?")
public static final Property<Boolean> REMOVE_LIMITED_CREATIVE_INVENTORIES =
newProperty("Purge.removeLimitedCreativesInventories", false);
@Comment("Do we need to remove the AntiXRayData/PlayerData/player file during purge process?")
public static final Property<Boolean> REMOVE_ANTI_XRAY_FILE =
newProperty("Purge.removeAntiXRayFile", false);
@Comment("Do we need to remove permissions?")
public static final Property<Boolean> REMOVE_PERMISSIONS =
newProperty("Purge.removePermissions", false);
private PurgeSettings() {
}
}
@@ -0,0 +1,98 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.process.register.RegisterSecondaryArgument;
import fr.xephi.authme.process.register.RegistrationType;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class RegistrationSettings implements SettingsHolder {
@Comment("Enable registration on the server?")
public static final Property<Boolean> IS_ENABLED =
newProperty("settings.registration.enabled", true);
@Comment({
"Send every X seconds a message to a player to",
"remind him that he has to login/register"})
public static final Property<Integer> MESSAGE_INTERVAL =
newProperty("settings.registration.messageInterval", 5);
@Comment({
"Only registered and logged in players can play.",
"See restrictions for exceptions"})
public static final Property<Boolean> FORCE =
newProperty("settings.registration.force", true);
@Comment({
"Type of registration: PASSWORD or EMAIL",
"PASSWORD = account is registered with a password supplied by the user;",
"EMAIL = password is generated and sent to the email provided by the user.",
"More info at https://github.com/AuthMe/AuthMeReloaded/wiki/Registration"
})
public static final Property<RegistrationType> REGISTRATION_TYPE =
newProperty(RegistrationType.class, "settings.registration.type", RegistrationType.PASSWORD);
@Comment({
"Second argument the /register command should take: ",
"NONE = no 2nd argument",
"CONFIRMATION = must repeat first argument (pass or email)",
"EMAIL_OPTIONAL = for password register: 2nd argument can be empty or have email address",
"EMAIL_MANDATORY = for password register: 2nd argument MUST be an email address"
})
public static final Property<RegisterSecondaryArgument> REGISTER_SECOND_ARGUMENT =
newProperty(RegisterSecondaryArgument.class, "settings.registration.secondArg",
RegisterSecondaryArgument.CONFIRMATION);
@Comment({
"Do we force kick a player after a successful registration?",
"Do not use with login feature below"})
public static final Property<Boolean> FORCE_KICK_AFTER_REGISTER =
newProperty("settings.registration.forceKickAfterRegister", false);
@Comment("Does AuthMe need to enforce a /login after a successful registration?")
public static final Property<Boolean> FORCE_LOGIN_AFTER_REGISTER =
newProperty("settings.registration.forceLoginAfterRegister", false);
@Comment("Should we delay the join message and display it once the player has logged in?")
public static final Property<Boolean> DELAY_JOIN_MESSAGE =
newProperty("settings.delayJoinMessage", true);
@Comment({
"The custom join message that will be sent after a successful login,",
"keep empty to use the original one.",
"Available variables:",
"{PLAYERNAME}: the player name (no colors)",
"{DISPLAYNAME}: the player display name (with colors)",
"{DISPLAYNAMENOCOLOR}: the player display name (without colors)"})
public static final Property<String> CUSTOM_JOIN_MESSAGE =
newProperty("settings.customJoinMessage", "");
@Comment("Should we remove the leave messages of unlogged users?")
public static final Property<Boolean> REMOVE_UNLOGGED_LEAVE_MESSAGE =
newProperty("settings.removeUnloggedLeaveMessage", true);
@Comment("Should we remove join messages altogether?")
public static final Property<Boolean> REMOVE_JOIN_MESSAGE =
newProperty("settings.removeJoinMessage", true);
@Comment("Should we remove leave messages altogether?")
public static final Property<Boolean> REMOVE_LEAVE_MESSAGE =
newProperty("settings.removeLeaveMessage", true);
@Comment("Do we need to add potion effect Blinding before login/register?")
public static final Property<Boolean> APPLY_BLIND_EFFECT =
newProperty("settings.applyBlindEffect", false);
@Comment({
"Do we need to prevent people to login with another case?",
"If Xephi is registered, then Xephi can login, but not XEPHI/xephi/XePhI"})
public static final Property<Boolean> PREVENT_OTHER_CASE =
newProperty("settings.preventOtherCase", true);
private RegistrationSettings() {
}
}
@@ -0,0 +1,215 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.Property;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static ch.jalu.configme.properties.PropertyInitializer.newListProperty;
import static ch.jalu.configme.properties.PropertyInitializer.newLowercaseStringSetProperty;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class RestrictionSettings implements SettingsHolder {
@Comment({
"Can not authenticated players chat?",
"Keep in mind that this feature also blocks all commands not",
"listed in the list below."})
public static final Property<Boolean> ALLOW_CHAT =
newProperty("settings.restrictions.allowChat", false);
@Comment("Hide the chat log from players who are not authenticated?")
public static final Property<Boolean> HIDE_CHAT =
newProperty("settings.restrictions.hideChat", false);
@Comment("Allowed commands for unauthenticated players")
public static final Property<Set<String>> ALLOW_COMMANDS =
newLowercaseStringSetProperty("settings.restrictions.allowCommands",
"/login", "/log", "/l", "/register", "/reg", "/email", "/captcha", "/2fa", "/totp");
@Comment({
"Max number of allowed registrations per IP",
"The value 0 means an unlimited number of registrations!"})
public static final Property<Integer> MAX_REGISTRATION_PER_IP =
newProperty("settings.restrictions.maxRegPerIp", 3);
@Comment("Minimum allowed username length")
public static final Property<Integer> MIN_NICKNAME_LENGTH =
newProperty("settings.restrictions.minNicknameLength", 3);
@Comment("Maximum allowed username length")
public static final Property<Integer> MAX_NICKNAME_LENGTH =
newProperty("settings.restrictions.maxNicknameLength", 16);
@Comment({
"When this setting is enabled, online players can't be kicked out",
"due to \"Logged in from another Location\"",
"This setting will prevent potential security exploits."})
public static final Property<Boolean> FORCE_SINGLE_SESSION =
newProperty("settings.restrictions.ForceSingleSession", true);
@Comment({
"If enabled, every player that spawn in one of the world listed in",
"\"ForceSpawnLocOnJoin.worlds\" will be teleported to the spawnpoint after successful",
"authentication. The quit location of the player will be overwritten.",
"This is different from \"teleportUnAuthedToSpawn\" that teleport player",
"to the spawnpoint on join."})
public static final Property<Boolean> FORCE_SPAWN_LOCATION_AFTER_LOGIN =
newProperty("settings.restrictions.ForceSpawnLocOnJoin.enabled", false);
@Comment({
"WorldNames where we need to force the spawn location",
"Case-sensitive!"})
public static final Property<List<String>> FORCE_SPAWN_ON_WORLDS =
newListProperty("settings.restrictions.ForceSpawnLocOnJoin.worlds",
"world", "world_nether", "world_the_end");
@Comment("This option will save the quit location of the players.")
public static final Property<Boolean> SAVE_QUIT_LOCATION =
newProperty("settings.restrictions.SaveQuitLocation", false);
@Comment({
"To activate the restricted user feature you need",
"to enable this option and configure the AllowedRestrictedUser field."})
public static final Property<Boolean> ENABLE_RESTRICTED_USERS =
newProperty("settings.restrictions.AllowRestrictedUser", true);
@Comment({
"The restricted user feature will kick players listed below",
"if they don't match the defined IP address. Names are case-insensitive.",
"You can use * as wildcard (127.0.0.*), or regex with a \"regex:\" prefix regex:127\\.0\\.0\\..*",
"Example:",
" AllowedRestrictedUser:",
" - playername;127.0.0.1",
" - playername;regex:127\\.0\\.0\\..*"})
public static final Property<Set<String>> RESTRICTED_USERS =
newLowercaseStringSetProperty("settings.restrictions.AllowedRestrictedUser",
"server_land;127.0.0.1","server;127.0.0.1","bukkit;127.0.0.1","purpur;127.0.0.1",
"system;127.0.0.1","admin;127.0.0.1","md_5;127.0.0.1","administrator;127.0.0.1","notch;127.0.0.1",
"spigot;127.0.0.1","bukkit;127.0.0.1","bukkitcraft;127.0.0.1","paperclip;127.0.0.1","papermc;127.0.0.1",
"spigotmc;127.0.0.1","root;127.0.0.1","console;127.0.0.1","purpur;127.0.0.1","authme;127.0.0.1",
"owner;127.0.0.1");
@Comment("Ban unknown IPs trying to log in with a restricted username?")
public static final Property<Boolean> BAN_UNKNOWN_IP =
newProperty("settings.restrictions.banUnsafedIP", false);
@Comment("Should unregistered players be kicked immediately?")
public static final Property<Boolean> KICK_NON_REGISTERED =
newProperty("settings.restrictions.kickNonRegistered", false);
@Comment("Should players be kicked on wrong password?")
public static final Property<Boolean> KICK_ON_WRONG_PASSWORD =
newProperty("settings.restrictions.kickOnWrongPassword", false);
@Comment({
"Should not logged in players be teleported to the spawn?",
"After the authentication they will be teleported back to",
"their normal position."})
public static final Property<Boolean> TELEPORT_UNAUTHED_TO_SPAWN =
newProperty("settings.restrictions.teleportUnAuthedToSpawn", false);
@Comment("Can unregistered players walk around?")
public static final Property<Boolean> ALLOW_UNAUTHED_MOVEMENT =
newProperty("settings.restrictions.allowMovement", false);
@Comment({
"After how many seconds should players who fail to login or register",
"be kicked? Set to 0 to disable."})
public static final Property<Integer> TIMEOUT =
newProperty("settings.restrictions.timeout", 120);
@Comment("Regex pattern of allowed characters in the player name.")
public static final Property<String> ALLOWED_NICKNAME_CHARACTERS =
newProperty("settings.restrictions.allowedNicknameCharacters", "[a-zA-Z0-9_]*");
@Comment({
"How far can unregistered players walk?",
"Set to 0 for unlimited radius"
})
public static final Property<Integer> ALLOWED_MOVEMENT_RADIUS =
newProperty("settings.restrictions.allowedMovementRadius", 0);
@Comment("Should we protect the player inventory before logging in? Requires ProtocolLib.")
public static final Property<Boolean> PROTECT_INVENTORY_BEFORE_LOGIN =
newProperty("settings.restrictions.ProtectInventoryBeforeLogIn", false);
@Comment("Should we deny the tabcomplete feature before logging in? Requires ProtocolLib.")
public static final Property<Boolean> DENY_TABCOMPLETE_BEFORE_LOGIN =
newProperty("settings.restrictions.DenyTabCompleteBeforeLogin", false);
@Comment({
"Should we display all other accounts from a player when he joins?",
"permission: /authme.admin.accounts"})
public static final Property<Boolean> DISPLAY_OTHER_ACCOUNTS =
newProperty("settings.restrictions.displayOtherAccounts", false);
@Comment("Spawn priority; values: authme, essentials, cmi, multiverse, default")
public static final Property<String> SPAWN_PRIORITY =
newProperty("settings.restrictions.spawnPriority", "authme,essentials,cmi,multiverse,default");
@Comment("Maximum Login authorized by IP")
public static final Property<Integer> MAX_LOGIN_PER_IP =
newProperty("settings.restrictions.maxLoginPerIp", 3);
@Comment("Maximum Join authorized by IP")
public static final Property<Integer> MAX_JOIN_PER_IP =
newProperty("settings.restrictions.maxJoinPerIp", 3);
@Comment("AuthMe will NEVER teleport players if set to true!")
public static final Property<Boolean> NO_TELEPORT =
newProperty("settings.restrictions.noTeleport", false);
@Comment({
"Regex syntax for allowed chars in passwords. The default [!-~] allows all visible ASCII",
"characters, which is what we recommend. See also http://asciitable.com",
"You can test your regex with https://regex101.com"
})
public static final Property<String> ALLOWED_PASSWORD_REGEX =
newProperty("settings.restrictions.allowedPasswordCharacters", "[!-~]*");
@Comment("Regex syntax for allowed chars in email.")
public static final Property<String> ALLOWED_EMAIL_REGEX =
newProperty("settings.restrictions.allowedEmailCharacters", "^[A-Za-z0-9_]{4,15}@(qq|outlook|163|gmail|icloud)\\.com$");
@Comment("Force survival gamemode when player joins?")
public static final Property<Boolean> FORCE_SURVIVAL_MODE =
newProperty("settings.GameMode.ForceSurvivalMode", false);
@Comment({
"Below you can list all account names that AuthMe will ignore",
"for registration or login. Configure it at your own risk!!",
"This option adds compatibility with BuildCraft and some other mods.",
"It is case-insensitive! Example:",
"UnrestrictedName:",
"- 'npcPlayer'",
"- 'npcPlayer2'"
})
public static final Property<Set<String>> UNRESTRICTED_NAMES =
newLowercaseStringSetProperty("settings.unrestrictions.UnrestrictedName");
@Comment({
"Below you can list all inventories names that AuthMe will ignore",
"for registration or login. Configure it at your own risk!!",
"This option adds compatibility with some mods.",
"It is case-insensitive! Example:",
"UnrestrictedInventories:",
"- 'myCustomInventory1'",
"- 'myCustomInventory2'"
})
public static final Property<Set<String>> UNRESTRICTED_INVENTORIES =
newLowercaseStringSetProperty("settings.unrestrictions.UnrestrictedInventories");
private RestrictionSettings() {
}
}
@@ -0,0 +1,204 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.Comment;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.properties.BooleanProperty;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.EnumSetProperty;
import java.util.Set;
import fr.xephi.authme.listener.PlayerQuitListener;
import static ch.jalu.configme.properties.PropertyInitializer.newLowercaseStringSetProperty;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class SecuritySettings implements SettingsHolder {
@Comment({"Stop the server if we can't contact the sql database",
"Take care with this, if you set this to false,",
"AuthMe will automatically disable and the server won't be protected!"})
public static final Property<Boolean> STOP_SERVER_ON_PROBLEM =
newProperty("Security.SQLProblem.stopServer", false);
@Comment("Enable the new feature to prevent ghost players?")
public static final Property<Boolean> ANTI_GHOST_PLAYERS = newProperty("3rdPartyFeature.fixes.antiGhostPlayer", false);
@Comment({"Choose the best teleport method by server brand?",
"(Enable this if you are using Paper)"})
public static final Property<Boolean> SMART_ASYNC_TELEPORT = newProperty("3rdPartyFeature.optimizes.smartAsyncTeleport",false);
@Comment("Send a GUI captcha to unregistered players?(Requires ProtocolLib)")
public static final Property<Boolean> GUI_CAPTCHA = newProperty("3rdPartyFeature.captcha.guiCaptcha",false);
@Comment({"Should we kick the players when they don't finish the GUI captcha in seconds?",
"(less than or equals 0 is disabled)"})
public static final Property<Integer> GUI_CAPTCHA_TIMEOUT = newProperty("3rdPartyFeature.captcha.timeOut",0);
@Comment({"Should we ignore floodgate players when sending GUI captcha?",
"(require hookFloodgate enabled)"})
public static final Property<Boolean> GUI_CAPTCHA_BE_COMPATIBILITY = newProperty("3rdPartyFeature.captcha.ignoreBedrock",false);
// @Comment({"Should we kick the players when they failed captcha too many times?",
// "(Set this to 0 to disable)(Default: 4)"})
// public static final Property<Integer> GUI_CAPTCHA_MAX_TRY = newProperty("3rdPartyFeature.captcha.maxTryTimes",4);
// @Comment({"Kick the players when they didn't finish the gui captcha in time? " ,
// "(0 is disabled)"})
// public static final Property<Integer> CAPTCHA_TIMEOUT = newProperty("3rdPartyFeature.captcha.timeout",0);
//@Comment({"Using which API to get hash data?",
//"Available options: github, gitee, ghproxy (if your server is in China, please use gitee or ghproxy.)"})
//public static final Property<String> SHA_CHECK_METHOD = newProperty("Plugin.hashing.hashApi","github");
//@Comment("Should we use the local cache sometimes instead of requesting API?")
//public static final Property<Boolean> USE_LOCAL_CACHE = newProperty("Plugin.hashing.useLocalCache",false);
//@Comment("DON'T TOUCH!!!")
//public static final Property<String> SHA_CHECK_CACHE = newProperty("Plugin.hashing.hashCached","");
@Comment("Copy AuthMe log output in a separate file as well?")
public static final Property<Boolean> USE_LOGGING =
newProperty("Security.console.logConsole", true);
@Comment({"Query haveibeenpwned.com with a hashed version of the password.",
"This is used to check whether it is safe."})
public static final Property<Boolean> HAVE_I_BEEN_PWNED_CHECK =
newProperty("Security.account.haveIBeenPwned.check", false);
@Comment({"If the password is used more than this number of times, it is considered unsafe."})
public static final Property<Integer> HAVE_I_BEEN_PWNED_LIMIT =
newProperty("Security.account.haveIBeenPwned.limit", 0);
@Comment("Enable captcha when a player uses wrong password too many times")
public static final Property<Boolean> ENABLE_LOGIN_FAILURE_CAPTCHA =
newProperty("Security.captcha.useCaptcha", false);
@Comment("Check for updates on enabled from GitHub?")
public static final Property<Boolean> CHECK_FOR_UPDATES =
newProperty("Plugin.updates.checkForUpdates", true);
@Comment("Max allowed tries before a captcha is required")
public static final Property<Integer> MAX_LOGIN_TRIES_BEFORE_CAPTCHA =
newProperty("Security.captcha.maxLoginTry", 8);
@Comment("Captcha length")
public static final Property<Integer> CAPTCHA_LENGTH =
newProperty("Security.captcha.captchaLength", 6);
@Comment("Minutes after which login attempts count is reset for a player")
public static final Property<Integer> CAPTCHA_COUNT_MINUTES_BEFORE_RESET =
newProperty("Security.captcha.captchaCountReset", 120);
@Comment("Require captcha before a player may register?")
public static final Property<Boolean> ENABLE_CAPTCHA_FOR_REGISTRATION =
newProperty("Security.captcha.requireForRegistration", false);
@Comment("Minimum length of password")
public static final Property<Integer> MIN_PASSWORD_LENGTH =
newProperty("settings.security.minPasswordLength", 8);
@Comment("Maximum length of password")
public static final Property<Integer> MAX_PASSWORD_LENGTH =
newProperty("settings.security.passwordMaxLength", 26);
@Comment({
"Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512,",
"MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB,",
"PBKDF2DJANGO, WORDPRESS, ROYALAUTH, ARGON2, CUSTOM (for developers only). See full list at",
"https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md",
"If you use ARGON2, check that you have the argon2 c library on your system"
})
public static final Property<HashAlgorithm> PASSWORD_HASH =
newProperty(HashAlgorithm.class, "settings.security.passwordHash", HashAlgorithm.SHA256);
@Comment({
"If a password check fails, AuthMe will also try to check with the following hash methods.",
"Use this setting when you change from one hash method to another.",
"AuthMe will update the password to the new hash. Example:",
"legacyHashes:",
"- 'SHA1'"
})
public static final Property<Set<HashAlgorithm>> LEGACY_HASHES =
new EnumSetProperty<>(HashAlgorithm.class, "settings.security.legacyHashes");
@Comment("Salt length for the SALTED2MD5 MD5(MD5(password)+salt)")
public static final Property<Integer> DOUBLE_MD5_SALT_LENGTH =
newProperty("settings.security.doubleMD5SaltLength", 8);
@Comment("Number of rounds to use if passwordHash is set to PBKDF2. Default is 10000")
public static final Property<Integer> PBKDF2_NUMBER_OF_ROUNDS =
newProperty("settings.security.pbkdf2Rounds", 10000);
@Comment({"Prevent unsafe passwords from being used; put them in lowercase!",
"You should always set 'help' as unsafePassword due to possible conflicts.",
"unsafePasswords:",
"- '123456'",
"- 'password'",
"- 'help'"})
public static final Property<Set<String>> UNSAFE_PASSWORDS =
newLowercaseStringSetProperty("settings.security.unsafePasswords",
"12345678", "password", "qwertyui", "123456789", "87654321", "1234567890", "asdfghjkl","zxcvbnm,","asdfghjk","12312312","123123123","32132132","321321321");
@Comment("Tempban a user's IP address if they enter the wrong password too many times")
public static final Property<Boolean> TEMPBAN_ON_MAX_LOGINS =
newProperty("Security.tempban.enableTempban", false);
@Comment("How many times a user can attempt to login before their IP being tempbanned")
public static final Property<Integer> MAX_LOGIN_TEMPBAN =
newProperty("Security.tempban.maxLoginTries", 8);
@Comment({"The length of time a IP address will be tempbanned in minutes",
"Default: 480 minutes, or 8 hours"})
public static final Property<Integer> TEMPBAN_LENGTH =
newProperty("Security.tempban.tempbanLength", 480);
@Comment({"How many minutes before resetting the count for failed logins by IP and username",
"Default: 480 minutes (8 hours)"})
public static final Property<Integer> TEMPBAN_MINUTES_BEFORE_RESET =
newProperty("Security.tempban.minutesBeforeCounterReset", 480);
@Comment({"The command to execute instead of using the internal ban system, empty if disabled.",
"Available placeholders: %player%, %ip%"})
public static final Property<String> TEMPBAN_CUSTOM_COMMAND =
newProperty("Security.tempban.customCommand", "");
@Comment("Number of characters a recovery code should have (0 to disable)")
public static final Property<Integer> RECOVERY_CODE_LENGTH =
newProperty("Security.recoveryCode.length", 8);
@Comment("How many hours is a recovery code valid for?")
public static final Property<Integer> RECOVERY_CODE_HOURS_VALID =
newProperty("Security.recoveryCode.validForHours", 6);
@Comment("Max number of tries to enter recovery code")
public static final Property<Integer> RECOVERY_CODE_MAX_TRIES =
newProperty("Security.recoveryCode.maxTries", 4);
@Comment({"How long a player has after password recovery to change their password",
"without logging in. This is in minutes.",
"Default: 2 minutes"})
public static final Property<Integer> PASSWORD_CHANGE_TIMEOUT =
newProperty("Security.recoveryCode.passwordChangeTimeout", 5);
@Comment({
"Seconds a user has to wait for before a password recovery mail may be sent again",
"This prevents an attacker from abusing AuthMe's email feature."
})
public static final Property<Integer> EMAIL_RECOVERY_COOLDOWN_SECONDS =
newProperty("Security.emailRecovery.cooldown", 60);
@Comment({
"The mail shown using /email show will be partially hidden",
"E.g. (if enabled)",
" original email: my.email@example.com",
" hidden email: my.***@***mple.com"
})
public static final Property<Boolean> USE_EMAIL_MASKING =
newProperty("Security.privacy.enableEmailMasking", false);
@Comment("Minutes after which a verification code will expire")
public static final Property<Integer> VERIFICATION_CODE_EXPIRATION_MINUTES =
newProperty("Security.privacy.verificationCodeExpiration", 10);
private SecuritySettings() {
}
}