diff --git a/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java index 323b5bcd..9eddcdc9 100644 --- a/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java @@ -22,7 +22,9 @@ import com.xpdustry.distributor.command.cloud.parser.PlayerParser; import com.xpdustry.distributor.command.cloud.parser.TeamParser; import com.xpdustry.distributor.command.cloud.specifier.AllTeams; +import com.xpdustry.distributor.core.DistributorProvider; import com.xpdustry.distributor.core.command.CommandSender; +import com.xpdustry.distributor.core.permission.PermissionManager; import com.xpdustry.distributor.core.plugin.MindustryPlugin; import com.xpdustry.distributor.core.plugin.PluginAware; import io.leangen.geantyref.TypeToken; @@ -105,7 +107,16 @@ public ArcCommandManager( @Override public boolean hasPermission(final @NonNull C sender, final String permission) { - return permission.isEmpty() || senderMapper().reverse(sender).isServer(); // TODO Add permission + if (permission.isEmpty()) { + return true; + } + final var reversed = senderMapper().reverse(sender); + return reversed.isServer() + || DistributorProvider.get() + .getService(PermissionManager.class) + .orElseThrow() + .getPermission(reversed.getPlayer(), permission) + .asBoolean(); } @Override diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/ImmutablePermissionTree.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/ImmutablePermissionTree.java new file mode 100644 index 00000000..6f59033d --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/ImmutablePermissionTree.java @@ -0,0 +1,45 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.permission; + +import java.util.Map; + +final class ImmutablePermissionTree implements PermissionTree { + + private final PermissionTree inner; + + ImmutablePermissionTree(final PermissionTree inner) { + this.inner = inner; + } + + @Override + public TriState getPermission(final String permission) { + return inner.getPermission(permission); + } + + @Override + public void setPermission(final String permission, final TriState state) { + throw new UnsupportedOperationException(); + } + + @Override + public Map getPermissions() { + return inner.getPermissions(); + } +} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/PermissionManager.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/PermissionManager.java new file mode 100644 index 00000000..c93ee03f --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/PermissionManager.java @@ -0,0 +1,47 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.permission; + +import java.util.regex.Pattern; +import mindustry.gen.Player; + +public interface PermissionManager { + + /** + * Regex pattern used to validate permission strings. + *

+ * Notes: + *

+ * Permission strings are composed of a series of nodes separated by dots with alphanumeric characters and minus + * signs, such as {@code "plugin.command"}. + *
+ * A parent node is always overridden by a child node, such as {@code "plugin.command"} overriding {@code "plugin"}. + *
+ * Wildcards are also allowed, but they currently have the same effect as a normal node, such as {@code "plugin.command.*"} equals {@code "plugin.command"}. + *
+ * The only relevant use of wildcards is the root permission {@code "*"} permission. It + * allows you to set a default value for all permissions. + *
+ */ + String PERMISSION_REGEX = "^(\\*|[a-z\\d\\-]+)(\\.(\\*|[a-z\\d\\-]+))*$"; + + Pattern PERMISSION_PATTERN = Pattern.compile(PERMISSION_REGEX); + + TriState getPermission(final Player player, final String permission); +} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/PermissionTree.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/PermissionTree.java new file mode 100644 index 00000000..ac397ed7 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/PermissionTree.java @@ -0,0 +1,38 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.permission; + +import java.util.Map; + +public interface PermissionTree { + + static PermissionTree simple() { + return new SimplePermissionTree(); + } + + static PermissionTree immutable(final PermissionTree tree) { + return (tree instanceof ImmutablePermissionTree) ? tree : new ImmutablePermissionTree(tree); + } + + TriState getPermission(final String permission); + + void setPermission(final String permission, final TriState state); + + Map getPermissions(); +} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/SimplePermissionTree.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/SimplePermissionTree.java new file mode 100644 index 00000000..5da8f915 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/SimplePermissionTree.java @@ -0,0 +1,121 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.permission; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.jspecify.annotations.Nullable; + +final class SimplePermissionTree implements PermissionTree { + + private final @Nullable SimplePermissionTree parent; + private final Map children = new HashMap<>(); + private TriState value = TriState.UNDEFINED; + + SimplePermissionTree() { + this.parent = null; + } + + private SimplePermissionTree(final @Nullable SimplePermissionTree parent) { + this.parent = parent; + } + + public TriState getPermission(final String permission) { + if (!PermissionManager.PERMISSION_PATTERN.matcher(permission).matches()) { + throw new IllegalArgumentException("The permission doesn't match the regex: " + permission); + } + var state = TriState.UNDEFINED; + var node = this; + for (final var part : permission.split("\\.", -1)) { + if (node.children.containsKey("*") && node.children.get("*").value != TriState.UNDEFINED) { + state = node.children.get("*").value; + } + node = node.children.get(part); + if (node == null) { + return state; + } else if (node.value != TriState.UNDEFINED) { + state = node.value; + } + } + return state; + } + + public void setPermission(final String permission, final TriState state) { + if (!PermissionManager.PERMISSION_PATTERN.matcher(permission).matches()) { + throw new IllegalArgumentException("The permission doesn't match the regex: " + permission); + } + final var parts = permission.split("\\.", -1); + var node = this; + if (state != TriState.UNDEFINED) { + for (final var part : parts) { + final var parent = node; + node = node.children.computeIfAbsent(part, k -> new SimplePermissionTree(parent)); + } + node.value = state; + } else { + for (final var part : parts) { + node = node.children.get(part); + if (node == null) { + return; + } + } + node.value = state; + var index = parts.length - 1; + while (node.parent != null && node.children.isEmpty()) { + node = node.parent; + node.children.remove(parts[index--]); + } + } + } + + public Map getPermissions() { + final Map permissions = new HashMap<>(); + for (final var child : this.children.entrySet()) { + if (child.getValue().value != TriState.UNDEFINED) { + permissions.put(child.getKey(), child.getValue().value.asBoolean()); + } + for (final var entry : child.getValue().getPermissions().entrySet()) { + permissions.put(child.getKey() + "." + entry.getKey(), entry.getValue()); + } + } + return Collections.unmodifiableMap(permissions); + } + + @Override + public boolean equals(final @Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof final SimplePermissionTree that)) { + return false; + } + if (!this.children.equals(that.children)) { + return false; + } + return this.value == that.value; + } + + @Override + public int hashCode() { + int result = this.children.hashCode(); + result = 31 * result + this.value.hashCode(); + return result; + } +} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/TriState.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/TriState.java new file mode 100644 index 00000000..6e0c1de0 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/TriState.java @@ -0,0 +1,44 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.permission; + +import org.jspecify.annotations.Nullable; + +/** + * A ternary boolean type, used by the permission system. + */ +public enum TriState { + FALSE(false), + TRUE(true), + UNDEFINED(false); + + private final boolean value; + + TriState(final boolean value) { + this.value = value; + } + + public static TriState of(final @Nullable Boolean state) { + return state == null ? UNDEFINED : state ? TRUE : FALSE; + } + + public boolean asBoolean() { + return this.value; + } +} diff --git a/distributor-permission-rank/build.gradle.kts b/distributor-permission-rank/build.gradle.kts new file mode 100644 index 00000000..e3f9ce9d --- /dev/null +++ b/distributor-permission-rank/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("distributor4.base-conventions") + id("distributor4.mindustry-conventions") +} + +module { + identifier = "distributor-permission" + display = "DistributorLoggerSimple" + main = "com.xpdustry.distributor.logger.simple.DistributorLoggerPlugin" + description = "Simple permission system based on linear ranks." +} + +dependencies { + compileOnly(project(":distributor-common")) +} diff --git a/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/LinearEnumRankNode.java b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/LinearEnumRankNode.java new file mode 100644 index 00000000..5a1ad78a --- /dev/null +++ b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/LinearEnumRankNode.java @@ -0,0 +1,46 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.permission; + +import java.util.function.Function; +import org.jspecify.annotations.Nullable; + +record LinearEnumRankNode>(E value, Function nameProvider, boolean ascending) + implements RankNode { + + @Override + public String getName() { + return nameProvider.apply(value); + } + + @Override + public @Nullable RankNode getPrevious() { + @SuppressWarnings("unchecked") + final var constants = (E[]) value.getClass().getEnumConstants(); + if (ascending) { + return (value.ordinal() > 0) + ? new LinearEnumRankNode<>(constants[value.ordinal() - 1], nameProvider, true) + : null; + } else { + return (value.ordinal() + 1 < constants.length) + ? new LinearEnumRankNode<>(constants[value.ordinal() + 1], nameProvider, false) + : null; + } + } +} diff --git a/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankNode.java b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankNode.java new file mode 100644 index 00000000..98ef4f62 --- /dev/null +++ b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankNode.java @@ -0,0 +1,34 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.permission; + +import java.util.function.Function; +import org.jspecify.annotations.Nullable; + +public interface RankNode { + + static > RankNode linearEnum( + final E value, final Function nameProvider, boolean ascending) { + return new LinearEnumRankNode<>(value, nameProvider, ascending); + } + + String getName(); + + @Nullable RankNode getPrevious(); +} diff --git a/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankPermissionStorage.java b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankPermissionStorage.java new file mode 100644 index 00000000..332b642e --- /dev/null +++ b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankPermissionStorage.java @@ -0,0 +1,26 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.permission; + +import com.xpdustry.distributor.core.permission.PermissionTree; + +public interface RankPermissionStorage { + + PermissionTree getRankPermissions(final RankNode node); +} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/Permissible.java b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankProvider.java similarity index 79% rename from distributor-common/src/main/java/com/xpdustry/distributor/core/permission/Permissible.java rename to distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankProvider.java index ba37c29b..ea3d2c72 100644 --- a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/Permissible.java +++ b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/RankProvider.java @@ -16,6 +16,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.xpdustry.distributor.core.permission; +package com.xpdustry.distributor.permission; -public interface Permissible {} +import java.util.Collection; +import mindustry.gen.Player; + +public interface RankProvider { + + Collection getRanks(final Player player); +} diff --git a/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/SimpleRankPermissionManager.java b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/SimpleRankPermissionManager.java new file mode 100644 index 00000000..774622a4 --- /dev/null +++ b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/SimpleRankPermissionManager.java @@ -0,0 +1,49 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.permission; + +import com.xpdustry.distributor.core.permission.PermissionManager; +import com.xpdustry.distributor.core.permission.TriState; +import mindustry.gen.Player; + +final class SimpleRankPermissionManager implements PermissionManager { + + private RankProvider provider; + private RankPermissionStorage storage; + + SimpleRankPermissionManager(final RankProvider provider, final RankPermissionStorage storage) { + this.provider = provider; + this.storage = storage; + } + + @Override + public TriState getPermission(final Player player, final String permission) { + for (final var node : this.provider.getRanks(player)) { + RankNode current = node; + while (current != null) { + final var state = this.storage.getRankPermissions(current).getPermission(permission); + if (state != TriState.UNDEFINED) { + return state; + } + current = node.getPrevious(); + } + } + return TriState.UNDEFINED; + } +} diff --git a/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/package-info.java b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/package-info.java new file mode 100644 index 00000000..6414c90d --- /dev/null +++ b/distributor-permission-rank/src/main/java/com/xpdustry/distributor/permission/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.permission; + +import org.jspecify.annotations.NullMarked; diff --git a/settings.gradle.kts b/settings.gradle.kts index eceb13f3..d9ce1fee 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,3 +4,4 @@ includeBuild("distributor-build-logic") include(":distributor-logging-simple") include(":distributor-common") include(":distributor-command-cloud") +include(":distributor-permission-rank")