Search in sources :

Example 1 with InvalidStructure

use of eelfloat.replcraft.exceptions.InvalidStructure in project replcraft by LeeFlemingRepl.

the class StructureInteractions method onPlayerInteract.

@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
    Player player = event.getPlayer();
    if (!player.isSneaking() || event.getAction() != Action.RIGHT_CLICK_BLOCK)
        return;
    Block block = event.getClickedBlock();
    UUID uuid = player.getUniqueId();
    if (block == null)
        return;
    ItemStack item = player.getInventory().getItemInMainHand();
    if (item.getType() == Material.STICK) {
        boolean tryPrint = false;
        try {
            Structure structure = StructureUtil.verifyStructure(block);
            if (structure.location_equals(boundStructures.get(uuid))) {
                tryPrint = true;
            } else {
                boundStructures.put(uuid, structure);
                player.sendMessage(String.format("%s bound. Shift-right-click with stick again inside to print block info.", structure));
            }
        } catch (InvalidStructure ex) {
            tryPrint = true;
        }
        if (tryPrint) {
            Structure structure = boundStructures.get(uuid);
            if (structure == null) {
                player.sendMessage(String.format("%sNo structure bound%s. Shift-right-click with stick on a valid structure wall first.", ChatColor.RED, ChatColor.WHITE));
                return;
            }
            player.sendMessage(String.format("%s===%s Structure %s===%s\n%sStructure:%s %s\n%sRelative coordinates:%s %d %d %d\n%sBlock string:%s \"%s%s%s\"", ChatColor.GRAY, ChatColor.WHITE, ChatColor.GRAY, ChatColor.WHITE, ChatColor.GRAY, ChatColor.WHITE, structure, ChatColor.GRAY, ChatColor.WHITE, block.getX() - structure.inner_min_x(), block.getY() - structure.inner_min_y(), block.getZ() - structure.inner_min_z(), ChatColor.GRAY, ChatColor.WHITE, ChatColor.GOLD, block.getBlockData().getAsString(), ChatColor.WHITE));
        }
        return;
    }
    if (!(block.getState() instanceof org.bukkit.block.Sign))
        return;
    try {
        AtomicReference<String> checkedUsername = new AtomicReference<>();
        AtomicReference<String> usedPermission = new AtomicReference<>();
        boolean permAdmin = player.hasPermission("replcraft.auth.admin");
        boolean permPublic = player.hasPermission("replcraft.auth.public");
        boolean permSelf = player.hasPermission("replcraft.auth.self");
        StructureUtil.verifySign(block, uuid, username -> {
            checkedUsername.set(username);
            if (username.equals("@ADMIN")) {
                usedPermission.set("admin");
                return permAdmin;
            }
            if (username.equals("@PUBLIC")) {
                usedPermission.set("public");
                return permPublic;
            }
            if (username.equals(player.getName())) {
                usedPermission.set("player");
                return permSelf;
            }
            if (username.matches("[A-Za-z0-9]+")) {
                usedPermission.set("admin");
                return permAdmin;
            }
            return false;
        });
        Claims claims = Jwts.claims();
        claims.put("host", ReplCraft.plugin.public_address);
        claims.put("world", block.getWorld().getName());
        claims.put("x", block.getX());
        claims.put("y", block.getY());
        claims.put("z", block.getZ());
        claims.put("uuid", uuid.toString());
        claims.put("username", checkedUsername.get());
        claims.put("permission", usedPermission.get());
        String jws = Jwts.builder().setClaims(claims).signWith(ReplCraft.plugin.key).compact();
        player.sendMessage("Your token (click to copy): " + jws);
        player.sendMessage("Keep it secret! To revoke this token, break the sign.");
    } catch (InvalidStructure ex) {
        player.sendMessage(ex.getMessage());
    }
}
Also used : Player(org.bukkit.entity.Player) InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure) Claims(io.jsonwebtoken.Claims) AtomicReference(java.util.concurrent.atomic.AtomicReference) Block(org.bukkit.block.Block) UUID(java.util.UUID) ItemStack(org.bukkit.inventory.ItemStack) Structure(eelfloat.replcraft.Structure) InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure) EventHandler(org.bukkit.event.EventHandler)

Example 2 with InvalidStructure

use of eelfloat.replcraft.exceptions.InvalidStructure in project replcraft by LeeFlemingRepl.

the class WebsocketServer method toClientError.

private JSONObject toClientError(Exception ex, String nonce) {
    JSONObject json = new JSONObject();
    json.put("ok", false);
    json.put("nonce", nonce);
    if (ex instanceof JSONException) {
        json.put("error", "bad request");
        json.put("message", ex.getMessage());
    } else if (ex instanceof InvalidStructure) {
        json.put("error", "invalid structure");
        json.put("message", ex.getMessage());
    } else if (ex instanceof ApiError) {
        json.put("error", ((ApiError) ex).type);
        json.put("message", ((ApiError) ex).message);
    } else {
        json.put("error", "internal error");
        json.put("message", "an internal server error occurred (this is a bug)");
        ex.printStackTrace();
    }
    return json;
}
Also used : InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure) JSONObject(org.json.JSONObject) JSONException(org.json.JSONException) ApiError(eelfloat.replcraft.exceptions.ApiError)

Example 3 with InvalidStructure

use of eelfloat.replcraft.exceptions.InvalidStructure in project replcraft by LeeFlemingRepl.

the class StructureUtil method verifyToken.

/**
 * Verifies a token and the associated structure
 * @param json_web_token the jwt to verify
 * @throws InvalidStructure if the structure is invalid or missing
 * @return a validated structure
 */
public static Structure verifyToken(String json_web_token) throws InvalidStructure {
    try {
        Claims body = Jwts.parserBuilder().setSigningKey(ReplCraft.plugin.key).build().parseClaimsJws(json_web_token).getBody();
        String worldName = body.get("world", String.class);
        World world = ReplCraft.plugin.getServer().getWorld(worldName);
        if (world == null)
            throw new InvalidStructure("Invalid world");
        int x = body.get("x", Integer.class);
        int y = body.get("y", Integer.class);
        int z = body.get("z", Integer.class);
        String username = body.get("username", String.class);
        UUID uuid = UUID.fromString(body.get("uuid", String.class));
        OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
        String permission = String.format("replcraft.auth.%s", body.get("permission", String.class));
        if (!ReplCraft.plugin.permissionProvider.hasPermission(offlinePlayer, world, permission)) {
            String issue = String.format("This token was issued when you held the `%s` permission, but you no longer have it.", permission);
            throw new ApiError("unauthenticated", issue);
        }
        return verifySign(world.getBlockAt(x, y, z), uuid, username::equals);
    } catch (JwtException ex) {
        throw new InvalidStructure("Token is invalid: " + ex.getMessage(), ex);
    } catch (ApiError e) {
        throw new InvalidStructure("Token is invalid: " + e.message);
    }
}
Also used : InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure) Claims(io.jsonwebtoken.Claims) JwtException(io.jsonwebtoken.JwtException) ApiError(eelfloat.replcraft.exceptions.ApiError)

Example 4 with InvalidStructure

use of eelfloat.replcraft.exceptions.InvalidStructure in project replcraft by LeeFlemingRepl.

the class StructureUtil method verifySign.

/**
 * Verifies a sign and the attached structure
 * @param block the block containing the sign.
 * @param usernameValidator A function that validates the username on the sign.
 * @throws InvalidStructure if the sign or structure is invalid
 * @return the validated structure
 */
public static Structure verifySign(Block block, UUID minecraft_uuid, Predicate<String> usernameValidator) throws InvalidStructure {
    BlockState state = block.getState();
    if (!(state instanceof org.bukkit.block.Sign)) {
        throw new InvalidStructure("Block is not a sign.");
    }
    BlockData data = block.getBlockData();
    if (!(data instanceof WallSign)) {
        throw new InvalidStructure("Sign must be on a wall.");
    }
    String[] lines = ((org.bukkit.block.Sign) state).getLines();
    assert lines.length == 4;
    String repl = lines[0];
    // Note: No way to actually enforce these at the moment, that's why we're using auth tokens instead
    String replit_username = lines[1];
    String repl_id = lines[2];
    String minecraft_username = lines[3];
    if (!repl.equals("REPL")) {
        throw new InvalidStructure("Line 1 of sign is not \"REPL\"");
    }
    if (!usernameValidator.test(minecraft_username)) {
        throw new InvalidStructure("Missing permissions for this sign. Does your username match line 4?");
    }
    Structure structure = verifyStructure(block.getRelative(((WallSign) data).getFacing().getOppositeFace()));
    structure.sign = block;
    structure.replit_username = replit_username;
    structure.replit_repl_id = repl_id;
    structure.minecraft_username = minecraft_username;
    structure.minecraft_uuid = minecraft_uuid;
    return structure;
}
Also used : WallSign(org.bukkit.block.data.type.WallSign) InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure) BlockState(org.bukkit.block.BlockState) org.bukkit(org.bukkit) WallSign(org.bukkit.block.data.type.WallSign) BlockData(org.bukkit.block.data.BlockData) Structure(eelfloat.replcraft.Structure) InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure)

Example 5 with InvalidStructure

use of eelfloat.replcraft.exceptions.InvalidStructure in project replcraft by LeeFlemingRepl.

the class StructureUtil method verifyStructure.

/**
 * Verifies the structure attached to a sign
 * @param starting_block A block on the frame of the structure
 * @throws InvalidStructure if the structure is invalid
 * @return a partially validated structure, missing its sign field
 */
public static Structure verifyStructure(Block starting_block) throws InvalidStructure {
    // A list of all seen frame blocks, attached chests, and signs.
    HashSet<Location> seen = new HashSet<>();
    Stack<Location> to_explore = new Stack<>();
    int min_x = starting_block.getX();
    int min_y = starting_block.getY();
    int min_z = starting_block.getZ();
    int max_x = starting_block.getX();
    int max_y = starting_block.getY();
    int max_z = starting_block.getZ();
    List<Block> chests = new ArrayList<>();
    to_explore.push(starting_block.getLocation());
    StructureMaterial material = null;
    int explored = 0;
    while (!to_explore.isEmpty()) {
        explored += 1;
        if (material == null && explored > 100) {
            throw new InvalidStructure("Too many connected blocks before finding any valid structure material.");
        }
        if (material != null && explored > material.max_size) {
            throw new InvalidStructure(String.format("Too many connected %s blocks. The limit is %d for this material type.", material.name, material.max_size));
        }
        Block block = to_explore.pop().getBlock();
        for (BlockFace face : BlockFace.values()) {
            if (!face.isCartesian())
                continue;
            Block relative = block.getRelative(face);
            Location location = relative.getLocation();
            if (relative.getType() == Material.CHEST) {
                seen.add(location);
                chests.add(relative);
            }
            boolean isValidStructureMaterial = material == null ? ReplCraft.plugin.frame_materials.stream().flatMap(mats -> mats.validMaterials.stream()).anyMatch(mat -> relative.getType() == mat) : material.validMaterials.contains(relative.getType());
            if (isValidStructureMaterial && !seen.contains(location)) {
                if (material == null) {
                    material = ReplCraft.plugin.frame_materials.stream().filter(mats -> mats.validMaterials.contains(relative.getType())).findFirst().orElseThrow(() -> new RuntimeException("unreachable"));
                    ReplCraft.plugin.logger.info("Structure type determined to be " + material.name);
                }
                seen.add(location);
                to_explore.push(location);
                min_x = Math.min(min_x, location.getBlockX());
                min_y = Math.min(min_y, location.getBlockY());
                min_z = Math.min(min_z, location.getBlockZ());
                max_x = Math.max(max_x, location.getBlockX());
                max_y = Math.max(max_y, location.getBlockY());
                max_z = Math.max(max_z, location.getBlockZ());
            }
        }
    }
    ReplCraft.plugin.logger.info(String.format("Structure min [%s %s %s] max [%s %s %s]", min_x, min_y, min_z, max_x, max_y, max_z));
    if (material == null) {
        throw new InvalidStructure("Structure must contain at least one valid structure block from any structure type.");
    }
    // Top edges
    // 1
    check_axis(material, new Location(starting_block.getWorld(), min_x, max_y, max_z), 1, 0, 0, max_x - min_x);
    // 2
    check_axis(material, new Location(starting_block.getWorld(), max_x, max_y, max_z), 0, 0, -1, max_z - min_z);
    // 3
    check_axis(material, new Location(starting_block.getWorld(), max_x, max_y, min_z), -1, 0, 0, max_x - min_x);
    // 4
    check_axis(material, new Location(starting_block.getWorld(), min_x, max_y, min_z), 0, 0, 1, max_z - min_z);
    // Side edges
    // 5
    check_axis(material, new Location(starting_block.getWorld(), max_x, min_y, max_z), 0, 1, 0, max_y - min_y);
    // 6
    check_axis(material, new Location(starting_block.getWorld(), max_x, min_y, min_z), 0, 1, 0, max_y - min_y);
    // 7
    check_axis(material, new Location(starting_block.getWorld(), min_x, min_y, min_z), 0, 1, 0, max_y - min_y);
    // 8
    check_axis(material, new Location(starting_block.getWorld(), min_x, min_y, max_z), 0, 1, 0, max_y - min_y);
    // Bottom edges
    // 9
    check_axis(material, new Location(starting_block.getWorld(), min_x, min_y, max_z), 1, 0, 0, max_x - min_x);
    // 10
    check_axis(material, new Location(starting_block.getWorld(), max_x, min_y, max_z), 0, 0, -1, max_z - min_z);
    // 11
    check_axis(material, new Location(starting_block.getWorld(), max_x, min_y, min_z), -1, 0, 0, max_x - min_x);
    // 12
    check_axis(material, new Location(starting_block.getWorld(), min_x, min_y, min_z), 0, 0, 1, max_z - min_z);
    Structure structure = new Structure(material, null, null, null, null, null, seen, chests, min_x, min_y, min_z, max_x, max_y, max_z);
    if (structure.inner_size_x() < 1 || structure.inner_size_y() < 1 || structure.inner_size_z() < 1) {
        throw new InvalidStructure("Structure must have a nonzero interior size.");
    }
    return structure;
}
Also used : java.util(java.util) BlockData(org.bukkit.block.data.BlockData) Predicate(java.util.function.Predicate) Structure(eelfloat.replcraft.Structure) BlockState(org.bukkit.block.BlockState) ApiError(eelfloat.replcraft.exceptions.ApiError) WallSign(org.bukkit.block.data.type.WallSign) BlockFace(org.bukkit.block.BlockFace) StructureMaterial(eelfloat.replcraft.StructureMaterial) Collectors(java.util.stream.Collectors) org.bukkit(org.bukkit) Claims(io.jsonwebtoken.Claims) Jwts(io.jsonwebtoken.Jwts) Block(org.bukkit.block.Block) InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure) ReplCraft(eelfloat.replcraft.ReplCraft) JwtException(io.jsonwebtoken.JwtException) InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure) BlockFace(org.bukkit.block.BlockFace) StructureMaterial(eelfloat.replcraft.StructureMaterial) Block(org.bukkit.block.Block) Structure(eelfloat.replcraft.Structure) InvalidStructure(eelfloat.replcraft.exceptions.InvalidStructure)

Aggregations

InvalidStructure (eelfloat.replcraft.exceptions.InvalidStructure)6 Structure (eelfloat.replcraft.Structure)3 ApiError (eelfloat.replcraft.exceptions.ApiError)3 Claims (io.jsonwebtoken.Claims)3 JwtException (io.jsonwebtoken.JwtException)2 org.bukkit (org.bukkit)2 Block (org.bukkit.block.Block)2 BlockFace (org.bukkit.block.BlockFace)2 BlockState (org.bukkit.block.BlockState)2 BlockData (org.bukkit.block.data.BlockData)2 WallSign (org.bukkit.block.data.type.WallSign)2 ReplCraft (eelfloat.replcraft.ReplCraft)1 StructureMaterial (eelfloat.replcraft.StructureMaterial)1 Jwts (io.jsonwebtoken.Jwts)1 java.util (java.util)1 UUID (java.util.UUID)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 Predicate (java.util.function.Predicate)1 Collectors (java.util.stream.Collectors)1 Location (org.bukkit.Location)1