diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java index 4ef0aca2e..8ae97407f 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java @@ -81,7 +81,7 @@ public enum PlayerPermission implements PermissionNode { SEE_OTHER_ACCOUNTS("authme.player.seeotheraccounts"), /** - * + * Permission to use all player (non-admin) commands. */ ALL_COMMANDS("authme.player.*"); diff --git a/src/tools/README.md b/src/tools/README.md new file mode 100644 index 000000000..b61dabead --- /dev/null +++ b/src/tools/README.md @@ -0,0 +1,3 @@ +# About src/tools +This _tools_ folder provides helpers and extended tests useful during the development of AuthMe. +This folder is not included during the build of AuthMe. diff --git a/src/tools/docs/permission_nodes.md b/src/tools/docs/permission_nodes.md new file mode 100644 index 000000000..8e78dee43 --- /dev/null +++ b/src/tools/docs/permission_nodes.md @@ -0,0 +1,43 @@ + + + +## AuthMe Permission Nodes +The following are the permission nodes that are currently supported by the latest dev builds. + +- **authme.admin.*** – Give access to all admin commands. +- **authme.admin.accounts** – Administrator command to see all accounts associated with a user. +- **authme.admin.changemail** – Administrator command to set or change the email address of a user. +- **authme.admin.changepassword** – Administrator command to change the password of a user. +- **authme.admin.converter** – Administrator command to convert old or other data to AuthMe data. +- **authme.admin.firstspawn** – Administrator command to teleport to the first AuthMe spawn. +- **authme.admin.forcelogin** – Administrator command to force-login an existing user. +- **authme.admin.getemail** – Administrator command to get the email address of a user, if set. +- **authme.admin.getip** – Administrator command to get the last known IP of a user. +- **authme.admin.lastlogin** – Administrator command to see the last login date and time of a user. +- **authme.admin.purge** – Administrator command to purge old user data. +- **authme.admin.purgebannedplayers** – Administrator command to purge all data associated with banned players. +- **authme.admin.purgelastpos** – Administrator command to purge the last position of a user. +- **authme.admin.register** – Administrator command to register a new user. +- **authme.admin.reload** – Administrator command to reload the plugin configuration. +- **authme.admin.setfirstspawn** – Administrator command to set the first AuthMe spawn. +- **authme.admin.setspawn** – Administrator command to set the AuthMe spawn. +- **authme.admin.spawn** – Administrator command to teleport to the AuthMe spawn. +- **authme.admin.switchantibot** – Administrator command to toggle the AntiBot protection status. +- **authme.admin.unregister** – Administrator command to unregister an existing user. +- **authme.player.*** – Permission to use all player (non-admin) commands. +- **authme.player.allow2accounts** – Permission for users to allow two accounts. +- **authme.player.bypassantibot** – Permission node to bypass AntiBot protection. +- **authme.player.bypassforcesurvival** – Permission for users to bypass force-survival mode. +- **authme.player.canbeforced** – Permission for users a login can be forced to. +- **authme.player.captcha** – Command permission to use captcha. +- **authme.player.changepassword** – Command permission to change the password. +- **authme.player.email.add** – Command permission to add an email address. +- **authme.player.email.change** – Command permission to change the email address. +- **authme.player.email.recover** – Command permission to recover an account using it's email address. +- **authme.player.login** – Command permission to login. +- **authme.player.logout** – Command permission to logout. +- **authme.player.register** – Command permission to register. +- **authme.player.seeotheraccounts** – Permission for user to see other accounts. +- **authme.player.unregister** – Command permission to unregister. +- **authme.player.vip** – Permission node to identify VIP users. + diff --git a/src/tools/permissions/PermissionNodesGatherer.java b/src/tools/permissions/PermissionNodesGatherer.java new file mode 100644 index 000000000..5b61adbef --- /dev/null +++ b/src/tools/permissions/PermissionNodesGatherer.java @@ -0,0 +1,113 @@ +package permissions; + +import fr.xephi.authme.permission.AdminPermission; +import fr.xephi.authme.permission.PermissionNode; +import fr.xephi.authme.permission.PlayerPermission; +import fr.xephi.authme.util.StringUtils; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Gatherer to generate up-to-date lists of the AuthMe permission nodes. + */ +public class PermissionNodesGatherer { + + /** The folder in which the implementations of {@link PermissionNode} reside. */ + private static final String PERMISSION_NODE_SOURCE_FOLDER = + "src/main/java/fr/xephi/authme/permission/"; + + /** + * Regular expression that should match the JavaDoc comment above an enum, including + * the name of the enum value. The first group (i.e. {@code \\1}) should be the JavaDoc description; + * the second group should contain the enum value. + */ + private static final Pattern JAVADOC_WITH_ENUM_PATTERN = Pattern.compile( + "/\\*\\*\\s+\\*" // Match starting '/**' and the '*' on the next line + + "(.*?)\\s+\\*/" // Capture everything until we encounter '*/' + + "\\s+([A-Z_]+)\\("); // Match the enum name (e.g. 'LOGIN'), until before the first '(' + + /** + * Return a sorted collection of all permission nodes. + * + * @return AuthMe permission nodes sorted alphabetically + */ + public Set gatherNodes() { + Set nodes = new TreeSet<>(); + for (PermissionNode perm : PlayerPermission.values()) { + nodes.add(perm.getNode()); + } + for (PermissionNode perm : AdminPermission.values()) { + nodes.add(perm.getNode()); + } + return nodes; + } + + /** + * Return a sorted collection of all permission nodes, including its JavaDoc description. + * + * @return Ordered map whose keys are the permission nodes and the values the associated JavaDoc + */ + public Map gatherNodesWithJavaDoc() { + Map result = new TreeMap<>(); + addDescriptionsForClass(PlayerPermission.class, result); + addDescriptionsForClass(AdminPermission.class, result); + return result; + } + + private & PermissionNode> void addDescriptionsForClass(Class clazz, + Map descriptions) { + String classSource = getSourceForClass(clazz); + Map sourceDescriptions = extractJavaDocFromSource(classSource); + + for (T perm : EnumSet.allOf(clazz)) { + String description = sourceDescriptions.get(perm.name()); + if (description == null) { + System.out.println("Note: Could not retrieve description for " + + clazz.getSimpleName() + "#" + perm.name()); + description = ""; + } + descriptions.put(perm.getNode(), description.trim()); + } + } + + private static Map extractJavaDocFromSource(String source) { + Map allMatches = new HashMap<>(); + Matcher matcher = JAVADOC_WITH_ENUM_PATTERN.matcher(source); + while (matcher.find()) { + String description = matcher.group(1); + String enumValue = matcher.group(2); + allMatches.put(enumValue, description); + } + return allMatches; + } + + /** + * Return the Java source code for the given implementation of {@link PermissionNode}. + * + * @param clazz The clazz to the get the source for + * @param The concrete class + * @return Source code of the file + */ + private static & PermissionNode> String getSourceForClass(Class clazz) { + String classFile = PERMISSION_NODE_SOURCE_FOLDER + clazz.getSimpleName() + ".java"; + Charset cs = Charset.forName("utf-8"); + try { + return StringUtils.join("\n", + Files.readAllLines(Paths.get(classFile), cs)); + } catch (IOException e) { + throw new RuntimeException("Failed to get the source for class '" + clazz.getSimpleName() + "'"); + } + } + +} diff --git a/src/tools/permissions/PermissionsListWriter.java b/src/tools/permissions/PermissionsListWriter.java new file mode 100644 index 000000000..7d361b1c5 --- /dev/null +++ b/src/tools/permissions/PermissionsListWriter.java @@ -0,0 +1,86 @@ +package permissions; + +import utils.ANewMap; +import utils.GeneratedFileWriter; +import utils.TagReplacer; +import utils.ToolsConstants; + +import java.util.Map; +import java.util.Scanner; +import java.util.Set; + +/** + * Class responsible for formatting a permissions node list and + * for writing it to a file if desired. + */ +public class PermissionsListWriter { + + private static final String PERMISSIONS_OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "permission_nodes.md"; + + public static void main(String[] args) { + // Ask if result should be written to file + Scanner scanner = new Scanner(System.in); + System.out.println("Include description? [Enter 'n' for no]"); + boolean includeDescription = !matches("n", scanner); + + if (!includeDescription) { + outputSimpleList(); + return; + } + + System.out.println("Write to file? [Enter 'n' for console output]"); + boolean writeToFile = !matches("n", scanner); + scanner.close(); + + + if (writeToFile) { + generateAndWriteFile(); + } else { + System.out.println(generatePermissionsList()); + } + } + + + private static void generateAndWriteFile() { + final String permissionsTagValue = generatePermissionsList(); + + Map tags = ANewMap.with("permissions", permissionsTagValue).build(); + GeneratedFileWriter.generateFileFromTemplate( + ToolsConstants.TOOLS_SOURCE_ROOT + "permissions/permission_nodes.tpl.md", PERMISSIONS_OUTPUT_FILE, tags); + System.out.println("Wrote to '" + PERMISSIONS_OUTPUT_FILE + "'"); + System.out.println("Before committing, please verify the output!"); + } + + private static String generatePermissionsList() { + PermissionNodesGatherer gatherer = new PermissionNodesGatherer(); + Map permissions = gatherer.gatherNodesWithJavaDoc(); + + final String template = GeneratedFileWriter.readFromToolsFile("permissions/permission_node_entry.tpl.md"); + StringBuilder sb = new StringBuilder(); + + for (Map.Entry entry : permissions.entrySet()) { + Map tags = ANewMap. + with("node", entry.getKey()) + .and("description", entry.getValue()) + .build(); + sb.append(TagReplacer.applyReplacements(template, tags)); + } + return sb.toString(); + } + + private static void outputSimpleList() { + PermissionNodesGatherer gatherer = new PermissionNodesGatherer(); + Set nodes = gatherer.gatherNodes(); + for (String node : nodes) { + System.out.println(node); + } + System.out.println(); + System.out.println("Total: " + nodes.size()); + } + + private static boolean matches(String answer, Scanner sc) { + String userInput = sc.nextLine(); + return answer.equalsIgnoreCase(userInput); + } + +} diff --git a/src/tools/permissions/README.md b/src/tools/permissions/README.md new file mode 100644 index 000000000..9603cd386 --- /dev/null +++ b/src/tools/permissions/README.md @@ -0,0 +1,2 @@ +# About +Helper script to generate a page with an up-to-date list of permission nodes. diff --git a/src/tools/permissions/permission_node_entry.tpl.md b/src/tools/permissions/permission_node_entry.tpl.md new file mode 100644 index 000000000..ab7f06872 --- /dev/null +++ b/src/tools/permissions/permission_node_entry.tpl.md @@ -0,0 +1 @@ +- **{node}** – {description} diff --git a/src/tools/permissions/permission_nodes.tpl.md b/src/tools/permissions/permission_nodes.tpl.md new file mode 100644 index 000000000..641d28dfa --- /dev/null +++ b/src/tools/permissions/permission_nodes.tpl.md @@ -0,0 +1,7 @@ + + + +## AuthMe Permission Nodes +The following are the permission nodes that are currently supported by the latest dev builds. + +{permissions} diff --git a/src/tools/utils/ANewMap.java b/src/tools/utils/ANewMap.java new file mode 100644 index 000000000..de37bd746 --- /dev/null +++ b/src/tools/utils/ANewMap.java @@ -0,0 +1,36 @@ +package utils; + +import java.util.HashMap; +import java.util.Map; + +/** + * A map builder for the lazy. + *

+ * Sample usage: + * + * Map<String, Integer> map = ANewMap + * .with("test", 123) + * .and("text", 938) + * .and("abc", 456) + * .build(); + * + */ +public class ANewMap { + + private Map map = new HashMap<>(); + + public static ANewMap with(K key, V value) { + ANewMap instance = new ANewMap<>(); + return instance.and(key, value); + } + + public ANewMap and(K key, V value) { + map.put(key, value); + return this; + } + + public Map build() { + return map; + } + +} diff --git a/src/tools/utils/GeneratedFileWriter.java b/src/tools/utils/GeneratedFileWriter.java new file mode 100644 index 000000000..06cbcaf3d --- /dev/null +++ b/src/tools/utils/GeneratedFileWriter.java @@ -0,0 +1,47 @@ +package utils; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; + +/** + * Utility class for writing a generated file with a timestamp. + */ +public final class GeneratedFileWriter { + + private final static Charset CHARSET = Charset.forName("utf-8"); + + private GeneratedFileWriter() { + } + + public static void generateFileFromTemplate(String templateFile, String destinationFile, Map tags) { + String template = readFromFile(templateFile); + String result = TagReplacer.applyReplacements(template, tags); + + writeToFile(destinationFile, result); + } + + private static void writeToFile(String outputFile, String contents) { + try { + Files.write(Paths.get(outputFile), contents.getBytes()); + } catch (IOException e) { + throw new RuntimeException("Failed to write to file '" + outputFile + "'", e); + } + } + + public static String readFromFile(String file) { + try { + return new String(Files.readAllBytes(Paths.get(file)), CHARSET); + } catch (IOException e) { + throw new RuntimeException("Could not read from file '" + file + "'", e); + } + } + + public static String readFromToolsFile(String file) { + return readFromFile(ToolsConstants.TOOLS_SOURCE_ROOT + file); + } + + +} diff --git a/src/tools/utils/TagReplacer.java b/src/tools/utils/TagReplacer.java new file mode 100644 index 000000000..404857b7f --- /dev/null +++ b/src/tools/utils/TagReplacer.java @@ -0,0 +1,46 @@ +package utils; + +import java.util.Date; +import java.util.Map; + +/** + * Class responsible for replacing template tags to actual content. + * For all files, the following tags are defined: + *

    + *
  • {gen_date} – the generation date
  • + *
  • {gen_warning} - warning not to edit the generated file directly
  • + *
+ */ +public class TagReplacer { + + /** + * Replace a template with default tags and custom ones supplied by a map. + * + * @param template The template to process + * @param tags Map with additional tags, e.g. a map entry with key "foo" and value "bar" will replace + * any occurrences of "{foo}" to "bar". + * @return The filled template + */ + public static String applyReplacements(String template, Map tags) { + String result = template; + for (Map.Entry tagRule : tags.entrySet()) { + result = result.replace("{" + tagRule.getKey() + "}", tagRule.getValue().toString()); + } + + return applyReplacements(result); + } + + /** + * Apply the default tag replacements. + * + * @param template The template to process + * @return The filled template + */ + public static String applyReplacements(String template) { + return template + .replace("{gen_date}", new Date().toString()) + .replace("{gen_warning}", "AUTO-GENERATED FILE! Do not edit this directly"); + } + + +} diff --git a/src/tools/utils/ToolsConstants.java b/src/tools/utils/ToolsConstants.java new file mode 100644 index 000000000..30cb0f413 --- /dev/null +++ b/src/tools/utils/ToolsConstants.java @@ -0,0 +1,17 @@ +package utils; + +/** + * Constants for the src/tools folder. + */ +public final class ToolsConstants { + + private ToolsConstants() { + } + + public static final String MAIN_SOURCE_ROOT = "src/main/java/"; + + public static final String TOOLS_SOURCE_ROOT = "src/tools/"; + + public static final String DOCS_FOLDER = TOOLS_SOURCE_ROOT + "docs/"; + +}