From c66622bd09a0ba142e35dae8c56df9134d2756f8 Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 29 May 2018 14:45:21 +0100 Subject: [PATCH] Fix regex permissions not really working properly (#1021) --- .../api/nodetype/types/RegexType.java | 67 +++++++++++++++ .../node/factory/LegacyNodeFactory.java | 10 --- .../common/node/model/ImmutableNode.java | 10 +-- .../common/node/model/NodeTypes.java | 84 +++++++++++++++++-- .../common/processors/RegexProcessor.java | 14 ++-- .../luckperms/common/utils/PatternCache.java | 23 ++++- 6 files changed, 177 insertions(+), 31 deletions(-) create mode 100644 api/src/main/java/me/lucko/luckperms/api/nodetype/types/RegexType.java diff --git a/api/src/main/java/me/lucko/luckperms/api/nodetype/types/RegexType.java b/api/src/main/java/me/lucko/luckperms/api/nodetype/types/RegexType.java new file mode 100644 index 00000000..470df647 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/nodetype/types/RegexType.java @@ -0,0 +1,67 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.api.nodetype.types; + +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.nodetype.NodeType; +import me.lucko.luckperms.api.nodetype.NodeTypeKey; + +import java.util.Optional; +import java.util.regex.Pattern; + +import javax.annotation.Nonnull; + +/** + * A sub-type of {@link Node} used to store regex permissions. + * + * @since 4.3 + */ +public interface RegexType extends NodeType { + + /** + * The key for this type. + */ + NodeTypeKey KEY = new NodeTypeKey(){}; + + /** + * Gets the non-compiled pattern string. + * + * @return the pattern string + */ + @Nonnull + String getPatternString(); + + /** + * Gets the pattern for the regex node. + * + *

Will return an empty optional if the Pattern could not be parsed.

+ * + * @return the pattern + */ + @Nonnull + Optional getPattern(); + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/node/factory/LegacyNodeFactory.java b/common/src/main/java/me/lucko/luckperms/common/node/factory/LegacyNodeFactory.java index fee267ae..b13264ac 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/factory/LegacyNodeFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/factory/LegacyNodeFactory.java @@ -37,16 +37,6 @@ import java.util.regex.Pattern; public final class LegacyNodeFactory { - /** - * The characters which are delimited when serializing a permission string - */ - public static final String[] PERMISSION_DELIMITERS = new String[]{"/", "-", "$", "(", ")", "=", ","}; - - /** - * The characters which are delimited when serializing a server or world string - */ - public static final String[] SERVER_WORLD_DELIMITERS = new String[]{"/", "-"}; - /** * The characters which are delimited when serializing a context set */ diff --git a/common/src/main/java/me/lucko/luckperms/common/node/model/ImmutableNode.java b/common/src/main/java/me/lucko/luckperms/common/node/model/ImmutableNode.java index 03b8be79..dad2f0ce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/model/ImmutableNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/model/ImmutableNode.java @@ -35,7 +35,7 @@ import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.api.nodetype.NodeType; import me.lucko.luckperms.api.nodetype.NodeTypeKey; -import me.lucko.luckperms.common.node.factory.LegacyNodeFactory; +import me.lucko.luckperms.api.nodetype.types.RegexType; import me.lucko.luckperms.common.node.factory.NodeBuilder; import me.lucko.luckperms.common.node.utils.ShorthandParser; import me.lucko.luckperms.common.processors.WildcardProcessor; @@ -115,18 +115,18 @@ public final class ImmutableNode implements Node { world = standardizeServerWorld(world); // define core attributes - this.permission = LegacyNodeFactory.unescapeDelimiters(permission, LegacyNodeFactory.PERMISSION_DELIMITERS).intern(); + this.permission = permission.intern(); this.value = value; this.override = override; this.expireAt = expireAt; - this.server = internString(LegacyNodeFactory.unescapeDelimiters(server, LegacyNodeFactory.SERVER_WORLD_DELIMITERS)); - this.world = internString(LegacyNodeFactory.unescapeDelimiters(world, LegacyNodeFactory.SERVER_WORLD_DELIMITERS)); + this.server = internString(server); + this.world = internString(world); this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable(); // define cached state this.wildcardLevel = this.permission.endsWith(WildcardProcessor.WILDCARD_SUFFIX) ? this.permission.chars().filter(num -> num == NODE_SEPARATOR_CODE).sum() : -1; this.resolvedTypes = NodeTypes.parseTypes(this.permission); - this.resolvedShorthand = ImmutableList.copyOf(ShorthandParser.parseShorthand(getPermission())); + this.resolvedShorthand = this.resolvedTypes.containsKey(RegexType.KEY) ? ImmutableList.of() : ImmutableList.copyOf(ShorthandParser.parseShorthand(getPermission())); this.optServer = Optional.ofNullable(this.server); this.optWorld = Optional.ofNullable(this.world); diff --git a/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java b/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java index af6632bf..6a62acbd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java @@ -34,8 +34,10 @@ import me.lucko.luckperms.api.nodetype.types.DisplayNameType; import me.lucko.luckperms.api.nodetype.types.InheritanceType; import me.lucko.luckperms.api.nodetype.types.MetaType; import me.lucko.luckperms.api.nodetype.types.PrefixType; +import me.lucko.luckperms.api.nodetype.types.RegexType; import me.lucko.luckperms.api.nodetype.types.SuffixType; import me.lucko.luckperms.api.nodetype.types.WeightType; +import me.lucko.luckperms.common.buffers.Cache; import me.lucko.luckperms.common.node.factory.LegacyNodeFactory; import me.lucko.luckperms.common.utils.PatternCache; @@ -43,8 +45,11 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public final class NodeTypes { @@ -60,10 +65,13 @@ public final class NodeTypes { public static final String META_NODE_MARKER = META_KEY + "."; public static final String WEIGHT_NODE_MARKER = WEIGHT_KEY + "."; public static final String DISPLAY_NAME_NODE_MARKER = DISPLAY_NAME_KEY + "."; + public static final String REGEX_MARKER_1 = "r="; + public static final String REGEX_MARKER_2 = "R="; // used to split prefix/suffix/meta nodes private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimiterPattern(".", "\\")).limit(2); + @Nonnull public static Map, NodeType> parseTypes(String s) { Map, NodeType> results = new IdentityHashMap<>(); @@ -97,6 +105,11 @@ public final class NodeTypes { results.put(DisplayNameType.KEY, type); } + type = parseRegexType(s); + if (type != null) { + results.put(RegexType.KEY, type); + } + if (results.isEmpty()) { return ImmutableMap.of(); } @@ -104,7 +117,8 @@ public final class NodeTypes { return results; } - private static InheritanceType parseInheritanceType(String s) { + @Nullable + public static InheritanceType parseInheritanceType(String s) { s = s.toLowerCase(); if (!s.startsWith(GROUP_NODE_MARKER)) { return null; @@ -114,7 +128,8 @@ public final class NodeTypes { return new Inheritance(groupName); } - private static MetaType parseMetaType(String s) { + @Nullable + public static MetaType parseMetaType(String s) { if (!s.toLowerCase().startsWith(META_NODE_MARKER)) { return null; } @@ -133,7 +148,8 @@ public final class NodeTypes { ); } - private static PrefixType parsePrefixType(String s) { + @Nullable + public static PrefixType parsePrefixType(String s) { if (!s.toLowerCase().startsWith(PREFIX_NODE_MARKER)) { return null; } @@ -155,7 +171,8 @@ public final class NodeTypes { } } - private static SuffixType parseSuffixType(String s) { + @Nullable + public static SuffixType parseSuffixType(String s) { if (!s.toLowerCase().startsWith(SUFFIX_NODE_MARKER)) { return null; } @@ -177,7 +194,8 @@ public final class NodeTypes { } } - private static WeightType parseWeightType(String s) { + @Nullable + public static WeightType parseWeightType(String s) { String lower = s.toLowerCase(); if (!lower.startsWith(WEIGHT_NODE_MARKER)) { return null; @@ -190,7 +208,8 @@ public final class NodeTypes { } } - private static DisplayNameType parseDisplayNameType(String s) { + @Nullable + public static DisplayNameType parseDisplayNameType(String s) { if (!s.toLowerCase().startsWith(DISPLAY_NAME_NODE_MARKER)) { return null; } @@ -198,6 +217,15 @@ public final class NodeTypes { return new DisplayName(s.substring(DISPLAY_NAME_NODE_MARKER.length())); } + @Nullable + public static RegexType parseRegexType(String s) { + if (!s.startsWith(REGEX_MARKER_1) && !s.startsWith(REGEX_MARKER_2)) { + return null; + } + + return new Regex(s.substring(2)); + } + private static final class Inheritance implements InheritanceType { private final String groupName; @@ -456,6 +484,50 @@ public final class NodeTypes { } } + private static final class Regex extends Cache implements RegexType { + private final String patternString; + + private Regex(String patternString) { + this.patternString = patternString; + } + + @Nonnull + @Override + protected PatternCache.CachedPattern supply() { + return PatternCache.lookup(this.patternString); + } + + @Nonnull + @Override + public String getPatternString() { + return this.patternString; + } + + @Nonnull + @Override + public Optional getPattern() { + return Optional.ofNullable(get().getPattern()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Regex that = (Regex) o; + return Objects.equals(this.patternString, that.patternString); + } + + @Override + public int hashCode() { + return Objects.hash(this.patternString); + } + + @Override + public String toString() { + return "Regex{pattern=" + this.patternString + '}'; + } + } + private NodeTypes() {} } diff --git a/common/src/main/java/me/lucko/luckperms/common/processors/RegexProcessor.java b/common/src/main/java/me/lucko/luckperms/common/processors/RegexProcessor.java index 3ef8b073..4c305bc2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/processors/RegexProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/processors/RegexProcessor.java @@ -28,7 +28,8 @@ package me.lucko.luckperms.common.processors; import com.google.common.collect.ImmutableMap; import me.lucko.luckperms.api.Tristate; -import me.lucko.luckperms.common.utils.PatternCache; +import me.lucko.luckperms.api.nodetype.types.RegexType; +import me.lucko.luckperms.common.node.model.NodeTypes; import java.util.Collections; import java.util.Map; @@ -52,18 +53,17 @@ public class RegexProcessor extends AbstractPermissionProcessor implements Permi public void refresh() { ImmutableMap.Builder builder = ImmutableMap.builder(); for (Map.Entry e : this.sourceMap.entrySet()) { - if (!e.getKey().startsWith("r=") && !e.getKey().startsWith("R=")) { + RegexType regexType = NodeTypes.parseRegexType(e.getKey()); + if (regexType == null) { continue; } - String pattern = e.getKey().substring(2); - Pattern p = PatternCache.compile(pattern); - - if (p == null) { + Pattern pattern = regexType.getPattern().orElse(null); + if (pattern == null) { continue; } - builder.put(p, e.getValue()); + builder.put(pattern, e.getValue()); } this.regexPermissions = builder.build(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java b/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java index 7c223b82..b9246460 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java @@ -32,6 +32,8 @@ import java.util.Objects; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import javax.annotation.Nullable; + public final class PatternCache { private static final LoadingCache CACHE = Caffeine.newBuilder() @@ -43,9 +45,14 @@ public final class PatternCache { } }); - public static Pattern compile(String regex) { + public static CachedPattern lookup(String regex) { CachedPattern pattern = CACHE.get(regex); Objects.requireNonNull(pattern, "pattern"); + return pattern; + } + + public static Pattern compile(String regex) throws PatternSyntaxException { + CachedPattern pattern = lookup(regex); if (pattern.ex != null) { throw pattern.ex; } else { @@ -60,12 +67,12 @@ public final class PatternCache { * @param escape the string used to escape the delimiter where the pattern shouldn't match * @return a pattern */ - public static Pattern compileDelimiterPattern(String delimiter, String escape) { + public static Pattern compileDelimiterPattern(String delimiter, String escape) throws PatternSyntaxException { String pattern = "(?