From 2f5f696b685bafc3cc534a04f58dec13dd430361 Mon Sep 17 00:00:00 2001 From: JTK222 Date: Mon, 20 Dec 2021 19:34:57 +0100 Subject: [PATCH] Redo of the whole PermissionAPI (#7780) Co-authored-by: LexManos --- .../minecraftforge/common/ForgeConfig.java | 9 + .../server/ServerLifecycleHooks.java | 3 + .../permission/DefaultPermissionHandler.java | 94 --------- .../server/permission/IPermissionHandler.java | 51 ----- .../server/permission/PermissionAPI.java | 163 ++++++++++----- .../permission/context/AreaContext.java | 51 ----- .../permission/context/BlockPosContext.java | 80 -------- .../server/permission/context/Context.java | 87 -------- .../server/permission/context/ContextKey.java | 67 ------ .../permission/context/ContextKeys.java | 46 ----- .../server/permission/context/IContext.java | 56 ----- .../permission/context/PlayerContext.java | 46 ----- .../permission/context/TargetContext.java | 49 ----- .../permission/context/WorldContext.java | 49 ----- .../events/PermissionGatherEvent.java | 109 ++++++++++ .../UnregisteredPermissionException.java} | 30 +-- .../handler/DefaultPermissionHandler.java | 64 ++++++ .../handler/IPermissionHandler.java | 81 ++++++++ .../IPermissionHandlerFactory.java} | 14 +- .../nodes/PermissionDynamicContext.java | 71 +++++++ .../nodes/PermissionDynamicContextKey.java | 39 ++++ .../permission/nodes/PermissionNode.java | 192 ++++++++++++++++++ .../permission/nodes/PermissionType.java | 73 +++++++ .../permission/nodes/PermissionTypes.java | 52 +++++ .../server/permission/package-info.java | 2 +- .../minecraftforge/debug/PermissionTest.java | 108 ++++++++++ src/test/resources/META-INF/mods.toml | 2 + 27 files changed, 938 insertions(+), 750 deletions(-) delete mode 100644 src/main/java/net/minecraftforge/server/permission/DefaultPermissionHandler.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/IPermissionHandler.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/AreaContext.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/BlockPosContext.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/Context.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/ContextKey.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/ContextKeys.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/IContext.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/PlayerContext.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/TargetContext.java delete mode 100644 src/main/java/net/minecraftforge/server/permission/context/WorldContext.java create mode 100644 src/main/java/net/minecraftforge/server/permission/events/PermissionGatherEvent.java rename src/main/java/net/minecraftforge/server/permission/{DefaultPermissionLevel.java => exceptions/UnregisteredPermissionException.java} (59%) create mode 100644 src/main/java/net/minecraftforge/server/permission/handler/DefaultPermissionHandler.java create mode 100644 src/main/java/net/minecraftforge/server/permission/handler/IPermissionHandler.java rename src/main/java/net/minecraftforge/server/permission/{context/package-info.java => handler/IPermissionHandlerFactory.java} (72%) create mode 100644 src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContext.java create mode 100644 src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContextKey.java create mode 100644 src/main/java/net/minecraftforge/server/permission/nodes/PermissionNode.java create mode 100644 src/main/java/net/minecraftforge/server/permission/nodes/PermissionType.java create mode 100644 src/main/java/net/minecraftforge/server/permission/nodes/PermissionTypes.java create mode 100644 src/test/java/net/minecraftforge/debug/PermissionTest.java diff --git a/src/main/java/net/minecraftforge/common/ForgeConfig.java b/src/main/java/net/minecraftforge/common/ForgeConfig.java index a78e2c3900..4592cce92a 100644 --- a/src/main/java/net/minecraftforge/common/ForgeConfig.java +++ b/src/main/java/net/minecraftforge/common/ForgeConfig.java @@ -28,6 +28,8 @@ import org.apache.logging.log4j.LogManager; import net.minecraftforge.common.ForgeConfigSpec.BooleanValue; import net.minecraftforge.common.ForgeConfigSpec.DoubleValue; +import net.minecraftforge.common.ForgeConfigSpec.ConfigValue; + public class ForgeConfig { public static class Server { @@ -42,6 +44,8 @@ public class ForgeConfig { public final BooleanValue fixAdvancementLoading; + public final ConfigValue permissionHandler; + Server(ForgeConfigSpec.Builder builder) { builder.comment("Server configuration settings") .push("server"); @@ -80,6 +84,11 @@ public class ForgeConfig { .translation("forge.configgui.fixAdvancementLoading") .define("fixAdvancementLoading", true); + permissionHandler = builder + .comment("") + .translation("forge.configgui.permissionHandler") + .define("permissionHandler", "forge:default_handler"); + builder.pop(); } } diff --git a/src/main/java/net/minecraftforge/server/ServerLifecycleHooks.java b/src/main/java/net/minecraftforge/server/ServerLifecycleHooks.java index f133e01fd4..052019587b 100644 --- a/src/main/java/net/minecraftforge/server/ServerLifecycleHooks.java +++ b/src/main/java/net/minecraftforge/server/ServerLifecycleHooks.java @@ -44,6 +44,7 @@ import net.minecraftforge.network.NetworkHooks; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.forgespi.locating.IModFile; import net.minecraftforge.resource.PathResourcePack; +import net.minecraftforge.server.permission.PermissionAPI; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; @@ -97,6 +98,7 @@ public class ServerLifecycleHooks public static boolean handleServerStarting(final MinecraftServer server) { DistExecutor.runWhenOn(Dist.DEDICATED_SERVER, ()->()->LanguageHook.loadLanguagesOnServer(server)); + PermissionAPI.initializePermissionAPI(); return !MinecraftForge.EVENT_BUS.post(new ServerStartingEvent(server)); } @@ -109,6 +111,7 @@ public class ServerLifecycleHooks public static void handleServerStopping(final MinecraftServer server) { allowLogins.set(false); + PermissionAPI.resetPermissionAPI(); MinecraftForge.EVENT_BUS.post(new ServerStoppingEvent(server)); } diff --git a/src/main/java/net/minecraftforge/server/permission/DefaultPermissionHandler.java b/src/main/java/net/minecraftforge/server/permission/DefaultPermissionHandler.java deleted file mode 100644 index c1332ff315..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/DefaultPermissionHandler.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission; - -import com.mojang.authlib.GameProfile; -import net.minecraft.server.MinecraftServer; -import net.minecraftforge.server.ServerLifecycleHooks; -import net.minecraftforge.server.permission.context.IContext; - -import javax.annotation.Nullable; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; - -/** - * Default implementation of PermissionAPI. - * {@link #hasPermission(GameProfile, String, IContext)} is based on DefaultPermissionLevel - * - * @see IPermissionHandler - */ -public enum DefaultPermissionHandler implements IPermissionHandler -{ - INSTANCE; - private static final HashMap PERMISSION_LEVEL_MAP = new HashMap(); - private static final HashMap DESCRIPTION_MAP = new HashMap(); - - @Override - public void registerNode(String node, DefaultPermissionLevel level, String desc) - { - PERMISSION_LEVEL_MAP.put(node, level); - - if(!desc.isEmpty()) - { - DESCRIPTION_MAP.put(node, desc); - } - } - - @Override - public Collection getRegisteredNodes() - { - return Collections.unmodifiableSet(PERMISSION_LEVEL_MAP.keySet()); - } - - @Override - public boolean hasPermission(GameProfile profile, String node, @Nullable IContext context) - { - DefaultPermissionLevel level = getDefaultPermissionLevel(node); - - if(level == DefaultPermissionLevel.NONE) - { - return false; - } - else if(level == DefaultPermissionLevel.ALL) - { - return true; - } - - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - return server != null && server.getPlayerList().isOp(profile); - } - - @Override - public String getNodeDescription(String node) - { - String desc = DESCRIPTION_MAP.get(node); - return desc == null ? "" : desc; - } - - /** - * @return The default permission level of a node. If the permission isn't registred, it will return NONE - */ - public DefaultPermissionLevel getDefaultPermissionLevel(String node) - { - DefaultPermissionLevel level = PERMISSION_LEVEL_MAP.get(node); - return level == null ? DefaultPermissionLevel.NONE : level; - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/IPermissionHandler.java b/src/main/java/net/minecraftforge/server/permission/IPermissionHandler.java deleted file mode 100644 index 3f6e5d6f22..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/IPermissionHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission; - -import com.mojang.authlib.GameProfile; -import net.minecraftforge.server.permission.context.IContext; - -import javax.annotation.Nullable; -import java.util.Collection; - -public interface IPermissionHandler -{ - /** - * Use {@link PermissionAPI#registerNode(String, DefaultPermissionLevel, String)} - */ - void registerNode(String node, DefaultPermissionLevel level, String desc); - - /** - * @return Immutable collection of all registered nodes - */ - Collection getRegisteredNodes(); - - /** - * Use {@link PermissionAPI#hasPermission(GameProfile, String, IContext)} - */ - boolean hasPermission(GameProfile profile, String node, @Nullable IContext context); - - /** - * @param node Permission node - * @return Description of the node. "" in case this node doesn't have a decription - * @see #registerNode(String, DefaultPermissionLevel, String) - */ - String getNodeDescription(String node); -} diff --git a/src/main/java/net/minecraftforge/server/permission/PermissionAPI.java b/src/main/java/net/minecraftforge/server/permission/PermissionAPI.java index 38ffbc7e4f..0fffc9b7e4 100644 --- a/src/main/java/net/minecraftforge/server/permission/PermissionAPI.java +++ b/src/main/java/net/minecraftforge/server/permission/PermissionAPI.java @@ -19,80 +19,135 @@ package net.minecraftforge.server.permission; -import com.google.common.base.Preconditions; -import com.mojang.authlib.GameProfile; -import net.minecraft.world.entity.player.Player; -import net.minecraftforge.server.permission.context.IContext; -import net.minecraftforge.server.permission.context.PlayerContext; - -import javax.annotation.Nullable; - +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import net.minecraft.ResourceLocationException; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.StringRepresentable; +import net.minecraftforge.common.ForgeConfig; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.server.permission.events.PermissionGatherEvent; +import net.minecraftforge.server.permission.exceptions.UnregisteredPermissionException; +import net.minecraftforge.server.permission.handler.DefaultPermissionHandler; +import net.minecraftforge.server.permission.handler.IPermissionHandler; +import net.minecraftforge.server.permission.handler.IPermissionHandlerFactory; +import net.minecraftforge.server.permission.nodes.PermissionDynamicContext; +import net.minecraftforge.server.permission.nodes.PermissionNode; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class PermissionAPI +import javax.annotation.Nullable; +import java.util.*; + +public final class PermissionAPI { private static final Logger LOGGER = LogManager.getLogger(); - - private static IPermissionHandler permissionHandler = DefaultPermissionHandler.INSTANCE; + private static IPermissionHandler activeHandler = null; + + public static Collection> getRegisteredNodes() + { + return activeHandler == null ? Collections.emptySet() : activeHandler.getRegisteredNodes(); + } + + private PermissionAPI() + { + } /** - * Only use this in PreInit state! + * @return the Identifier of the currently active permission handler */ - public static void setPermissionHandler(IPermissionHandler handler) + @Nullable + public static ResourceLocation getActivePermissionHandler() { - Preconditions.checkNotNull(handler, "Permission handler can't be null!"); - // TODO Loader states Preconditions.checkState(Loader.instance().getLoaderState().ordinal() <= LoaderState.PREINITIALIZATION.ordinal(), "Can't register after IPermissionHandler PreInit!"); - LOGGER.warn("Replacing {} with {}", permissionHandler.getClass().getName(), handler.getClass().getName()); - permissionHandler = handler; - } - - public static IPermissionHandler getPermissionHandler() - { - return permissionHandler; + return activeHandler == null ? null : activeHandler.getIdentifier(); } /** - * Only use this after PreInit state! + *

Queries a player's permission for a given node and contexts

+ *

Warning: PermissionNodes must be registered using the + * {@link PermissionGatherEvent.Nodes} event before querying.

* - * @param node Permission node, best if it's lowercase and contains '.' (e.g. "modid.subgroup.permission_id") - * @param level Default permission level for this node. If not isn't registered, it's level is going to be 'NONE' - * @param desc Optional description of the node + * @param player player for which you want to check permissions + * @param node the PermissionNode for which you want to query + * @param context optional array of PermissionDynamicContext, single entries will be ignored if they weren't + * registered to the node + * @param type of the queried PermissionNode + * @return a value of type {@code }, that the combination of Player and PermissionNode map to, defaults to the + * PermissionNodes default handler. + * @throws UnregisteredPermissionException when the PermissionNode wasn't registered properly */ - public static String registerNode(String node, DefaultPermissionLevel level, String desc) + public static T getPermission(ServerPlayer player, PermissionNode node, PermissionDynamicContext... context) { - Preconditions.checkNotNull(node, "Permission node can't be null!"); - Preconditions.checkNotNull(level, "Permission level can't be null!"); - Preconditions.checkNotNull(desc, "Permission description can't be null!"); - Preconditions.checkArgument(!node.isEmpty(), "Permission node can't be empty!"); - // TODO Loader states Preconditions.checkState(Loader.instance().getLoaderState().ordinal() > LoaderState.PREINITIALIZATION.ordinal(), "Can't register permission nodes before Init!"); - permissionHandler.registerNode(node, level, desc); - return node; + if (!activeHandler.getRegisteredNodes().contains(node)) throw new UnregisteredPermissionException(node); + return activeHandler.getPermission(player, node, context); } /** - * @param profile GameProfile of the player who is requesting permission. The player doesn't have to be online - * @param node Permission node. See {@link #registerNode(String, DefaultPermissionLevel, String)} - * @param context Context for this permission. Highly recommended to not be null. See {@link IContext} - * @return true, if player has permission, false if he does not. - * @see DefaultPermissionHandler - */ - public static boolean hasPermission(GameProfile profile, String node, @Nullable IContext context) - { - Preconditions.checkNotNull(profile, "GameProfile can't be null!"); - Preconditions.checkNotNull(node, "Permission node can't be null!"); - Preconditions.checkArgument(!node.isEmpty(), "Permission node can't be empty!"); - return permissionHandler.hasPermission(profile, node, context); - } - - /** - * Shortcut method using EntityPlayer and creating PlayerContext + * See {@link PermissionAPI#getPermission(ServerPlayer, PermissionNode, PermissionDynamicContext[])} * - * @see PermissionAPI#hasPermission(GameProfile, String, IContext) + * @param player offline player for which you want to check permissions + * @param node the PermissionNode for which you want to query + * @param context optional array of PermissionDynamicContext, single entries will be ignored if they weren't + * registered to the node + * @param type of the queried PermissionNode + * @return a value of type {@code }, that the combination of Player and PermissionNode map to, defaults to the + * PermissionNodes default handler. + * @throws UnregisteredPermissionException when the PermissionNode wasn't registered properly */ - public static boolean hasPermission(Player player, String node) + public static T getOfflinePermission(UUID player, PermissionNode node, PermissionDynamicContext... context) { - Preconditions.checkNotNull(player, "Player can't be null!"); - return hasPermission(player.getGameProfile(), node, new PlayerContext(player)); + if (!activeHandler.getRegisteredNodes().contains(node)) throw new UnregisteredPermissionException(node); + return activeHandler.getOfflinePermission(player, node, context); + } + + + /** + *

Helper method for internal use only!

+ *

Initializes the active permission handler based on the users config.

+ */ + public static void initializePermissionAPI() + { + if (PermissionAPI.activeHandler != null) throw new IllegalStateException("Tried to initialize PermissionAPI multiple times!"); + + PermissionGatherEvent.Handler handlerEvent = new PermissionGatherEvent.Handler(); + MinecraftForge.EVENT_BUS.post(handlerEvent); + Map availableHandlers = handlerEvent.getAvailablePermissionHandlerFactories(); + + try + { + ResourceLocation selectedPermissionHandler = new ResourceLocation(ForgeConfig.SERVER.permissionHandler.get()); + if (!availableHandlers.containsKey(selectedPermissionHandler)) + { + LOGGER.error("Unable to find configured permission handler {}, will use {}", selectedPermissionHandler, DefaultPermissionHandler.IDENTIFIER); + selectedPermissionHandler = DefaultPermissionHandler.IDENTIFIER; + } + + IPermissionHandlerFactory factory = availableHandlers.get(selectedPermissionHandler); + + PermissionGatherEvent.Nodes nodesEvent = new PermissionGatherEvent.Nodes(); + MinecraftForge.EVENT_BUS.post(nodesEvent); + + PermissionAPI.activeHandler = factory.create(nodesEvent.getNodes()); + + if(!selectedPermissionHandler.equals(activeHandler.getIdentifier())) + LOGGER.warn("Identifier for permission handler {} does not match registered one {}", activeHandler.getIdentifier(), selectedPermissionHandler); + + LOGGER.info("Successfully initialized permission handler {}", PermissionAPI.activeHandler.getIdentifier()); + } + catch (ResourceLocationException e) + { + LOGGER.error("Error parsing config value 'permissionHandler'", e); + } + } + + /** + *

Helper method for internal use only!

+ *

Resets the active permission handler.

+ */ + public static void resetPermissionAPI() + { + PermissionAPI.activeHandler = null; + LOGGER.debug("Reset PermissionAPI"); } } diff --git a/src/main/java/net/minecraftforge/server/permission/context/AreaContext.java b/src/main/java/net/minecraftforge/server/permission/context/AreaContext.java deleted file mode 100644 index e23408311e..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/AreaContext.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import com.google.common.base.Preconditions; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.phys.AABB; - -import javax.annotation.Nullable; - -public class AreaContext extends PlayerContext -{ - private final AABB area; - - public AreaContext(Player ep, AABB aabb) - { - super(ep); - area = Preconditions.checkNotNull(aabb, "AxisAlignedBB can't be null in AreaContext!"); - } - - @SuppressWarnings("unchecked") - @Override - @Nullable - public T get(ContextKey key) - { - return key.equals(ContextKeys.AREA) ? (T) area : super.get(key); - } - - @Override - protected boolean covers(ContextKey key) - { - return key.equals(ContextKeys.AREA); - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/BlockPosContext.java b/src/main/java/net/minecraftforge/server/permission/context/BlockPosContext.java deleted file mode 100644 index 81d242dce3..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/BlockPosContext.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import com.google.common.base.Preconditions; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.entity.player.Player; -import net.minecraft.core.Direction; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.ChunkPos; - -import javax.annotation.Nullable; - -public class BlockPosContext extends PlayerContext -{ - private final BlockPos blockPos; - private BlockState blockState; - private Direction facing; - - public BlockPosContext(Player ep, BlockPos pos, @Nullable BlockState state, @Nullable Direction f) - { - super(ep); - blockPos = Preconditions.checkNotNull(pos, "BlockPos can't be null in BlockPosContext!"); - blockState = state; - facing = f; - } - - public BlockPosContext(Player ep, ChunkPos pos) - { - this(ep, new BlockPos(pos.getMinBlockX() + 8, 0, pos.getMinBlockZ() + 8), null, null); - } - - @Override - @Nullable - public T get(ContextKey key) - { - if(key.equals(ContextKeys.POS)) - { - return (T) blockPos; - } - else if(key.equals(ContextKeys.BLOCK_STATE)) - { - if(blockState == null) - { - blockState = getWorld().getBlockState(blockPos); - } - - return (T) blockState; - } - else if(key.equals(ContextKeys.FACING)) - { - return (T) facing; - } - - return super.get(key); - } - - @Override - protected boolean covers(ContextKey key) - { - return key.equals(ContextKeys.POS) || key.equals(ContextKeys.BLOCK_STATE) || (facing != null && key.equals(ContextKeys.FACING)); - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/Context.java b/src/main/java/net/minecraftforge/server/permission/context/Context.java deleted file mode 100644 index 9e86b24d29..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/Context.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; - -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; - -public class Context implements IContext -{ - private Map, Object> map; - - @Override - @Nullable - public Level getWorld() - { - return null; - } - - @Override - @Nullable - public Player getPlayer() - { - return null; - } - - @Override - @Nullable - public T get(ContextKey key) - { - return map == null || map.isEmpty() ? null : (T) map.get(key); - } - - @Override - public boolean has(ContextKey key) - { - return covers(key) || (map != null && !map.isEmpty() && map.containsKey(key)); - } - - /** - * Sets Context object - * - * @param key Context key - * @param obj Context object. Can be null - * @return itself, for easy context chaining - */ - public Context set(ContextKey key, @Nullable T obj) - { - if(covers(key)) - { - return this; - } - - if(map == null) - { - map = new HashMap, Object>(); - } - - map.put(key, obj); - return this; - } - - protected boolean covers(ContextKey key) - { - return false; - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/ContextKey.java b/src/main/java/net/minecraftforge/server/permission/context/ContextKey.java deleted file mode 100644 index cd6d8a997e..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/ContextKey.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import com.google.common.base.Preconditions; - -public final class ContextKey -{ - private final String ID; - private final Class typeClass; - - public static ContextKey create(String id, Class c) - { - Preconditions.checkNotNull(id, "ContextKey's ID can't be null!"); - Preconditions.checkNotNull(c, "ContextKey's Type can't be null!"); - - if(id.isEmpty()) - { - throw new IllegalArgumentException("ContextKey's ID can't be blank!"); - } - - return new ContextKey(id, c); - } - - private ContextKey(String id, Class c) - { - ID = id; - typeClass = c; - } - - public String toString() - { - return ID; - } - - public int hashCode() - { - return ID.hashCode(); - } - - public boolean equals(Object o) - { - return o == this || (o != null && o.toString().equals(ID)); - } - - public Class getTypeClass() - { - return typeClass; - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/ContextKeys.java b/src/main/java/net/minecraftforge/server/permission/context/ContextKeys.java deleted file mode 100644 index 8a27579d1a..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/ContextKeys.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.entity.Entity; -import net.minecraft.core.Direction; -import net.minecraft.world.phys.AABB; -import net.minecraft.core.BlockPos; - -/** - * Some default context keys, for easier compatibility - */ -public class ContextKeys -{ - /** - * BlockPos for interacting, breaking and other permissions - */ - public static final ContextKey POS = ContextKey.create("pos", BlockPos.class); - - /** - * The entity can be anything that gets interacted with - a sheep when you try to dye it, skeleton that you attack, etc. - */ - public static final ContextKey TARGET = ContextKey.create("target", Entity.class); - - public static final ContextKey FACING = ContextKey.create("facing", Direction.class); - public static final ContextKey AREA = ContextKey.create("area", AABB.class); - public static final ContextKey BLOCK_STATE = ContextKey.create("blockstate", BlockState.class); -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/IContext.java b/src/main/java/net/minecraftforge/server/permission/context/IContext.java deleted file mode 100644 index f6259b8d86..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/IContext.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; - -import javax.annotation.Nullable; - -/** - * Use {@link BlockPosContext} or {@link PlayerContext} when possible - */ -public interface IContext -{ - /** - * World from where permission is requested. Can be null - */ - @Nullable - Level getWorld(); - - /** - * @return Player requesting permission. Can be null - */ - @Nullable - Player getPlayer(); - - /** - * @param key Context key - * @return Context object - */ - @Nullable - T get(ContextKey key); - - /** - * @param key Context key - * @return true if context contains this key - */ - boolean has(ContextKey key); -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/PlayerContext.java b/src/main/java/net/minecraftforge/server/permission/context/PlayerContext.java deleted file mode 100644 index 620467061c..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/PlayerContext.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import com.google.common.base.Preconditions; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; - -public class PlayerContext extends Context -{ - private final Player player; - - public PlayerContext(Player ep) - { - player = Preconditions.checkNotNull(ep, "Player can't be null in PlayerContext!"); - } - - @Override - public Level getWorld() - { - return player.getCommandSenderWorld(); - } - - @Override - public Player getPlayer() - { - return player; - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/TargetContext.java b/src/main/java/net/minecraftforge/server/permission/context/TargetContext.java deleted file mode 100644 index b38e6d047c..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/TargetContext.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; - -import javax.annotation.Nullable; - -public class TargetContext extends PlayerContext -{ - private final Entity target; - - public TargetContext(Player ep, @Nullable Entity entity) - { - super(ep); - target = entity; - } - - @Override - @Nullable - public T get(ContextKey key) - { - return key.equals(ContextKeys.TARGET) ? (T) target : super.get(key); - } - - @Override - protected boolean covers(ContextKey key) - { - return target != null && key.equals(ContextKeys.TARGET); - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/context/WorldContext.java b/src/main/java/net/minecraftforge/server/permission/context/WorldContext.java deleted file mode 100644 index 9ef1538675..0000000000 --- a/src/main/java/net/minecraftforge/server/permission/context/WorldContext.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.server.permission.context; - -import com.google.common.base.Preconditions; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; - -import javax.annotation.Nullable; - -public class WorldContext extends Context -{ - private final Level world; - - public WorldContext(Level w) - { - world = Preconditions.checkNotNull(w, "World can't be null in WorldContext!"); - } - - @Override - public Level getWorld() - { - return world; - } - - @Override - @Nullable - public Player getPlayer() - { - return null; - } -} diff --git a/src/main/java/net/minecraftforge/server/permission/events/PermissionGatherEvent.java b/src/main/java/net/minecraftforge/server/permission/events/PermissionGatherEvent.java new file mode 100644 index 0000000000..fbef8778a2 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/events/PermissionGatherEvent.java @@ -0,0 +1,109 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.events; + +import com.google.common.base.Preconditions; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.server.permission.handler.DefaultPermissionHandler; +import net.minecraftforge.server.permission.handler.IPermissionHandler; +import net.minecraftforge.server.permission.handler.IPermissionHandlerFactory; +import net.minecraftforge.server.permission.nodes.PermissionNode; + +import java.util.*; +import java.util.function.Function; + +/** + * Fired to gather information for the permissions API, such as the {@link IPermissionHandler} and {@link PermissionNode}s. + * + *

+ * {@link Handler} allows to set a new PermissionHandler
+ * {@link Nodes} allows you to register new PermissionNodes

+ * + *

Note: All PermissionNodes that you want to use, must be registered!

+ */ +public class PermissionGatherEvent extends Event +{ + + /** + * Used to register a new PermissionHandler, a server config value exists to choose which one to use. + *

Note: Create a new instance when registering a PermissionHandler. + * If you cache it, make sure that your PermissionHandler is actually used after this event.

+ */ + public static class Handler extends PermissionGatherEvent + { + private Map availableHandlers = new HashMap<>(); + + public Handler() + { + availableHandlers.put(DefaultPermissionHandler.IDENTIFIER, DefaultPermissionHandler::new); + } + + public Map getAvailablePermissionHandlerFactories() + { + return Collections.unmodifiableMap(availableHandlers); + } + + public void addPermissionHandler(ResourceLocation identifier, IPermissionHandlerFactory handlerFactory) + { + Preconditions.checkNotNull(identifier, "Permission handler identifier cannot be null!"); + Preconditions.checkNotNull(handlerFactory, "Permission handler cannot be null!"); + if(this.availableHandlers.containsKey(identifier)) + throw new IllegalArgumentException("Attempted to overwrite permission handler " + identifier + ", this is not allowed."); + this.availableHandlers.put(identifier, handlerFactory); + } + } + + + /** + * Used to register your PermissionNodes, every node that you want to use, must be registered! + */ + public static class Nodes extends PermissionGatherEvent + { + private final Set> nodes = new HashSet<>(); + + public Nodes() + { + } + + public Collection> getNodes() + { + return Collections.unmodifiableCollection(this.nodes); + } + + public void addNodes(PermissionNode... nodes) + { + for (PermissionNode node : nodes) + { + if (!this.nodes.add(node)) + throw new IllegalArgumentException("Tried to register duplicate PermissionNode '" + node.getNodeName() + "'"); + } + } + + public void addNodes(Iterable> nodes) + { + for (PermissionNode node : nodes) + { + if (!this.nodes.add(node)) + throw new IllegalArgumentException("Tried to register duplicate PermissionNode '" + node.getNodeName() + "'"); + } + } + } +} diff --git a/src/main/java/net/minecraftforge/server/permission/DefaultPermissionLevel.java b/src/main/java/net/minecraftforge/server/permission/exceptions/UnregisteredPermissionException.java similarity index 59% rename from src/main/java/net/minecraftforge/server/permission/DefaultPermissionLevel.java rename to src/main/java/net/minecraftforge/server/permission/exceptions/UnregisteredPermissionException.java index 485db98760..b47c114b21 100644 --- a/src/main/java/net/minecraftforge/server/permission/DefaultPermissionLevel.java +++ b/src/main/java/net/minecraftforge/server/permission/exceptions/UnregisteredPermissionException.java @@ -17,20 +17,22 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package net.minecraftforge.server.permission; +package net.minecraftforge.server.permission.exceptions; -/** - * - * - * - * - * - * - *
Default Permission Levels
LevelPlayerOP
ALLtruetrue
OPfalsetrue
NONEfalsefalse
- */ -public enum DefaultPermissionLevel +import net.minecraftforge.server.permission.nodes.PermissionNode; + +public class UnregisteredPermissionException extends RuntimeException { - ALL, - OP, - NONE + private PermissionNode node; + + public UnregisteredPermissionException(PermissionNode node) + { + super(String.format("Tried to query PermissionNode '%s' although it has not been Registered", node.getNodeName())); + this.node = node; + } + + public PermissionNode getNode() + { + return node; + } } diff --git a/src/main/java/net/minecraftforge/server/permission/handler/DefaultPermissionHandler.java b/src/main/java/net/minecraftforge/server/permission/handler/DefaultPermissionHandler.java new file mode 100644 index 0000000000..3218bbd785 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/handler/DefaultPermissionHandler.java @@ -0,0 +1,64 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.handler; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.server.permission.nodes.PermissionDynamicContext; +import net.minecraftforge.server.permission.nodes.PermissionNode; + +import java.util.*; + +public final class DefaultPermissionHandler implements IPermissionHandler +{ + public static final ResourceLocation IDENTIFIER = new ResourceLocation("forge", "default_handler");; + private final Set> registeredNodes = new HashSet<>(); + private Set> immutableRegisteredNodes = Collections.unmodifiableSet(this.registeredNodes); + + + public DefaultPermissionHandler(Collection> permissions) + { + this.registeredNodes.addAll(permissions); + } + + @Override + public ResourceLocation getIdentifier() + { + return IDENTIFIER; + } + + @Override + public Set> getRegisteredNodes() + { + return immutableRegisteredNodes; + } + + @Override + public T getPermission(ServerPlayer player, PermissionNode node, PermissionDynamicContext... context) + { + return node.getDefaultResolver().resolve(player, player.getUUID(), context); + } + + @Override + public T getOfflinePermission(UUID player, PermissionNode node, PermissionDynamicContext... context) + { + return node.getDefaultResolver().resolve(null, player, context); + } +} diff --git a/src/main/java/net/minecraftforge/server/permission/handler/IPermissionHandler.java b/src/main/java/net/minecraftforge/server/permission/handler/IPermissionHandler.java new file mode 100644 index 0000000000..7a9c288491 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/handler/IPermissionHandler.java @@ -0,0 +1,81 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.handler; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.StringRepresentable; +import net.minecraftforge.server.permission.PermissionAPI; +import net.minecraftforge.server.permission.events.PermissionGatherEvent; +import net.minecraftforge.server.permission.nodes.PermissionDynamicContext; +import net.minecraftforge.server.permission.nodes.PermissionNode; + +import java.util.Set; +import java.util.UUID; + +/** + * This is the Heart of the PermissionAPI, it manages {@link PermissionNode}s + * as well as it handles all permission queries. + * + *

Note: You do not need to implement a PermissionHandler to query for permissions.

+ * + * @implNote The {@link DefaultPermissionHandler} does forward all permission queries to the PermissionNodes default resolver. + * @apiNote You can implement your own PermissionHandler using the {@link PermissionGatherEvent.Handler} event. + */ +public interface IPermissionHandler +{ + /** + * {@return an identifier for the PermissionHandler} + */ + ResourceLocation getIdentifier(); + + /** + * {@return an unmodifiable view of the collection of registered permission nodes} + */ + Set> getRegisteredNodes(); + + /** + * Mods must use {@link PermissionAPI#getPermission(ServerPlayer, PermissionNode, PermissionDynamicContext[])} + * + *

Queries a player's permission for a given node and contexts

+ *

Warning: PermissionNodes must be registered using the + * {@link PermissionGatherEvent.Nodes} event before querying.

+ * + * @param player player for which you want to check permissions + * @param node the PermissionNode for which you want to query + * @param context optional array of PermissionDynamicContext, single entries will be ignored if they weren't + * registered to the node + * @param type of the queried PermissionNode + * @return a value of type {@code }, that the combination of Player and PermissionNode map to. + */ + T getPermission(ServerPlayer player, PermissionNode node, PermissionDynamicContext... context); + + /** + * See {@link IPermissionHandler#getPermission(ServerPlayer, PermissionNode, PermissionDynamicContext[])} + * + * @param player offline player for which you want to check permissions + * @param node the PermissionNode for which you want to query + * @param context optional array of PermissionDynamicContext, single entries will be ignored if they weren't + * registered to the node + * @param type of the queried PermissionNode + * @return a value of type {@code }, that the combination of Player and PermissionNode map to. + */ + T getOfflinePermission(UUID player, PermissionNode node, PermissionDynamicContext... context); +} diff --git a/src/main/java/net/minecraftforge/server/permission/context/package-info.java b/src/main/java/net/minecraftforge/server/permission/handler/IPermissionHandlerFactory.java similarity index 72% rename from src/main/java/net/minecraftforge/server/permission/context/package-info.java rename to src/main/java/net/minecraftforge/server/permission/handler/IPermissionHandlerFactory.java index 8bc1264c13..61f4512bae 100644 --- a/src/main/java/net/minecraftforge/server/permission/context/package-info.java +++ b/src/main/java/net/minecraftforge/server/permission/handler/IPermissionHandlerFactory.java @@ -17,10 +17,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -package net.minecraftforge.server.permission.context; +package net.minecraftforge.server.permission.handler; -import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraftforge.server.permission.nodes.PermissionNode; -import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Collection; + +@FunctionalInterface +public interface IPermissionHandlerFactory +{ + IPermissionHandler create(Collection> permissions); +} diff --git a/src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContext.java b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContext.java new file mode 100644 index 0000000000..5ae5925a47 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContext.java @@ -0,0 +1,71 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.nodes; + +import java.util.Objects; + +/** + * Pair of a PermissionDynamicContextKey and a value of the corresponding type. + * Use {@link PermissionDynamicContextKey#createContext(Object)} )} for constructing. + * + *

Note: While the DynamicContext behaves similar to BlockStates, it does not oblige to the same limitations. + * There is no string representation that you have to follow, nor is there a limit on how many unique value a DynamicContext may have

+ * + * @implNote this class could be a record but is not in favor of a package private constructor + */ +public final class PermissionDynamicContext +{ + private PermissionDynamicContextKey dynamic; + private T value; + + PermissionDynamicContext(PermissionDynamicContextKey dynamic, T value) + { + this.dynamic = dynamic; + this.value = value; + } + + public PermissionDynamicContextKey getDynamic() + { + return dynamic; + } + + public T getValue() + { + return value; + } + + public String getSerializedValue(){ + return this.dynamic.serializer().apply(this.value); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (!(o instanceof PermissionDynamicContext otherContext)) return false; + return dynamic.equals(otherContext.dynamic) && value.equals(otherContext.value); + } + + @Override + public int hashCode() + { + return Objects.hash(dynamic, value); + } +} diff --git a/src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContextKey.java b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContextKey.java new file mode 100644 index 0000000000..5dca873dab --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionDynamicContextKey.java @@ -0,0 +1,39 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.nodes; + +import java.util.function.Function; + +/** + * Represents a key that can be used to build a {@link PermissionDynamicContext}. + * + *

Keys, along with their associated values, can be used to provide additional context for a permission handler + * in determining whether to grant permission for an actor and a specific node.

+ * + *

As an example usage, a dimension context key could be used inside a building permission + * check to ensure that the actor can build given those constraints.

+ */ +public record PermissionDynamicContextKey(Class typeToken, String name, Function serializer) +{ + public PermissionDynamicContext createContext(T value) + { + return new PermissionDynamicContext<>(this, value); + } +} diff --git a/src/main/java/net/minecraftforge/server/permission/nodes/PermissionNode.java b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionNode.java new file mode 100644 index 0000000000..0f56649e55 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionNode.java @@ -0,0 +1,192 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.nodes; + +import com.google.common.base.Preconditions; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.server.permission.events.PermissionGatherEvent; +import net.minecraftforge.server.permission.handler.IPermissionHandler; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Objects; +import java.util.UUID; + +/** + * Represents the basic unit at the heart of the permission system. + * + *

A permission indicates the ability for an actor to perform an action, + * in its most general sense. In the permission system, all permissions are encoded as instances of this class, + * optionally integrated by a {@link PermissionDynamicContext}.

+ * + *

A node is uniquely identified by its `nodeName`, + * which is a dot-separated string providing meaning to the node itself. + * The suggested structure of the name is `modid.name`, where `modid` is the ID of the owner of the node. + * This API does not require any implicit hierarchy, + * so `modid.name` is not considered a parent of `modid.name.sub`. Such decisions are left to the {@link IPermissionHandler}.

+ * + *

Each node also has an associated {@link PermissionType}, detailing its exact type, + * along with a {@link PermissionResolver} that is used to obtain the default value of the permission. + * More information can be found on their documentation.

+ * + *

A node can also be bound to additional {@link PermissionDynamicContextKey}s, + * which are used when querying the availability of the permission. + * There are no restrictions on their amount. It is also not mandatory to provide a value for all dynamics in a permission query. + * See the above link for more information.

+ * + *

Each node should be registered via the {@link PermissionGatherEvent.Nodes} and stored statically in a field. + * That instance should then be reused every-time a permission check needs to be performed via {@link net.minecraftforge.server.permission.PermissionAPI#getPermissions(ServerPlayer, PermissionNode, PermissionDynamicContext[])}.

+ */ +public final class PermissionNode +{ + private final String nodeName; + private final PermissionType type; + private final PermissionResolver defaultResolver; + private final PermissionDynamicContextKey[] dynamics; + + @Nullable + private Component readableName; + @Nullable + private Component description; + + /** + * Calls {@link PermissionNode#PermissionNode(String, PermissionType, PermissionResolver, PermissionDynamicContextKey[])} + * with "namespace.path" as the first parameter + */ + public PermissionNode(ResourceLocation nodeName, PermissionType type, PermissionResolver defaultResolver, PermissionDynamicContextKey... dynamics) + { + this(nodeName.getNamespace(), nodeName.getPath(), type, defaultResolver, dynamics); + } + + /** + * Calls {@link PermissionNode#PermissionNode(String, PermissionType, PermissionResolver, PermissionDynamicContextKey[])} + * with "modid.nodename" as the first parameter + */ + public PermissionNode(String modID, String nodeName, PermissionType type, PermissionResolver defaultResolver, PermissionDynamicContextKey... dynamics) + { + this(modID + "." + nodeName, type, defaultResolver, dynamics); + } + + /** + * @param nodeName The identifier of a node, recommended identifier structure: "modid.path.for.node" + * @param type type object for the PermissionNode, only use types in {@link PermissionTypes} + * @param defaultResolver Default resolver for the permission, can but doesn't have to be used to by PermissionHandlers + * @param dynamics PermissionDynamicContextKey is a dynamic component for permission nodes, similar to BlockState Properties. + * They must be passed into the constructor if you want to use them. + */ + private PermissionNode(String nodeName, PermissionType type, PermissionResolver defaultResolver, PermissionDynamicContextKey... dynamics) + { + this.nodeName = nodeName; + this.type = type; + this.dynamics = dynamics; + this.defaultResolver = defaultResolver; + } + + /** + * Allows you to set a human-readable name & description for your Permission. + * + *

Note: Even though not used by Default, PermissionHandlers may display this information in game, + * or provide it to the user by other means.
+ * You may use {@link net.minecraft.network.chat.TranslatableComponent}, but you'll need 2 language files. + * One inside the data directory for the server and one inside assets for the client.

+ * + * @param readableName an easier to read name for the PermissionNode, + * when using TranslatableComponent, key should be of format "permission.name." + * @param description description for the PermissionNode + * when using TranslatableComponent, key should be of format "permission.desc." + * @return itself with the new information set. + */ + public PermissionNode setInformation(@Nonnull Component readableName, @Nonnull Component description) + { + Preconditions.checkNotNull(readableName, "Readable name for PermissionNodes must not be null %s", this.nodeName); + Preconditions.checkNotNull(description, "Description for PermissionNodes must not be null %s", this.nodeName); + + this.readableName = readableName; + this.description = description; + + return this; + } + + public String getNodeName() + { + return nodeName; + } + + public PermissionType getType() + { + return type; + } + + public PermissionDynamicContextKey[] getDynamics() + { + return dynamics; + } + + public PermissionResolver getDefaultResolver() + { + return defaultResolver; + } + + @Nullable + public Component getReadableName() + { + return readableName; + } + + @Nullable + public Component getDescription() + { + return description; + } + + /** + * Utility Interface used for resolving the default value of PermissionNodes + * + * @param generic value of the PermissionType of a PermissionNode + */ + @FunctionalInterface + public interface PermissionResolver + { + /** + * @param player an online player + * @param playerUUID if the player is null, this UUID belongs to an offline player, + * otherwise it must match the UUID of the passed in player. + * @param context may contain DynamicContext if it was provided + * @return according Permission Value + */ + T resolve(@Nullable ServerPlayer player, UUID playerUUID, PermissionDynamicContext... context); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (!(o instanceof PermissionNode otherNode)) return false; + return nodeName.equals(otherNode.nodeName) && type.equals(otherNode.type); + } + + @Override + public int hashCode() + { + return Objects.hash(nodeName, type); + } +} diff --git a/src/main/java/net/minecraftforge/server/permission/nodes/PermissionType.java b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionType.java new file mode 100644 index 0000000000..aebe85c0b5 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionType.java @@ -0,0 +1,73 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.nodes; + +import java.util.Objects; + +/** + *

Type of a Permission, use the existing Types in {@link PermissionTypes}

+ * + * @implNote this class could be a record but is not in favor of a package private constructor + */ +public final class PermissionType +{ + private final Class typeToken; + private final String typeName; + + PermissionType(Class typeToken, String typeName) + { + this.typeToken = typeToken; + this.typeName = typeName; + } + + public Class typeToken() + { + return typeToken; + } + + public String typeName() + { + return typeName; + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) return true; + if(!(obj instanceof PermissionType otherType)) return false; + return Objects.equals(this.typeToken, otherType.typeToken) && + Objects.equals(this.typeName, otherType.typeName); + } + + @Override + public int hashCode() + { + return Objects.hash(typeToken, typeName); + } + + @Override + public String toString() + { + return "PermissionType[" + + "typeToken=" + typeToken + ", " + + "typeName=" + typeName + ']'; + } + +} diff --git a/src/main/java/net/minecraftforge/server/permission/nodes/PermissionTypes.java b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionTypes.java new file mode 100644 index 0000000000..36073347f0 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/permission/nodes/PermissionTypes.java @@ -0,0 +1,52 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.server.permission.nodes; + +import net.minecraft.network.chat.Component; + +import javax.annotation.Nullable; + +/** + * Default PermissionTypes, if you need additional ones, please PR it. + */ +public final class PermissionTypes +{ + public static final PermissionType BOOLEAN = new PermissionType<>(Boolean.class, "boolean"); + public static final PermissionType INTEGER = new PermissionType<>(Integer.class, "integer"); + public static final PermissionType STRING = new PermissionType<>(String.class, "string"); + public static final PermissionType COMPONENT = new PermissionType<>(Component.class, "component"); + + private PermissionTypes() + { + } + + @Nullable + public static PermissionType getTypeByName(String name) + { + return switch (name) + { + case "boolean" -> BOOLEAN; + case "integer" -> INTEGER; + case "string" -> STRING; + case "component" -> COMPONENT; + default -> null; + }; + } +} diff --git a/src/main/java/net/minecraftforge/server/permission/package-info.java b/src/main/java/net/minecraftforge/server/permission/package-info.java index ea8bf450a4..8f76dd64b7 100644 --- a/src/main/java/net/minecraftforge/server/permission/package-info.java +++ b/src/main/java/net/minecraftforge/server/permission/package-info.java @@ -23,4 +23,4 @@ package net.minecraftforge.server.permission; import net.minecraft.MethodsReturnNonnullByDefault; -import javax.annotation.ParametersAreNonnullByDefault; +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/PermissionTest.java b/src/test/java/net/minecraftforge/debug/PermissionTest.java new file mode 100644 index 0000000000..ee47b7558f --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/PermissionTest.java @@ -0,0 +1,108 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.debug; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.StringRepresentable; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.server.permission.PermissionAPI; +import net.minecraftforge.server.permission.events.PermissionGatherEvent; +import net.minecraftforge.server.permission.nodes.PermissionDynamicContext; +import net.minecraftforge.server.permission.nodes.PermissionNode; +import net.minecraftforge.server.permission.nodes.PermissionTypes; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Mod("permissiontest") +public class PermissionTest +{ + private static final boolean ENABLED = true; + private static final Logger LOGGER = LogManager.getLogger(); + + private static final PermissionNode boolPerm = new PermissionNode<>("permissiontest", "test.blob", PermissionTypes.BOOLEAN, (player, playerUUID, context) -> true); + private static final PermissionNode stringPerm = new PermissionNode<>("permissiontest", "test.blobText", PermissionTypes.STRING, (player, playerUUID, context) -> "Hello World"); + private static final PermissionNode intPerm = new PermissionNode<>("permissiontest", "test.blob.integer", PermissionTypes.INTEGER, (player, playerUUID, context) -> 3); + private static final PermissionNode componentPerm = new PermissionNode<>("permissiontest", "test.blob.component", PermissionTypes.COMPONENT, (player, playerUUID, context) -> new TextComponent("This is a component")); + + private static final PermissionNode unregisteredPerm = new PermissionNode<>("permissiontest", "test.unregistered", PermissionTypes.BOOLEAN, (player, playerUUID, context) -> false); + + + public PermissionTest() + { + if (ENABLED) + { + MinecraftForge.EVENT_BUS.register(this); + } + } + + @SubscribeEvent + public void registerNodes(PermissionGatherEvent.Nodes event) + { + event.addNodes(boolPerm, stringPerm, intPerm, componentPerm); + } + + @SubscribeEvent + public void registerCommands(RegisterCommandsEvent event) + { + event.getDispatcher().register(Commands.literal("permtest") + .requires(src -> canUseCommand(src, boolPerm)) + .executes(context -> { + context.getSource().sendSuccess(new TextComponent("Blob"), false); + context.getSource().sendSuccess(new TextComponent("String:" + PermissionAPI.getPermission((ServerPlayer) context.getSource().getEntity(), stringPerm)), false); + context.getSource().sendSuccess(new TextComponent("Int: " + PermissionAPI.getPermission((ServerPlayer) context.getSource().getEntity(), intPerm)), false); + context.getSource().sendSuccess(PermissionAPI.getPermission((ServerPlayer) context.getSource().getEntity(), componentPerm), false); + + return 1; + })); + + //This command should not be usable as the command isn't registered. + //It should also print an error + event.getDispatcher().register(Commands.literal("permtesterr") + .requires(src -> canUseCommand(src, unregisteredPerm)) + .executes(context -> { + context.getSource().sendSuccess(new TextComponent("Blob"), false); + return 1; + })); + } + + /** + * Simple utility to catch exceptions with the test commands above. + * Without that, the expected UnregisteredPermissionNode exception, triggers further exceptions and therefore isn't visible anymore. + * This is only required to handle the intended error in the permission API, and should not be necessary with correct use. + */ + private static boolean canUseCommand(CommandSourceStack src, PermissionNode booleanPermission, PermissionDynamicContext... context) + { + try + { + return src.getEntity() != null && src.getEntity() instanceof ServerPlayer && PermissionAPI.getPermission((ServerPlayer) src.getEntity(), booleanPermission, context); + } catch (Exception e) + { + LOGGER.error(e.getMessage()); + return false; + } + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 0644ea22bf..3f5dbf987d 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -168,6 +168,8 @@ license="LGPL v2.1" modId="custom_tooltip_test" [[mods]] modId="full_pots_accessor_demo" +[[mods]] + modId="permissiontest" [[mods]] modId="part_entity_test" [[mods]]