diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6491849b4..769f9fffc 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -163,28 +163,34 @@ permissions: default: true authme.player.email.add: description: Command permission to add an email address. - default: false + default: true authme.player.email.change: description: Command permission to change the email address. - default: false + default: true authme.player.email.recover: - description: Command permission to recover an account using it's email address. - default: false + description: Command permission to recover an account using its email address. + default: true authme.player.captcha: description: Command permission to use captcha. - default: false + default: true authme.player.canbeforced: description: Permission for users a login can be forced to. - default: false + default: true + authme.player.seeownaccounts: + description: Permission to use to see own other accounts. + default: true authme.vip: description: Allow vip slot when the server is full - default: false + default: op authme.bypassantibot: description: Bypass the AntiBot check - default: false + default: op authme.allowmultipleaccounts: description: Allow more accounts for same ip - default: false + default: op authme.bypassforcesurvival: description: Bypass all ForceSurvival features + default: op + authme.bypasspurge: + description: Permission to bypass the purging process default: false diff --git a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java new file mode 100644 index 000000000..b96224c36 --- /dev/null +++ b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java @@ -0,0 +1,186 @@ +package fr.xephi.authme.permission; + +import com.google.common.collect.ImmutableSet; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.configuration.MemorySection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static fr.xephi.authme.TestHelper.getJarFile; +import static org.junit.Assert.fail; + +/** + * Tests that the permissions listed in plugin.yml correspond to the ones in the code. + */ +public class PermissionConsistencyTest { + + /** All classes defining permission nodes. */ + private static final Set> PERMISSION_CLASSES = ImmutableSet + .>of(PlayerPermission.class, AdminPermission.class, PlayerStatePermission.class); + + /** Wildcard permissions (present in plugin.yml but not in the codebase). */ + private static final Set PLUGIN_YML_PERMISSIONS_WILDCARDS = + ImmutableSet.of("authme.admin.*", "authme.player.*", "authme.player.email"); + + /** Name of the fields that make up a permission entry in plugin.yml. */ + private static final Set PERMISSION_FIELDS = ImmutableSet.of("description", "default", "children"); + + /** All known PermissionNode objects. */ + private static List permissionNodes; + + /** All permissions listed in plugin.yml. */ + private static Map pluginYmlPermissions; + + @BeforeClass + public static void gatherPermissionNodes() { + permissionNodes = getPermissionsFromClasses(); + pluginYmlPermissions = getPermissionsFromPluginYmlFile(); + } + + @Test + public void shouldHaveAllPermissionsInPluginYml() { + // given + List errors = new ArrayList<>(); + + // when + for (PermissionNode node : permissionNodes) { + PermissionDefinition permDef = pluginYmlPermissions.get(node.getNode()); + if (permDef == null) { + errors.add("Permission '" + node.getNode() + "' does not exist in plugin.yml"); + } else if (!node.getDefaultPermission().equals(permDef.expectedDefault)) { + errors.add("Default value for '" + node.getNode() + "' has different default value"); + } + } + + // then + if (!errors.isEmpty()) { + fail("Found consistency issues!\n" + StringUtils.join("\n", errors)); + } + } + + @Test + public void shouldNotHaveUnknownPermissionsInPluginYml() { + // given + List errors = new ArrayList<>(); + + // when + for (String key : pluginYmlPermissions.keySet()) { + if (!PLUGIN_YML_PERMISSIONS_WILDCARDS.contains(key)) { + if (!doesPermissionExist(key, permissionNodes)) { + errors.add("Permission '" + key + "' in plugin.yml does not exist in the codebase"); + } + // TODO #337: Add else if checking that non-wildcard permissions do not have children + } + // TODO #337: Add check that children of wildcard permissions make sense + } + + // then + if (!errors.isEmpty()) { + fail("Found consistency issues!\n" + StringUtils.join("\n", errors)); + } + } + + private static boolean doesPermissionExist(String key, List nodes) { + for (PermissionNode node : nodes) { + if (key.equals(node.getNode())) { + return true; + } + } + return false; + } + + /** + * Returns all {@link PermissionNode} fields from the permission node classes. + * + * @return collection of all permission nodes in the code + */ + private static List getPermissionsFromClasses() { + List nodes = new ArrayList<>(); + for (Class clazz : PERMISSION_CLASSES) { + nodes.addAll(Arrays.asList(clazz.getEnumConstants())); + } + return Collections.unmodifiableList(nodes); + } + + /** + * Returns all permission entries from the plugin.yml file. + * + * @return map with the permission entries by permission node + */ + private static Map getPermissionsFromPluginYmlFile() { + FileConfiguration pluginFile = YamlConfiguration.loadConfiguration(getJarFile("/plugin.yml")); + MemorySection permsList = (MemorySection) pluginFile.get("permissions"); + + Map permissions = new HashMap<>(); + addChildren(permsList, permissions); + return permissions; + } + + /** + * Recursively visits every MemorySection and creates {@link PermissionDefinition} where applicable. + * + * @param node the node to visit + * @param collection the collection to add constructed permission definitions to + */ + private static void addChildren(MemorySection node, Map collection) { + // A MemorySection may have a permission entry, as well as MemorySection children + boolean hasPermissionEntry = false; + for (String key : node.getKeys(false)) { + if (node.get(key) instanceof MemorySection && !"children".equals(key)) { + addChildren((MemorySection) node.get(key), collection); + } else if (PERMISSION_FIELDS.contains(key)) { + hasPermissionEntry = true; + } else { + throw new IllegalStateException("Unexpected key '" + key + "'"); + } + } + if (hasPermissionEntry) { + PermissionDefinition permDef = new PermissionDefinition(node); + collection.put(permDef.node, permDef); + } + } + + // TODO #337: Save children to PermissionDefinition objects + private static final class PermissionDefinition { + + private final String node; + private final DefaultPermission expectedDefault; + + PermissionDefinition(MemorySection memorySection) { + this.node = removePermissionsPrefix(memorySection.getCurrentPath()); + this.expectedDefault = mapToDefaultPermission(memorySection.getString("default")); + } + + private static DefaultPermission mapToDefaultPermission(String defaultDescription) { + if ("true".equals(defaultDescription)) { + return DefaultPermission.ALLOWED; + } else if ("op".equals(defaultDescription)) { + return DefaultPermission.OP_ONLY; + } else if ("false".equals(defaultDescription)) { + return DefaultPermission.NOT_ALLOWED; + } else if (defaultDescription == null) { + // Return null: comparison with PermissionNode will always fail + return null; + } + throw new IllegalStateException("Unknown default description '" + defaultDescription + "'"); + } + + private static String removePermissionsPrefix(String path) { + if (path.startsWith("permissions.")) { + return path.substring("permissions.".length()); + } + throw new IllegalStateException("Unexpected path '" + path + "': does not begin with 'permissions.'"); + } + } + +}