Fix regex permissions not really working properly (#1021)
This commit is contained in:
parent
adf0589184
commit
c66622bd09
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* 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<RegexType> KEY = new NodeTypeKey<RegexType>(){};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the non-compiled pattern string.
|
||||||
|
*
|
||||||
|
* @return the pattern string
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
String getPatternString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the pattern for the regex node.
|
||||||
|
*
|
||||||
|
* <p>Will return an empty optional if the Pattern could not be parsed.</p>
|
||||||
|
*
|
||||||
|
* @return the pattern
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Optional<Pattern> getPattern();
|
||||||
|
|
||||||
|
}
|
@ -37,16 +37,6 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public final class LegacyNodeFactory {
|
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
|
* The characters which are delimited when serializing a context set
|
||||||
*/
|
*/
|
||||||
|
@ -35,7 +35,7 @@ import me.lucko.luckperms.api.context.ImmutableContextSet;
|
|||||||
import me.lucko.luckperms.api.context.MutableContextSet;
|
import me.lucko.luckperms.api.context.MutableContextSet;
|
||||||
import me.lucko.luckperms.api.nodetype.NodeType;
|
import me.lucko.luckperms.api.nodetype.NodeType;
|
||||||
import me.lucko.luckperms.api.nodetype.NodeTypeKey;
|
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.factory.NodeBuilder;
|
||||||
import me.lucko.luckperms.common.node.utils.ShorthandParser;
|
import me.lucko.luckperms.common.node.utils.ShorthandParser;
|
||||||
import me.lucko.luckperms.common.processors.WildcardProcessor;
|
import me.lucko.luckperms.common.processors.WildcardProcessor;
|
||||||
@ -115,18 +115,18 @@ public final class ImmutableNode implements Node {
|
|||||||
world = standardizeServerWorld(world);
|
world = standardizeServerWorld(world);
|
||||||
|
|
||||||
// define core attributes
|
// define core attributes
|
||||||
this.permission = LegacyNodeFactory.unescapeDelimiters(permission, LegacyNodeFactory.PERMISSION_DELIMITERS).intern();
|
this.permission = permission.intern();
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.override = override;
|
this.override = override;
|
||||||
this.expireAt = expireAt;
|
this.expireAt = expireAt;
|
||||||
this.server = internString(LegacyNodeFactory.unescapeDelimiters(server, LegacyNodeFactory.SERVER_WORLD_DELIMITERS));
|
this.server = internString(server);
|
||||||
this.world = internString(LegacyNodeFactory.unescapeDelimiters(world, LegacyNodeFactory.SERVER_WORLD_DELIMITERS));
|
this.world = internString(world);
|
||||||
this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable();
|
this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable();
|
||||||
|
|
||||||
// define cached state
|
// define cached state
|
||||||
this.wildcardLevel = this.permission.endsWith(WildcardProcessor.WILDCARD_SUFFIX) ? this.permission.chars().filter(num -> num == NODE_SEPARATOR_CODE).sum() : -1;
|
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.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.optServer = Optional.ofNullable(this.server);
|
||||||
this.optWorld = Optional.ofNullable(this.world);
|
this.optWorld = Optional.ofNullable(this.world);
|
||||||
|
|
||||||
|
@ -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.InheritanceType;
|
||||||
import me.lucko.luckperms.api.nodetype.types.MetaType;
|
import me.lucko.luckperms.api.nodetype.types.MetaType;
|
||||||
import me.lucko.luckperms.api.nodetype.types.PrefixType;
|
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.SuffixType;
|
||||||
import me.lucko.luckperms.api.nodetype.types.WeightType;
|
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.node.factory.LegacyNodeFactory;
|
||||||
import me.lucko.luckperms.common.utils.PatternCache;
|
import me.lucko.luckperms.common.utils.PatternCache;
|
||||||
|
|
||||||
@ -43,8 +45,11 @@ import java.util.IdentityHashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class NodeTypes {
|
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 META_NODE_MARKER = META_KEY + ".";
|
||||||
public static final String WEIGHT_NODE_MARKER = WEIGHT_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 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
|
// used to split prefix/suffix/meta nodes
|
||||||
private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimiterPattern(".", "\\")).limit(2);
|
private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimiterPattern(".", "\\")).limit(2);
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public static Map<NodeTypeKey<?>, NodeType> parseTypes(String s) {
|
public static Map<NodeTypeKey<?>, NodeType> parseTypes(String s) {
|
||||||
Map<NodeTypeKey<?>, NodeType> results = new IdentityHashMap<>();
|
Map<NodeTypeKey<?>, NodeType> results = new IdentityHashMap<>();
|
||||||
|
|
||||||
@ -97,6 +105,11 @@ public final class NodeTypes {
|
|||||||
results.put(DisplayNameType.KEY, type);
|
results.put(DisplayNameType.KEY, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type = parseRegexType(s);
|
||||||
|
if (type != null) {
|
||||||
|
results.put(RegexType.KEY, type);
|
||||||
|
}
|
||||||
|
|
||||||
if (results.isEmpty()) {
|
if (results.isEmpty()) {
|
||||||
return ImmutableMap.of();
|
return ImmutableMap.of();
|
||||||
}
|
}
|
||||||
@ -104,7 +117,8 @@ public final class NodeTypes {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InheritanceType parseInheritanceType(String s) {
|
@Nullable
|
||||||
|
public static InheritanceType parseInheritanceType(String s) {
|
||||||
s = s.toLowerCase();
|
s = s.toLowerCase();
|
||||||
if (!s.startsWith(GROUP_NODE_MARKER)) {
|
if (!s.startsWith(GROUP_NODE_MARKER)) {
|
||||||
return null;
|
return null;
|
||||||
@ -114,7 +128,8 @@ public final class NodeTypes {
|
|||||||
return new Inheritance(groupName);
|
return new Inheritance(groupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MetaType parseMetaType(String s) {
|
@Nullable
|
||||||
|
public static MetaType parseMetaType(String s) {
|
||||||
if (!s.toLowerCase().startsWith(META_NODE_MARKER)) {
|
if (!s.toLowerCase().startsWith(META_NODE_MARKER)) {
|
||||||
return null;
|
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)) {
|
if (!s.toLowerCase().startsWith(PREFIX_NODE_MARKER)) {
|
||||||
return null;
|
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)) {
|
if (!s.toLowerCase().startsWith(SUFFIX_NODE_MARKER)) {
|
||||||
return null;
|
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();
|
String lower = s.toLowerCase();
|
||||||
if (!lower.startsWith(WEIGHT_NODE_MARKER)) {
|
if (!lower.startsWith(WEIGHT_NODE_MARKER)) {
|
||||||
return null;
|
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)) {
|
if (!s.toLowerCase().startsWith(DISPLAY_NAME_NODE_MARKER)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -198,6 +217,15 @@ public final class NodeTypes {
|
|||||||
return new DisplayName(s.substring(DISPLAY_NAME_NODE_MARKER.length()));
|
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 static final class Inheritance implements InheritanceType {
|
||||||
private final String groupName;
|
private final String groupName;
|
||||||
|
|
||||||
@ -456,6 +484,50 @@ public final class NodeTypes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class Regex extends Cache<PatternCache.CachedPattern> 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<Pattern> 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() {}
|
private NodeTypes() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ package me.lucko.luckperms.common.processors;
|
|||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
import me.lucko.luckperms.api.Tristate;
|
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.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -52,18 +53,17 @@ public class RegexProcessor extends AbstractPermissionProcessor implements Permi
|
|||||||
public void refresh() {
|
public void refresh() {
|
||||||
ImmutableMap.Builder<Pattern, Boolean> builder = ImmutableMap.builder();
|
ImmutableMap.Builder<Pattern, Boolean> builder = ImmutableMap.builder();
|
||||||
for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) {
|
for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) {
|
||||||
if (!e.getKey().startsWith("r=") && !e.getKey().startsWith("R=")) {
|
RegexType regexType = NodeTypes.parseRegexType(e.getKey());
|
||||||
|
if (regexType == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String pattern = e.getKey().substring(2);
|
Pattern pattern = regexType.getPattern().orElse(null);
|
||||||
Pattern p = PatternCache.compile(pattern);
|
if (pattern == null) {
|
||||||
|
|
||||||
if (p == null) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.put(p, e.getValue());
|
builder.put(pattern, e.getValue());
|
||||||
}
|
}
|
||||||
this.regexPermissions = builder.build();
|
this.regexPermissions = builder.build();
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ import java.util.Objects;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.regex.PatternSyntaxException;
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class PatternCache {
|
public final class PatternCache {
|
||||||
|
|
||||||
private static final LoadingCache<String, CachedPattern> CACHE = Caffeine.newBuilder()
|
private static final LoadingCache<String, CachedPattern> 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);
|
CachedPattern pattern = CACHE.get(regex);
|
||||||
Objects.requireNonNull(pattern, "pattern");
|
Objects.requireNonNull(pattern, "pattern");
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pattern compile(String regex) throws PatternSyntaxException {
|
||||||
|
CachedPattern pattern = lookup(regex);
|
||||||
if (pattern.ex != null) {
|
if (pattern.ex != null) {
|
||||||
throw pattern.ex;
|
throw pattern.ex;
|
||||||
} else {
|
} else {
|
||||||
@ -60,12 +67,12 @@ public final class PatternCache {
|
|||||||
* @param escape the string used to escape the delimiter where the pattern shouldn't match
|
* @param escape the string used to escape the delimiter where the pattern shouldn't match
|
||||||
* @return a pattern
|
* @return a pattern
|
||||||
*/
|
*/
|
||||||
public static Pattern compileDelimiterPattern(String delimiter, String escape) {
|
public static Pattern compileDelimiterPattern(String delimiter, String escape) throws PatternSyntaxException {
|
||||||
String pattern = "(?<!" + Pattern.quote(escape) + ")" + Pattern.quote(delimiter);
|
String pattern = "(?<!" + Pattern.quote(escape) + ")" + Pattern.quote(delimiter);
|
||||||
return compile(pattern);
|
return compile(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class CachedPattern {
|
public static final class CachedPattern {
|
||||||
private final Pattern instance;
|
private final Pattern instance;
|
||||||
private final PatternSyntaxException ex;
|
private final PatternSyntaxException ex;
|
||||||
|
|
||||||
@ -78,6 +85,16 @@ public final class PatternCache {
|
|||||||
this.instance = null;
|
this.instance = null;
|
||||||
this.ex = ex;
|
this.ex = ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Pattern getPattern() {
|
||||||
|
return this.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public PatternSyntaxException getException() {
|
||||||
|
return this.ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PatternCache() {}
|
private PatternCache() {}
|
||||||
|
Loading…
Reference in New Issue
Block a user