#347 Test that all read properties exist as Property field

- Create consistency test to verify that all properties in config.yml are defined as a Property field in a SettingsClass implementation (currently fails)
- Add some missing properties
- Minor: convert tabs to spaces
This commit is contained in:
ljacqu 2016-01-09 09:30:49 +01:00
parent 0603243c86
commit 88629702f5
14 changed files with 345 additions and 95 deletions

View File

@ -30,6 +30,10 @@ public class EmailSettings implements SettingsClass {
public static final Property<String> MAIL_PASSWORD = public static final Property<String> MAIL_PASSWORD =
newProperty(STRING, "Email.mailPassword", ""); newProperty(STRING, "Email.mailPassword", "");
@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") @Comment("Recovery password length")
public static final Property<Integer> RECOVERY_PASSWORD_LENGTH = public static final Property<Integer> RECOVERY_PASSWORD_LENGTH =
newProperty(INTEGER, "Email.RecoveryPasswordLength", 8); newProperty(INTEGER, "Email.RecoveryPasswordLength", 8);

View File

@ -0,0 +1,46 @@
package fr.xephi.authme.settings.custom;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import static fr.xephi.authme.settings.domain.Property.newProperty;
public class PluginSettings implements SettingsClass {
@Comment("The name shown in the help messages")
public static final Property<String> HELP_HEADER =
newProperty("settings.helpHeader", "AuthMeReloaded");
@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", false);
@Comment({
"After how many minutes should a session expire?",
"0 for unlimited time (Very dangerous, use it at your own risk!)",
"Remember that sessions will end only after the timeout, and",
"if the player's IP has changed but the timeout hasn't expired,",
"the player will be kicked from the server due to invalid session"
})
public static final Property<Integer> SESSIONS_TIMEOUT =
newProperty("settings.sessions.timeout", 10);
@Comment({
"Should the session expire if the player tries to log in with",
"another IP address?"
})
public static final Property<Boolean> SESSIONS_EXPIRE_ON_IP_CHANGE =
newProperty("settings.sessions.sessionExpireOnIpChange", true);
private PluginSettings() {
}
}

View File

@ -0,0 +1,147 @@
package fr.xephi.authme.settings.custom;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newProperty;
public class RestrictionSettings implements SettingsClass {
@Comment({
"Can not authenticated players chat and see the chat log?",
"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("Allowed commands for unauthenticated players")
public static final Property<List<String>> ALLOW_COMMANDS =
newProperty(PropertyType.STRING_LIST, "settings.restrictions.allowCommands",
"login", "register", "l", "reg", "email", "captcha");
@Comment("Max number of allowed registrations per IP")
// TODO ljacqu 20160109: If 0 == unlimited, add this fact ot the comment
public static final Property<Integer> MAX_REGISTRATION_PER_IP =
newProperty("settings.restrictions.maxRegPerIp", 1);
@Comment("Minimum allowed username length")
public static final Property<Integer> MIN_NICKNAME_LENGTH =
newProperty("settings.restrictions.minNicknameLength", 4);
@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 will be teleported to the world spawnpoint",
"after successful authentication.",
"The quit location of the player will be overwritten.",
"This is different from \"teleportUnAuthedToSpawn\" that teleport player",
"back to his quit location after the authentication."})
public static final Property<Boolean> FORCE_SPAWN_LOCATION_AFTER_LOGIN =
newProperty("settings.restrictions.ForceSpawnLocOnJoinEnabled", false);
@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 AllowedRestrctedUser field."})
public static final Property<Boolean> ENABLE_RESTRICTED_USERS =
newProperty("settings.restrictions.AllowRestrictedUser", false);
@Comment({
"The restricted user feature will kick players listed below",
"if they don't match the defined IP address.",
"Example:",
" AllowedRestrictedUser:",
" - playername;127.0.0.1"})
public static final Property<List<String>> ALLOWED_RESTRICTED_USERS =
newProperty(PropertyType.STRING_LIST, "settings.restrictions.AllowedRestrictedUser");
@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({
"Should not authenticated players have speed = 0?",
"This will reset the fly/walk speed to default value after the login."})
public static final Property<Boolean> REMOVE_SPEED =
newProperty("settings.restrictions.removeSpeed", true);
@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", 30);
@Comment("Regex syntax 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", 100);
@Comment({
"Enable double check of password when you register",
"when it's true, registration requires that kind of command:",
"/register <password> <confirmPassword>"})
public static final Property<Boolean> ENABLE_PASSWORD_CONFIRMATION =
newProperty("settings.restrictions.enablePasswordConfirmation", true);
@Comment("Should we protect the player inventory before logging in?")
public static final Property<Boolean> PROTECT_INVENTORY_BEFORE_LOGIN =
newProperty("settings.restrictions.ProtectInventoryBeforeLogIn", true);
@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", true);
@Comment({
"WorldNames where we need to force the spawn location for ForceSpawnLocOnJoinEnabled",
"Case-sensitive!"})
public static final Property<List<String>> FORCE_SPAWN_ON_WORLDS =
newProperty(PropertyType.STRING_LIST, "settings.restrictions.ForceSpawnOnTheseWorlds",
"world", "world_nether", "world_the_end");
@Comment("Ban ip when the ip is not the ip registered in database")
public static final Property<Boolean> BAN_UNKNOWN_IP =
newProperty("settings.restrictions.banUnsafedIP", false);
private RestrictionSettings() {
}
}

View File

@ -18,7 +18,8 @@ final class SettingsFieldRetriever {
/** The classes to scan for properties. */ /** The classes to scan for properties. */
private static final List<Class<? extends SettingsClass>> CONFIGURATION_CLASSES = Arrays.asList( private static final List<Class<? extends SettingsClass>> CONFIGURATION_CLASSES = Arrays.asList(
ConverterSettings.class, DatabaseSettings.class, EmailSettings.class, HooksSettings.class, ConverterSettings.class, PluginSettings.class, RestrictionSettings.class,
DatabaseSettings.class, EmailSettings.class, HooksSettings.class,
ProtectionSettings.class, PurgeSettings.class, SecuritySettings.class); ProtectionSettings.class, PurgeSettings.class, SecuritySettings.class);
private SettingsFieldRetriever() { private SettingsFieldRetriever() {

View File

@ -14,7 +14,7 @@ import java.util.TreeMap;
*/ */
public class PropertyMap { public class PropertyMap {
private Map<Property<?>, String[]> propertyMap; private Map<Property<?>, String[]> map;
private PropertyMapComparator comparator; private PropertyMapComparator comparator;
/** /**
@ -22,7 +22,7 @@ public class PropertyMap {
*/ */
public PropertyMap() { public PropertyMap() {
comparator = new PropertyMapComparator(); comparator = new PropertyMapComparator();
propertyMap = new TreeMap<>(comparator); map = new TreeMap<>(comparator);
} }
/** /**
@ -33,7 +33,7 @@ public class PropertyMap {
*/ */
public void put(Property property, String[] comments) { public void put(Property property, String[] comments) {
comparator.add(property); comparator.add(property);
propertyMap.put(property, comments); map.put(property, comments);
} }
/** /**
@ -42,7 +42,7 @@ public class PropertyMap {
* @return The entry set * @return The entry set
*/ */
public Set<Map.Entry<Property<?>, String[]>> entrySet() { public Set<Map.Entry<Property<?>, String[]>> entrySet() {
return propertyMap.entrySet(); return map.entrySet();
} }
/** /**
@ -51,7 +51,16 @@ public class PropertyMap {
* @return The key set * @return The key set
*/ */
public Set<Property<?>> keySet() { public Set<Property<?>> keySet() {
return propertyMap.keySet(); return map.keySet();
}
/**
* Return the size of the map.
*
* @return The size
*/
public int size() {
return map.size();
} }
} }

View File

@ -340,8 +340,6 @@ Email:
RecoveryPasswordLength: 8 RecoveryPasswordLength: 8
# Email subject of password get # Email subject of password get
mailSubject: 'Your new AuthMe Password' mailSubject: 'Your new AuthMe Password'
# Email text here
mailText: 'Dear <playername>, <br /><br /> This is your new AuthMe password for the server <br /><br /> <servername> : <br /><br /> <generatedpass><br /><image><br />Do not forget to change password after login! <br /> /changepassword <generatedpass> newPassword'
# Like maxRegPerIp but with email # Like maxRegPerIp but with email
maxRegPerEmail: 1 maxRegPerEmail: 1
# Recall players to add an email? # Recall players to add an email?

View File

@ -1,14 +1,24 @@
package fr.xephi.authme.settings.custom; package fr.xephi.authme.settings.custom;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.configuration.MemorySection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat; import static org.junit.Assert.fail;
/** /**
* Test for {@link NewSetting} and the project's config.yml, * Test for {@link NewSetting} and the project's config.yml,
@ -16,14 +26,15 @@ import static org.junit.Assume.assumeThat;
*/ */
public class ConfigFileConsistencyTest { public class ConfigFileConsistencyTest {
/** The file name of the project's sample config file. */
private static final String CONFIG_FILE = "/config.yml";
@Test @Test
public void shouldHaveAllConfigs() throws IOException { public void shouldHaveAllConfigs() throws IOException {
URL url = this.getClass().getResource("/config.yml");
File configFile = new File(url.getFile());
// given // given
assumeThat(configFile.exists(), equalTo(true)); URL url = this.getClass().getResource(CONFIG_FILE);
NewSetting settings = new NewSetting(configFile); File configFile = new File(url.getFile());
NewSetting settings = new NewSetting(YamlConfiguration.loadConfiguration(configFile), new File("bogus"), null);
// when // when
boolean result = settings.containsAllSettings(SettingsFieldRetriever.getAllPropertyFields()); boolean result = settings.containsAllSettings(SettingsFieldRetriever.getAllPropertyFields());
@ -32,4 +43,38 @@ public class ConfigFileConsistencyTest {
assertThat(result, equalTo(true)); assertThat(result, equalTo(true));
} }
@Test
public void shouldNotHaveUnknownConfigs() {
// given
URL url = this.getClass().getResource(CONFIG_FILE);
File configFile = new File(url.getFile());
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(configFile);
Map<String, Object> allReadProperties = configuration.getValues(true);
Set<String> knownKeys = getAllKnownPropertyPaths();
// when
List<String> unknownPaths = new ArrayList<>();
for (Map.Entry<String, Object> entry : allReadProperties.entrySet()) {
// The value being a MemorySection means it's a parent node
if (!(entry.getValue() instanceof MemorySection) && !knownKeys.contains(entry.getKey())) {
unknownPaths.add(entry.getKey());
}
}
// then
if (!unknownPaths.isEmpty()) {
fail("Found " + unknownPaths.size() + " unknown property paths in the project's config.yml: \n- "
+ StringUtils.join("\n- ", unknownPaths));
}
}
private static Set<String> getAllKnownPropertyPaths() {
PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields();
Set<String> paths = new HashSet<>(propertyMap.size());
for (Property<?> property : propertyMap.keySet()) {
paths.add(property.getPath());
}
return paths;
}
} }