Implement permission system for commands

This commit is contained in:
Melledy
2023-10-09 07:43:51 -07:00
parent 6d3408f7c2
commit 94432f4222
14 changed files with 205 additions and 17 deletions

View File

@@ -1,5 +1,8 @@
package emu.lunarcore; package emu.lunarcore;
import java.util.HashSet;
import java.util.Set;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
@@ -15,6 +18,7 @@ public class Config {
public ServerConfig httpServer = new ServerConfig("127.0.0.1", 443); public ServerConfig httpServer = new ServerConfig("127.0.0.1", 443);
public GameServerConfig gameServer = new GameServerConfig("127.0.0.1", 23301); public GameServerConfig gameServer = new GameServerConfig("127.0.0.1", 23301);
public ServerOptions serverOptions = new ServerOptions();
public DownloadData downloadData = new DownloadData(); public DownloadData downloadData = new DownloadData();
public String resourceDir = "./resources"; public String resourceDir = "./resources";
@@ -70,6 +74,12 @@ public class Config {
} }
} }
@Getter
public static class ServerOptions {
public int entitySceneLimit = 2000;
public Set<String> defaultPermissions = Set.of("*");
}
@Getter @Getter
public static class DownloadData { public static class DownloadData {
public String assetBundleUrl = null; public String assetBundleUrl = null;

View File

@@ -10,4 +10,6 @@ public @interface Command {
public String[] aliases() default ""; public String[] aliases() default "";
public String desc() default ""; public String desc() default "";
public String permission() default "";
} }

View File

@@ -14,7 +14,7 @@ public class CommandArgs {
private int targetUid; private int targetUid;
private int level; private int level;
private int count; private int amount;
private int rank; private int rank;
private int promotion; private int promotion;
@@ -35,7 +35,7 @@ public class CommandArgs {
this.targetUid = Utils.parseSafeInt(arg.substring(1)); this.targetUid = Utils.parseSafeInt(arg.substring(1));
it.remove(); it.remove();
} else if (arg.startsWith("x")) { } else if (arg.startsWith("x")) {
this.count = Utils.parseSafeInt(arg.substring(1)); this.amount = Utils.parseSafeInt(arg.substring(1));
it.remove(); it.remove();
} else if (arg.startsWith("lv")) { } else if (arg.startsWith("lv")) {
this.level = Utils.parseSafeInt(arg.substring(2)); this.level = Utils.parseSafeInt(arg.substring(2));

View File

@@ -77,6 +77,22 @@ public class CommandManager {
return this; return this;
} }
private boolean checkPermission(Player sender, Command command) {
if (sender == null || command.permission().isEmpty()) {
return true;
}
return sender.getAccount().hasPermission(command.permission());
}
private boolean checkTargetPermission(Player sender, Command command) {
if (sender == null || command.permission().isEmpty()) {
return true;
}
return sender.getAccount().hasPermission("target." + command.permission());
}
public void invoke(Player sender, String message) { public void invoke(Player sender, String message) {
List<String> args = Arrays.stream(message.split(" ")).collect(Collectors.toCollection(ArrayList::new)); List<String> args = Arrays.stream(message.split(" ")).collect(Collectors.toCollection(ArrayList::new));
@@ -89,8 +105,24 @@ public class CommandManager {
// Get handler // Get handler
CommandHandler handler = this.commands.get(label); CommandHandler handler = this.commands.get(label);
// Execute
if (handler != null) { if (handler != null) {
handler.execute(sender, new CommandArgs(sender, args)); // Command annotation data
Command command = handler.getClass().getAnnotation(Command.class);
// Check permission
if (this.checkPermission(sender, command)) {
// Check targetted permission
CommandArgs cmdArgs = new CommandArgs(sender, args);
if (sender != cmdArgs.getTarget() && !this.checkTargetPermission(sender, command)) {
handler.sendMessage(sender, "You do not have permission to use this command on another player");
return;
}
// Run command
handler.execute(sender, cmdArgs);
} else {
handler.sendMessage(sender, "You do not have permission to use this command");
}
} else { } else {
if (sender != null) { if (sender != null) {
sender.sendMessage("Inavlid Command!"); sender.sendMessage("Inavlid Command!");

View File

@@ -7,7 +7,7 @@ import emu.lunarcore.game.account.AccountHelper;
import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils; import emu.lunarcore.util.Utils;
@Command(label = "account") @Command(label = "account", permission = "server.account")
public class AccountCommand implements CommandHandler { public class AccountCommand implements CommandHandler {
@Override @Override

View File

@@ -10,7 +10,7 @@ import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.inventory.ItemMainType; import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.Player;
@Command(label = "clear") @Command(label = "clear", permission = "player.clear")
public class ClearCommand implements CommandHandler { public class ClearCommand implements CommandHandler {
@Override @Override

View File

@@ -7,7 +7,7 @@ import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.player.PlayerGender; import emu.lunarcore.game.player.PlayerGender;
import emu.lunarcore.server.packet.send.PacketGetHeroBasicTypeInfoScRsp; import emu.lunarcore.server.packet.send.PacketGetHeroBasicTypeInfoScRsp;
@Command(label = "gender") @Command(label = "gender", permission = "player.gender")
public class GenderCommand implements CommandHandler { public class GenderCommand implements CommandHandler {
@Override @Override

View File

@@ -13,7 +13,7 @@ import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.inventory.ItemSubType; import emu.lunarcore.game.inventory.ItemSubType;
import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.Player;
@Command(label = "giveall", aliases = {"ga"}) @Command(label = "giveall", aliases = {"ga"}, permission = "player.give")
public class GiveAllCommand implements CommandHandler { public class GiveAllCommand implements CommandHandler {
@Override @Override

View File

@@ -12,7 +12,7 @@ import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils; import emu.lunarcore.util.Utils;
@Command(label = "give", aliases = {"g"}) @Command(label = "give", aliases = {"g"}, permission = "player.give")
public class GiveCommand implements CommandHandler { public class GiveCommand implements CommandHandler {
@Override @Override
@@ -24,7 +24,7 @@ public class GiveCommand implements CommandHandler {
} }
int itemId = Utils.parseSafeInt(args.get(0)); int itemId = Utils.parseSafeInt(args.get(0));
int amount = Math.max(args.getCount(), 1); int amount = Math.max(args.getAmount(), 1);
ItemExcel itemData = GameData.getItemExcelMap().get(itemId); ItemExcel itemData = GameData.getItemExcelMap().get(itemId);

View File

@@ -0,0 +1,50 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
@Command(label = "permission", aliases = {"perm"}, permission = "server.permission")
public class PermissionCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
} else if (args.size() < 2) {
this.sendMessage(sender, "Error: Not enough arguments");
return;
}
String type = args.get(0).toLowerCase();
String permission = args.get(1).toLowerCase();
switch (type) {
case "add" -> {
// Add permission
args.getTarget().getAccount().addPermission(permission);
// Send message
this.sendMessage(sender, "Added permission for " + args.getTarget().getName());
}
case "remove" -> {
// Remove permission
args.getTarget().getAccount().removePermission(permission);
// Send message
this.sendMessage(sender, "Removed permission for " + args.getTarget().getName());
}
case "clear" -> {
// Clear permissions
args.getTarget().getAccount().clearPermission();
// Send message
this.sendMessage(sender, "Cleared permissions for " + args.getTarget().getName());
}
default -> {
this.sendMessage(sender, "Error: Invalid argument");
}
}
}
}

View File

@@ -6,7 +6,7 @@ import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler; import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.Player;
@Command(label = "reload") @Command(label = "reload", permission = "server.reload")
public class ReloadCommand implements CommandHandler { public class ReloadCommand implements CommandHandler {
@Override @Override

View File

@@ -11,7 +11,7 @@ import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.util.Position; import emu.lunarcore.util.Position;
import emu.lunarcore.util.Utils; import emu.lunarcore.util.Utils;
@Command(label = "spawn") @Command(label = "spawn", permission = "player.spawn")
public class SpawnCommand implements CommandHandler { public class SpawnCommand implements CommandHandler {
@Override @Override
@@ -32,7 +32,7 @@ public class SpawnCommand implements CommandHandler {
// Get id // Get id
int id = Utils.parseSafeInt(args.get(0)); int id = Utils.parseSafeInt(args.get(0));
int stage = Math.max(Utils.parseSafeInt(args.get(1)), 1); int stage = Math.max(Utils.parseSafeInt(args.get(1)), 1);
int amount = Math.max(args.getCount(), 1); int amount = Math.max(args.getAmount(), 1);
int radius = Math.max(args.getRank(), 5) * 1000; int radius = Math.max(args.getRank(), 5) * 1000;
// Spawn monster // Spawn monster

View File

@@ -6,7 +6,7 @@ import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils; import emu.lunarcore.util.Utils;
@Command(label = "worldlevel", aliases = {"wl"}) @Command(label = "worldlevel", aliases = {"wl"}, permission = "player.worldlevel")
public class WorldLevelCommand implements CommandHandler { public class WorldLevelCommand implements CommandHandler {
@Override @Override

View File

@@ -1,5 +1,8 @@
package emu.lunarcore.game.account; package emu.lunarcore.game.account;
import java.util.*;
import java.util.stream.Stream;
import dev.morphia.annotations.*; import dev.morphia.annotations.*;
import emu.lunarcore.LunarRail; import emu.lunarcore.LunarRail;
import emu.lunarcore.util.Crypto; import emu.lunarcore.util.Crypto;
@@ -21,6 +24,8 @@ public class Account {
private String comboToken; // Combo token private String comboToken; // Combo token
private String dispatchToken; // Session token for dispatch server private String dispatchToken; // Session token for dispatch server
private Set<String> permissions;
@Deprecated @Deprecated
public Account() { public Account() {
@@ -30,6 +35,7 @@ public class Account {
public Account(String username) { public Account(String username) {
this.uid = Long.toString(Snowflake32.newUid()); this.uid = Long.toString(Snowflake32.newUid());
this.username = username; this.username = username;
this.permissions = new HashSet<>();
} }
public String getEmail() { public String getEmail() {
@@ -39,20 +45,108 @@ public class Account {
public void setReservedPlayerUid(int uid) { public void setReservedPlayerUid(int uid) {
this.reservedPlayerUid = uid; this.reservedPlayerUid = uid;
} }
// Permissions
public Set<String> getPermissions() {
if (this.permissions == null) {
this.permissions = new HashSet<>();
this.save();
}
return this.permissions;
}
public boolean addPermission(String permission) {
if (this.getPermissions().contains(permission)) {
return false;
}
this.getPermissions().add(permission);
this.save();
return true;
}
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
String[] wildcardParts = wildcard.split("\\.");
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
return false;
}
for (int i = 0; i < wildcardParts.length; i++) {
switch (wildcardParts[i]) {
case "**": // Recursing match
return true;
case "*": // Match only one layer
if (i >= (permissionParts.length-1)) {
return true;
}
break;
default: // This layer isn't a wildcard, it needs to match exactly
if (!wildcardParts[i].equals(permissionParts[i])) {
return false;
}
}
}
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
return wildcardParts.length == permissionParts.length;
}
public boolean hasPermission(String permission) {
// Skip if permission isnt required
if (permission.isEmpty()) {
return true;
}
// Default permissions
var defaultPermissions = LunarRail.getConfig().getServerOptions().getDefaultPermissions();
if (defaultPermissions.contains("*")) {
return true;
}
// Add default permissions if it doesn't exist
List<String> permissions = Stream.of(this.getPermissions(), defaultPermissions)
.flatMap(Collection::stream)
.distinct().toList();
if (permissions.contains(permission)) {
return true;
}
String[] permissionParts = permission.split("\\.");
for (String p : permissions) {
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
if (permissionMatchesWildcard(p, permissionParts)) return true;
}
return permissions.contains("*");
}
public boolean removePermission(String permission) {
boolean res = this.getPermissions().remove(permission);
if (res) this.save();
return res;
}
public void clearPermission() {
this.getPermissions().clear();
this.save();
}
// Tokens
// TODO make unique
public String generateComboToken() { public String generateComboToken() {
this.comboToken = Utils.bytesToHex(Crypto.createSessionKey(32)); this.comboToken = Utils.bytesToHex(Crypto.createSessionKey(32)); // TODO make unique
this.save(); this.save();
return this.comboToken; return this.comboToken;
} }
// TODO make unique
public String generateDispatchToken() { public String generateDispatchToken() {
this.dispatchToken = Utils.bytesToHex(Crypto.createSessionKey(32)); this.dispatchToken = Utils.bytesToHex(Crypto.createSessionKey(32)); // TODO make unique
this.save(); this.save();
return this.dispatchToken; return this.dispatchToken;
} }
// Database
public void save() { public void save() {
LunarRail.getAccountDatabase().save(this); LunarRail.getAccountDatabase().save(this);