diff --git a/src/main/java/net/TheElm/project/commands/ClaimCommand.java b/src/main/java/net/TheElm/project/commands/ClaimCommand.java
index c6787a9..088a71d 100644
--- a/src/main/java/net/TheElm/project/commands/ClaimCommand.java
+++ b/src/main/java/net/TheElm/project/commands/ClaimCommand.java
@@ -46,6 +46,7 @@
 import net.TheElm.project.enums.ClaimRanks;
 import net.TheElm.project.enums.ClaimSettings;
 import net.TheElm.project.enums.OpLevels;
+import net.TheElm.project.enums.Permissions;
 import net.TheElm.project.exceptions.ExceptionTranslatableServerSide;
 import net.TheElm.project.exceptions.NotEnoughMoneyException;
 import net.TheElm.project.interfaces.ClaimsAccessor;
@@ -147,15 +148,17 @@ public static void register(@NotNull CommandDispatcher<ServerCommandSource> disp
          * Admin Force commands
          */
         ServerCore.register(dispatcher, "Chunk", builder -> builder
-            .requires(CommandPredicate.opLevel(OpLevels.STOP))
+            .requires(CommandPredicate.opLevel(OpLevels.STOP).or(Permissions.ADMIN_CLAIMS).or(Permissions.ADMIN_CLAIM_TOWNS))
             .then(CommandManager.literal("set")
                 .then(CommandManager.literal("player")
+                    .requires(CommandPredicate.opLevel(OpLevels.STOP).or(Permissions.ADMIN_CLAIMS))
                     .then(CommandManager.argument("target", GameProfileArgumentType.gameProfile())
                         .suggests(CommandUtils::getAllPlayerNames)
                         .executes(ClaimCommand::rawSetChunkPlayer)
                     )
                 )
                 .then(CommandManager.literal("town")
+                    .requires(CommandPredicate.opLevel(OpLevels.STOP).or(Permissions.ADMIN_CLAIM_TOWNS))
                     .executes(ClaimCommand::rawSetChunkTown)
                 )
             )
@@ -340,7 +343,7 @@ public static void register(@NotNull CommandDispatcher<ServerCommandSource> disp
                 .executes(ClaimCommand::playerPartsTown)
             )
             .then(CommandManager.literal("set")
-                .requires(CommandPredicate.opLevel(OpLevels.STOP))
+                .requires(CommandPredicate.opLevel(OpLevels.STOP).or(Permissions.ADMIN_CLAIM_TOWNS))
                 .then(CommandManager.argument("target", GameProfileArgumentType.gameProfile())
                     .suggests(CommandUtils::getAllPlayerNames)
                     .then(CommandManager.argument("town", StringArgumentType.greedyString())
@@ -350,7 +353,7 @@ public static void register(@NotNull CommandDispatcher<ServerCommandSource> disp
                 )
             )
             .then(CommandManager.literal("villagers")
-                .requires(CommandPredicate.opLevel(OpLevels.STOP))
+                .requires(CommandPredicate.opLevel(OpLevels.STOP).or(Permissions.ADMIN_CLAIM_TOWNS))
                 .then(CommandManager.argument("entities", EntityArgumentType.entities())
                     .then(CommandManager.argument("town", StringArgumentType.greedyString())
                         .suggests(CommandUtils::getAllTowns)
diff --git a/src/main/java/net/TheElm/project/commands/ModCommands.java b/src/main/java/net/TheElm/project/commands/ModCommands.java
index 4acb8ad..079eb45 100644
--- a/src/main/java/net/TheElm/project/commands/ModCommands.java
+++ b/src/main/java/net/TheElm/project/commands/ModCommands.java
@@ -37,6 +37,7 @@
 import net.TheElm.project.blocks.entities.LecternGuideBlockEntity;
 import net.TheElm.project.config.SewConfig;
 import net.TheElm.project.enums.OpLevels;
+import net.TheElm.project.enums.Permissions;
 import net.TheElm.project.interfaces.CommandPredicate;
 import net.TheElm.project.interfaces.ShopSignData;
 import net.TheElm.project.utilities.BlockUtils;
@@ -84,8 +85,9 @@ private ModCommands() {
     
     public static void register(@NotNull CommandDispatcher<ServerCommandSource> dispatcher) {
         ServerCore.register(dispatcher, CoreMod.MOD_ID, (builder) -> builder
-            .requires(CommandPredicate.opLevel(OpLevels.STOP))
+            .requires(CommandPredicate.opLevel(OpLevels.CHEATING).or(Permissions.ADMIN_CLAIM_SHOPS))
             .then(CommandManager.literal("reload")
+                .requires(CommandPredicate.opLevel(OpLevels.STOP).or(Permissions.ALL_PERMISSIONS))
                 .then(CommandManager.literal("config")
                     .executes(ModCommands::reloadConfig)
                 )
@@ -95,6 +97,7 @@ public static void register(@NotNull CommandDispatcher<ServerCommandSource> disp
                 )
             )
             .then(CommandManager.literal("shops")
+                .requires(CommandPredicate.opLevel(OpLevels.CHEATING).or(Permissions.ADMIN_CLAIM_SHOPS))
                 .then(CommandManager.literal("change")
                     .then(CommandManager.literal("item")
                         .then(CommandManager.literal("hand")
@@ -123,6 +126,7 @@ public static void register(@NotNull CommandDispatcher<ServerCommandSource> disp
                 )
             )
             .then(CommandManager.literal("guides")
+                .requires(CommandPredicate.opLevel(OpLevels.CHEATING))
                 .then(CommandManager.argument("book", StringArgumentType.string())
                     .suggests(((context, suggestionsBuilder) -> CommandSource.suggestMatching(GuideUtils.getBooks(), suggestionsBuilder)))
                     .then(CommandManager.literal("give")
diff --git a/src/main/java/net/TheElm/project/enums/Permissions.java b/src/main/java/net/TheElm/project/enums/Permissions.java
index 38353df..486aca3 100644
--- a/src/main/java/net/TheElm/project/enums/Permissions.java
+++ b/src/main/java/net/TheElm/project/enums/Permissions.java
@@ -47,7 +47,10 @@
 public class Permissions {
     private static final Set<PermissionNode> PERMISSIONS = new HashSet<>();
     
-    public static final @NotNull PermissionNode ALL_PERMISSIONS = addInflictable("*", "");
+    public static final @NotNull PermissionNode ALL_PERMISSIONS = addInflictable("*", "Access to all permissions.");
+    public static final @NotNull PermissionNode ADMIN_CLAIMS = addPermission("claims.admin", "Allows admin control over regular player claims");
+    public static final @NotNull PermissionNode ADMIN_CLAIM_TOWNS = addPermission("claims.admin.towns", "Allows admin control of towns, town chunks, and players in towns");
+    public static final @NotNull PermissionNode ADMIN_CLAIM_SHOPS = addPermission("claims.admin.shops", "Allows admin control of shops and shop signs");
     
     /*
      * Self-Inflicting player permissions
diff --git a/src/main/java/net/TheElm/project/objects/ShopCraftAction.java b/src/main/java/net/TheElm/project/objects/ShopCraftAction.java
new file mode 100644
index 0000000..90ee8fd
--- /dev/null
+++ b/src/main/java/net/TheElm/project/objects/ShopCraftAction.java
@@ -0,0 +1,95 @@
+/*
+ * This software is licensed under the MIT License
+ * https://github.com/GStefanowich/MC-Server-Protection
+ *
+ * Copyright (c) 2019 Gregory Stefanowich
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package net.TheElm.project.objects;
+
+import net.TheElm.project.interfaces.ShopSignData;
+import net.TheElm.project.utilities.InventoryUtils;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.recipe.Ingredient;
+import net.minecraft.recipe.Recipe;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.registry.Registry;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * Created on Apr 25 2022 at 6:35 PM.
+ * By greg in SewingMachineMod
+ */
+public final class ShopCraftAction {
+    private final @NotNull Identifier input;
+    
+    private final int inputCount;
+    private final int remainder;
+    
+    private final @NotNull ShopSignData shop;
+    private final @NotNull BlockPos shopPos;
+    private final @NotNull Inventory container;
+    
+    public ShopCraftAction(@NotNull Recipe<?> recipe, @NotNull ShopSignData shop, @NotNull BlockPos signPos, @NotNull Inventory inventory) {
+        this.shop = shop;
+        this.shopPos = signPos;
+        this.container = inventory;
+        
+        // Get the ITEM that is used for the craft
+        Ingredient ingredient = recipe.getIngredients()
+            .get(0);
+        ItemStack stack = ingredient.getMatchingStacks()[0];
+        this.input = Registry.ITEM.getId(stack.getItem());
+        
+        int needs = 0;
+        int outputs = 0;
+        
+        while (outputs < shop.getShopItemCount()) {
+            needs += stack.getCount();
+            outputs += recipe.getOutput()
+                .getCount();
+        }
+        
+        this.inputCount = needs;
+        this.remainder = outputs - shop.getShopItemCount();
+    }
+    
+    private boolean itemMatchesInput(ItemStack stack) {
+        return stack != null && Objects.equals(this.input, Registry.ITEM.getId(stack.getItem()));
+    }
+    
+    public boolean craft(@NotNull ServerPlayerEntity player) {
+        if (!InventoryUtils.playerToChest(player, this.shopPos, player.getInventory(), this.container, this::itemMatchesInput, this.inputCount, true))
+            return false;
+        
+        if (this.remainder > 0) {
+            player.getInventory()
+                .offerOrDrop(new ItemStack(this.shop.getShopItem(), this.remainder));
+        }
+        
+        return true;
+    }
+}