mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-06-26 06:24:51 +02:00
Refactor command filter into a unit tested class.
Also adds support for regex \s as the subcommand delimiter.
This commit is contained in:
parent
abeab91be4
commit
cd221ea19c
|
@ -25,7 +25,9 @@
|
|||
<allow pkg="java"/>
|
||||
<allow pkg="javax"/>
|
||||
<allow pkg="org.junit"/>
|
||||
<allow pkg="junit"/>
|
||||
<allow pkg="org.mockito"/>
|
||||
<allow pkg="org.hamcrest"/>
|
||||
<allow pkg="com.sk89q"/>
|
||||
<allow pkg="org.enginehub"/>
|
||||
<allow pkg="org.bukkit"/>
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.command.CommandFilter;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
|
@ -1443,91 +1444,12 @@ public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
|
|||
RegionManager mgr = plugin.getGlobalRegionManager().get(world);
|
||||
ApplicableRegionSet set = mgr.getApplicableRegions(pt);
|
||||
|
||||
String usedCommand = event.getMessage().toLowerCase();
|
||||
|
||||
Set<String> allowedCommands = set.getFlag(DefaultFlag.ALLOWED_CMDS, localPlayer);
|
||||
Set<String> blockedCommands = set.getFlag(DefaultFlag.BLOCKED_CMDS, localPlayer);
|
||||
CommandFilter test = new CommandFilter(allowedCommands, blockedCommands);
|
||||
|
||||
/*
|
||||
* blocked used allow?
|
||||
* x x no
|
||||
* x x y no
|
||||
* x y x yes
|
||||
* x y x y no
|
||||
*
|
||||
* allowed used allow?
|
||||
* x x yes
|
||||
* x x y yes
|
||||
* x y x no
|
||||
* x y x y yes
|
||||
*/
|
||||
String result = "";
|
||||
String[] usedParts = usedCommand.split(" ");
|
||||
if (blockedCommands != null) {
|
||||
blocked:
|
||||
for (String blockedCommand : blockedCommands) {
|
||||
String[] blockedParts = blockedCommand.split(" ");
|
||||
for (int i = 0; i < blockedParts.length && i < usedParts.length; i++) {
|
||||
if (blockedParts[i].equalsIgnoreCase(usedParts[i])) {
|
||||
// first part matches - check if it's the whole thing
|
||||
if (i + 1 == blockedParts.length) {
|
||||
// consumed all blocked parts, block entire command
|
||||
result = blockedCommand;
|
||||
break blocked;
|
||||
} else {
|
||||
// more blocked parts to check, also check used length
|
||||
if (i + 1 == usedParts.length) {
|
||||
// all that was used, but there is more in blocked
|
||||
// allow this, check next command in flag
|
||||
continue blocked;
|
||||
} else {
|
||||
// more in both blocked and used, continue checking
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// found non-matching part, stop checking this command
|
||||
continue blocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedCommands != null) {
|
||||
allowed:
|
||||
for (String allowedCommand : allowedCommands) {
|
||||
String[] allowedParts = allowedCommand.split(" ");
|
||||
for (int i = 0; i < allowedParts.length && i < usedParts.length; i++) {
|
||||
if (allowedParts[i].equalsIgnoreCase(usedParts[i])) {
|
||||
// this part matches - check if it's the whole thing
|
||||
if (i + 1 == allowedParts.length) {
|
||||
// consumed all allowed parts before reaching used length
|
||||
// this command is definitely allowed
|
||||
result = "";
|
||||
break allowed;
|
||||
} else {
|
||||
// more allowed parts to check
|
||||
if (i + 1 == usedParts.length) {
|
||||
// all that was used, but there is more in allowed
|
||||
// block for now, check next part of flag
|
||||
result = usedCommand;
|
||||
continue allowed;
|
||||
} else {
|
||||
// more in both allowed and used, continue checking for match
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// doesn't match at all, block it, check next flag string
|
||||
result = usedCommand;
|
||||
continue allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + result + " is not allowed in this area.");
|
||||
if (!test.apply(event.getMessage())) {
|
||||
player.sendMessage(ChatColor.RED + event.getMessage() + " is not allowed in this area.");
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldGuard team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.util.command;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Checks whether a command is permitted with support for subcommands
|
||||
* split by {@code \s} (regular expressions).
|
||||
*
|
||||
* <p>{@code permitted} always overrides {@code denied} (unlike other parts of
|
||||
* WorldGuard). Either can be null. If both are null, then every command is
|
||||
* permitted. If only {@code permitted} is null, then all commands but
|
||||
* those in the list of denied are permitted. If only {@code denied} is null,
|
||||
* then only commands in the list of permitted are permitted. If neither are
|
||||
* null, only permitted commands are permitted and the list of denied commands
|
||||
* is not used.</p>
|
||||
*
|
||||
* <p>The test is case in-sensitive.</p>
|
||||
*/
|
||||
public class CommandFilter implements Predicate<String> {
|
||||
|
||||
@Nullable
|
||||
private final Collection<String> permitted;
|
||||
@Nullable
|
||||
private final Collection<String> denied;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param permitted a list of rules for permitted commands
|
||||
* @param denied a list of rules for denied commands
|
||||
*/
|
||||
public CommandFilter(@Nullable Collection<String> permitted, @Nullable Collection<String> denied) {
|
||||
this.permitted = permitted;
|
||||
this.denied = denied;
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
@Override
|
||||
public boolean apply(String command) {
|
||||
command = command.toLowerCase();
|
||||
|
||||
/*
|
||||
* denied used allow?
|
||||
* x x no
|
||||
* x x y no
|
||||
* x y x yes
|
||||
* x y x y no
|
||||
*
|
||||
* permitted used allow?
|
||||
* x x yes
|
||||
* x x y yes
|
||||
* x y x no
|
||||
* x y x y yes
|
||||
*/
|
||||
String result = "";
|
||||
String[] usedParts = command.split("\\s+");
|
||||
if (denied != null) {
|
||||
denied:
|
||||
for (String deniedCommand : denied) {
|
||||
String[] deniedParts = deniedCommand.split("\\s+");
|
||||
for (int i = 0; i < deniedParts.length && i < usedParts.length; i++) {
|
||||
if (deniedParts[i].equalsIgnoreCase(usedParts[i])) {
|
||||
// first part matches - check if it's the whole thing
|
||||
if (i + 1 == deniedParts.length) {
|
||||
// consumed all denied parts, block entire command
|
||||
result = deniedCommand;
|
||||
break denied;
|
||||
} else {
|
||||
// more denied parts to check, also check used length
|
||||
if (i + 1 == usedParts.length) {
|
||||
// all that was used, but there is more in denied
|
||||
// allow this, check next command in flag
|
||||
continue denied;
|
||||
} else {
|
||||
// more in both denied and used, continue checking
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// found non-matching part, stop checking this command
|
||||
continue denied;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (permitted != null) {
|
||||
permitted:
|
||||
for (String permittedCommand : permitted) {
|
||||
String[] permittedParts = permittedCommand.split("\\s+");
|
||||
for (int i = 0; i < permittedParts.length && i < usedParts.length; i++) {
|
||||
if (permittedParts[i].equalsIgnoreCase(usedParts[i])) {
|
||||
// this part matches - check if it's the whole thing
|
||||
if (i + 1 == permittedParts.length) {
|
||||
// consumed all permitted parts before reaching used length
|
||||
// this command is definitely permitted
|
||||
result = "";
|
||||
break permitted;
|
||||
} else {
|
||||
// more permitted parts to check
|
||||
if (i + 1 == usedParts.length) {
|
||||
// all that was used, but there is more in permitted
|
||||
// block for now, check next part of flag
|
||||
result = command;
|
||||
continue permitted;
|
||||
} else {
|
||||
// more in both permitted and used, continue checking for match
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// doesn't match at all, block it, check next flag string
|
||||
result = command;
|
||||
continue permitted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@code CommandFilter}.
|
||||
*
|
||||
* <p>If {@link #permit(String...)} is never called, then the
|
||||
* permitted rule list will be {@code null}. Likewise if
|
||||
* {@link #deny(String...)} is never called.</p>
|
||||
*/
|
||||
public static class Builder {
|
||||
private Set<String> permitted;
|
||||
private Set<String> denied;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit the given list of commands.
|
||||
*
|
||||
* @param rules list of commands
|
||||
* @return the builder object
|
||||
*/
|
||||
public Builder permit(String ... rules) {
|
||||
checkNotNull(rules);
|
||||
if (permitted == null) {
|
||||
permitted = new HashSet<String>();
|
||||
}
|
||||
permitted.addAll(Arrays.asList(rules));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deny the given list of commands.
|
||||
*
|
||||
* @param rules list of commands
|
||||
* @return the builder object
|
||||
*/
|
||||
public Builder deny(String ... rules) {
|
||||
checkNotNull(rules);
|
||||
if (denied == null) {
|
||||
denied = new HashSet<String>();
|
||||
}
|
||||
denied.addAll(Arrays.asList(rules));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a command filter.
|
||||
*
|
||||
* @return a new command filter
|
||||
*/
|
||||
public CommandFilter build() {
|
||||
return new CommandFilter(
|
||||
permitted != null ? new HashSet<String>(permitted) : null,
|
||||
denied != null ? new HashSet<String>(denied) : null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
196
src/test/java/com/sk89q/worldguard/util/CommandFilterTest.java
Normal file
196
src/test/java/com/sk89q/worldguard/util/CommandFilterTest.java
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldGuard team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.util;
|
||||
|
||||
import com.sk89q.worldguard.util.command.CommandFilter;
|
||||
import com.sk89q.worldguard.util.command.CommandFilter.Builder;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class CommandFilterTest extends TestCase {
|
||||
|
||||
private static final String[] COMMAND_SEPARATORS = new String[] {" ", " ", "\t", " \t", "\n", "\r\n"};
|
||||
|
||||
public void testApply() throws Exception {
|
||||
CommandFilter filter;
|
||||
|
||||
// ====================================================================
|
||||
// No rules
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder().build();
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/deny1", true);
|
||||
assertSubcommands(filter, "/other", true);
|
||||
|
||||
// ====================================================================
|
||||
// Root PERMIT
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.permit("/permit1", "/permit2")
|
||||
.build();
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/permit2", true);
|
||||
assertSubcommands(filter, "/other", false);
|
||||
|
||||
// ====================================================================
|
||||
// Root DENY
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.deny("/deny1", "/deny2")
|
||||
.build();
|
||||
assertSubcommands(filter, "/deny1", false);
|
||||
assertSubcommands(filter, "/deny2", false);
|
||||
assertSubcommands(filter, "/other", true);
|
||||
|
||||
// ====================================================================
|
||||
// Root PERMIT + DENY no overlap
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.permit("/permit1", "/permit2")
|
||||
.deny("/deny1", "/deny2")
|
||||
.build();
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/deny1", false);
|
||||
assertSubcommands(filter, "/deny2", false);
|
||||
assertSubcommands(filter, "/other", false);
|
||||
|
||||
// ====================================================================
|
||||
// Root PERMIT + DENY WITH overlap
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.permit("/permit1", "/permit2", "/strange")
|
||||
.deny("/deny1", "/deny2", "/strange")
|
||||
.build();
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/deny1", false);
|
||||
assertSubcommands(filter, "/deny2", false);
|
||||
assertSubcommands(filter, "/strange", true);
|
||||
assertSubcommands(filter, "/other", false);
|
||||
|
||||
// ====================================================================
|
||||
// Subcommand PERMIT
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.permit("/permit1", "/parent permit1", "/parent between subpermit1")
|
||||
.build();
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/parent", false);
|
||||
assertSubcommands(filter, "/parent permit1", true);
|
||||
assertSubcommands(filter, "/parent other", false);
|
||||
assertSubcommands(filter, "/parent between", false);
|
||||
assertSubcommands(filter, "/parent between subpermit1", true);
|
||||
assertSubcommands(filter, "/parent between other", false);
|
||||
assertSubcommands(filter, "/parent between other subpermit1", false);
|
||||
assertSubcommands(filter, "/other", false);
|
||||
assertSubcommands(filter, "/other permit1", false);
|
||||
assertSubcommands(filter, "/other between", false);
|
||||
assertSubcommands(filter, "/other between subpermit1", false);
|
||||
|
||||
// ====================================================================
|
||||
// Mixed DENY
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.deny("/deny1", "/parent deny1", "/parent between subdeny1")
|
||||
.build();
|
||||
assertSubcommands(filter, "/deny1", false);
|
||||
assertSubcommands(filter, "/parent", true);
|
||||
assertSubcommands(filter, "/parent deny1", false);
|
||||
assertSubcommands(filter, "/parent between", true);
|
||||
assertSubcommands(filter, "/parent between subdeny1", false);
|
||||
assertSubcommands(filter, "/parent between else", true);
|
||||
assertSubcommands(filter, "/parent else", true);
|
||||
assertSubcommands(filter, "/other", true);
|
||||
assertSubcommands(filter, "/other deny1", true);
|
||||
assertSubcommands(filter, "/other between", true);
|
||||
assertSubcommands(filter, "/other between subdeny1", true);
|
||||
assertSubcommands(filter, "/other between else", true);
|
||||
|
||||
// ====================================================================
|
||||
// Mixed PERMIT + DENY no overlap
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.deny("/deny1", "/denyparent deny1", "/denyparent between subdeny1")
|
||||
.permit("/permit1", "/permitparent permit1", "/permitparent between subpermit1")
|
||||
.build();
|
||||
assertSubcommands(filter, "/deny1", false);
|
||||
assertSubcommands(filter, "/denyparent", false);
|
||||
assertSubcommands(filter, "/denyparent deny1", false);
|
||||
assertSubcommands(filter, "/denyparent else", false);
|
||||
assertSubcommands(filter, "/denyparent between", false);
|
||||
assertSubcommands(filter, "/denyparent between subdeny1", false);
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/permitparent", false);
|
||||
assertSubcommands(filter, "/permitparent permit1", true);
|
||||
assertSubcommands(filter, "/permitparent else", false);
|
||||
assertSubcommands(filter, "/permitparent between", false);
|
||||
assertSubcommands(filter, "/permitparent between subpermit1", true);
|
||||
assertSubcommands(filter, "/other", false);
|
||||
assertSubcommands(filter, "/other permit1", false);
|
||||
assertSubcommands(filter, "/other between", false);
|
||||
assertSubcommands(filter, "/other between subpermit1", false);
|
||||
|
||||
// ====================================================================
|
||||
// Mixed PERMIT + DENY overlap
|
||||
// ====================================================================
|
||||
|
||||
filter = new Builder()
|
||||
.deny("/deny1", "/parent deny1", "/parent between subdeny1", "/parent between", "/parent between strange", "/parent between strange sub")
|
||||
.permit("/permit1", "/parent permit1", "/parent between sub", "/parent between strange")
|
||||
.build();
|
||||
assertSubcommands(filter, "/deny1", false);
|
||||
assertSubcommands(filter, "/parent", false);
|
||||
assertSubcommands(filter, "/parent deny1", false);
|
||||
assertSubcommands(filter, "/parent else", false);
|
||||
assertSubcommands(filter, "/parent permit1", true);
|
||||
assertSubcommands(filter, "/parent between", false);
|
||||
assertSubcommands(filter, "/parent between sub", true);
|
||||
assertSubcommands(filter, "/parent between other", false);
|
||||
assertSubcommands(filter, "/parent between strange", true);
|
||||
assertSubcommands(filter, "/parent between strange sub", true);
|
||||
assertSubcommands(filter, "/parent between strange other", true);
|
||||
assertSubcommands(filter, "/permit1", true);
|
||||
assertSubcommands(filter, "/permit1 deny1", true);
|
||||
assertSubcommands(filter, "/other", false);
|
||||
assertSubcommands(filter, "/other permit1", false);
|
||||
assertSubcommands(filter, "/other between", false);
|
||||
assertSubcommands(filter, "/other between subpermit1", false);
|
||||
}
|
||||
|
||||
private void assertSubcommands(CommandFilter filter, final String root, boolean expected) {
|
||||
for (String separator : COMMAND_SEPARATORS) {
|
||||
assertThat(filter.apply(root.replaceAll(" ", separator)), is(expected));
|
||||
assertThat(filter.apply((root + " _subcmd").replaceAll(" ", separator)), is(expected));
|
||||
assertThat(filter.apply((root + " _subcmd _another").replaceAll(" ", separator)), is(expected));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user