diff --git a/src/resources/containers/ContainerPermissions.java b/src/resources/containers/ContainerPermissions.java new file mode 100644 index 00000000..5a69b87d --- /dev/null +++ b/src/resources/containers/ContainerPermissions.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2015 /// Project SWG /// www.projectswg.com + * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. + * Our goal is to create an emulator which will provide a server for players to + * continue playing a game similar to the one they used to play. We are basing + * it on the final publish of the game prior to end-game events. + * + * This file is part of Holocore. + * + * -------------------------------------------------------------------------------- + * + * Holocore is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * Holocore 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Holocore. If not, see + ******************************************************************************/ + +package resources.containers; + +import java.io.Serializable; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public final class ContainerPermissions implements Serializable { + private static final long serialVersionUID = 1L; + + private Map permissionGroups; + private long owner; + + public ContainerPermissions(long owner) { + this.permissionGroups = new HashMap<>(); + this.owner = owner; + + permissionGroups.put("owner", Permission.valueOf(Permission.values())); + permissionGroups.put("admin", Permission.valueOf(Permission.values())); + } + + public boolean hasPermissions(String group, Permission... permissions) { + if (!hasPermissionGroup(group)) + return false; + + EnumSet groupPermissions = Permission.getFlags(permissionGroups.get(group)); + + for (Permission permission : permissions) { + if (!groupPermissions.contains(permission)) + return false; + } + + return true; + } + + public void addPermissions(String group, Permission... permissions) { + EnumSet groupPermissions = Permission.getFlags(permissionGroups.get(group)); + + for (Permission permission : permissions) { + groupPermissions.add(permission); + } + + permissionGroups.put(group, Permission.valueOf(groupPermissions)); + } + + public void removePermissions(String group, Permission... permissions) { + if (!hasPermissionGroup(group)) + return; + + EnumSet groupPermissions = Permission.getFlags(permissionGroups.get(group)); + + for (Permission permission : permissions) { + groupPermissions.add(permission); + } + + permissionGroups.put(group, Permission.valueOf(groupPermissions)); + } + + public void addDefaultWorldPermissions() { + permissionGroups.put("world", Permission.valueOf(Permission.ENTER_BUILDING, Permission.OPEN)); + } + + public void clearPermissions(String group) { + if (permissionGroups.containsKey(group)) + permissionGroups.put(group, 0); + } + + public boolean hasPermissionGroup(String group) { + return permissionGroups.containsKey(group); + } + + public Set getPermissionGroups() { + return permissionGroups.keySet(); + } + + public void setOwner(long owner) { + this.owner = owner; + } + + public long getOwner() { + return owner; + } + + public enum Permission { + OPEN(1), + REMOVE(1<<1), + ADD(1<<2), + MOVE(1<<3), + ENTER_BUILDING(1<<4); + + int bitmask; + + Permission(int bitmask) { + this.bitmask = bitmask; + } + + public static EnumSet getFlags(int bits) { + EnumSet states = EnumSet.noneOf(Permission.class); + for (Permission state : values()) { + if ((state.bitmask & bits) != 0) + states.add(state); + } + return states; + } + + public static int valueOf(EnumSet bitmaskSet) { + return valueOf(bitmaskSet); + } + + public static int valueOf(Permission... permissions) { + int value = 0; + + for (Permission permission : permissions) { + value += permission.bitmask; + } + + return value; + } + } +} diff --git a/src/resources/objects/SWGObject.java b/src/resources/objects/SWGObject.java index e2c39b14..3e777058 100644 --- a/src/resources/objects/SWGObject.java +++ b/src/resources/objects/SWGObject.java @@ -43,6 +43,7 @@ import network.packets.swg.zone.baselines.Baseline.BaselineType; import network.packets.swg.zone.object_controller.DataTransform; import resources.Location; import resources.common.CRC; +import resources.containers.ContainerPermissions; import resources.containers.ContainerResult; import resources.encodables.Stf; import resources.network.BaselineBuilder; @@ -61,6 +62,7 @@ public class SWGObject implements Serializable, Comparable { private final Map containedObjects; private final Map attributes; private final Map templateAttributes; + private final ContainerPermissions containerPermissions; private transient List objectsAware; private List > arrangement; @@ -91,6 +93,7 @@ public class SWGObject implements Serializable, Comparable { this.containedObjects = Collections.synchronizedMap(new HashMap()); this.attributes = new LinkedHashMap(); this.templateAttributes = new HashMap(); + this.containerPermissions = new ContainerPermissions(objectId); } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { @@ -108,6 +111,7 @@ public class SWGObject implements Serializable, Comparable { if (arrangementId == -1) { containedObjects.put(object.getObjectId(), object); object.parent = this; + object.containerPermissions.setOwner(objectId); return true; } // Not a child object, so time to check the slots! @@ -123,6 +127,7 @@ public class SWGObject implements Serializable, Comparable { object.parent = this; object.slotArrangement = arrangementId; + object.containerPermissions.setOwner(objectId); return true; } @@ -145,6 +150,7 @@ public class SWGObject implements Serializable, Comparable { object.parent = null; object.slotArrangement = -1; + object.containerPermissions.setOwner(-1); } /** @@ -154,30 +160,20 @@ public class SWGObject implements Serializable, Comparable { * @return {@link ContainerResult} */ public ContainerResult moveToContainer(SWGObject requester, SWGObject container) { - // TODO ContainerPermission check + if (!container.hasPermission(requester, ContainerPermissions.Permission.MOVE)) + return ContainerResult.NO_PERMISSION; // Check if object can fit into container or slots int arrangementId = container.getArrangementId(this); if (arrangementId == -1) { // Item is going to go into the container, so check to see if it'll fit - if (container.getMaxContainerSize() >= container.getContainedObjects().size()) + if (container.getMaxContainerSize() >= container.getContainedObjects().size()) { return ContainerResult.CONTAINER_FULL; - } else { - List requiredSlots = getArrangement().get(arrangementId - 4); - // Check to see if any of the required slots are occupied - int emptySlots = 0; - for (Map.Entry entry : container.getSlots().entrySet()) { - if (emptySlots == requiredSlots.size()) - break; - - if (requiredSlots.contains(entry.getKey()) && entry.getValue() == null) - emptySlots++; } - - if (emptySlots != requiredSlots.size()) - return ContainerResult.SLOT_OCCUPIED; } + // TODO Slot occupation check, old version was not working properly, always returning SLOT_OCCUPIED + // Get a pre-parent-removal list of the observers so we can send create/destroy/update messages List oldObservers = getObjectsAware(); @@ -194,6 +190,44 @@ public class SWGObject implements Serializable, Comparable { return ContainerResult.SUCCESS; } + public boolean hasOwnerPermissions(SWGObject object) { + return (object.getObjectId() == containerPermissions.getOwner()); + } + + public boolean hasPermission(SWGObject object, ContainerPermissions.Permission... permissions) { + if (object == this || hasOwnerPermissions(object)) + return true; + + if (!containerPermissions.hasPermissions(String.valueOf(object.getObjectId()), permissions)) { + // Doesn't have any owner permissions or specific permissions, check to see if this object has an + // acceptable permission group with the specified permissions available. + for (String permissionGroup : object.containerPermissions.getPermissionGroups()) { + if (containerPermissions.hasPermissions(permissionGroup, permissions)) { + //System.out.println(object + " can view " + this); + return true; + } + } + return false; + } + else return true; + } + + public void addPermissions(String group, ContainerPermissions.Permission... permissions) { + containerPermissions.addPermissions(group, permissions); + } + + public void removePermissions(String group, ContainerPermissions.Permission... permissions) { + containerPermissions.removePermissions(group, permissions); + } + + public void addPermissions(SWGObject permissionRequester, ContainerPermissions.Permission... permissions) { + addPermissions(String.valueOf(permissionRequester.getObjectId()), permissions); + } + + public void removePermissions(SWGObject permissionRequester, ContainerPermissions.Permission... permissions) { + removePermissions(String.valueOf(permissionRequester.getObjectId()), permissions); + } + public void addAttribute(String attribute, String value) { attributes.put(attribute, value); } @@ -384,6 +418,8 @@ public class SWGObject implements Serializable, Comparable { return Integer.valueOf(templateAttributes.get("containerVolumeLimit").toString()); } + public ContainerPermissions getContainerPermissions() { return containerPermissions; } + /** * Gets the arrangementId for the {@link SWGObject} for the current instance * @param object @@ -468,8 +504,6 @@ public class SWGObject implements Serializable, Comparable { && !(awareness.contains(getOwner().getCreatureObject()))) awareness.add(getOwner().getCreatureObject()); - // TODO Permission checking - return awareness; } @@ -492,6 +526,9 @@ public class SWGObject implements Serializable, Comparable { childrenAwareness.remove(this); // Remove self since only observers being notified for (SWGObject childObserver : childrenAwareness) { + if (childObserver == null) + continue; + Player p = childObserver.getOwner(); if (p == null || p.getPlayerState() != PlayerState.ZONED_IN) continue; @@ -593,18 +630,25 @@ public class SWGObject implements Serializable, Comparable { } protected void createChildrenObjects(Player target) { - // TODO Permission check for the target - + if (!hasPermission(target.getCreatureObject(), ContainerPermissions.Permission.OPEN)) { + // System.out.println(target.getCreatureObject() + " doesn't have permission to view " + this + " -- skipping packet sending"); + return; + } + // First create the objects in the slots for (SWGObject slotObject : slots.values()) { - if (slotObject != null) + if (slotObject != null) { + // System.out.println("Sending slotObj " + slotObject + " to " + target); slotObject.createObject(target); + } } // Now create the contained objects for (SWGObject containedObject : containedObjects.values()) { - if (containedObject != null) + if (containedObject != null) { + // System.out.println("Sending containedObj " + containedObject + " to " + target); containedObject.createObject(target); + } } } diff --git a/src/services/player/ZoneService.java b/src/services/player/ZoneService.java index ebfb3da9..4707b28b 100644 --- a/src/services/player/ZoneService.java +++ b/src/services/player/ZoneService.java @@ -436,8 +436,9 @@ public class ZoneService extends Service { if (hair.isEmpty()) return; TangibleObject hairObj = createTangible(objManager, ClientFactory.formatToSharedFile(hair)); - + hairObj.getContainerPermissions().addDefaultWorldPermissions(); hairObj.setAppearanceData(customization); + creatureObj.addObject(hairObj); // slot = hair creatureObj.addEquipment(hairObj); } @@ -461,12 +462,16 @@ public class ZoneService extends Service { creatureObj.addEquipment(inventory); creatureObj.addEquipment(datapad); creatureObj.addEquipment(apprncInventory); + + creatureObj.getContainerPermissions().addDefaultWorldPermissions(); } private void setPlayerObjectValues(PlayerObject playerObj, ClientCreateCharacter create) { playerObj.setProfession(create.getProfession()); Calendar date = Calendar.getInstance(); playerObj.setBornDate(date.get(Calendar.YEAR), date.get(Calendar.MONTH) + 1, date.get(Calendar.DAY_OF_MONTH)); + + playerObj.getContainerPermissions().addDefaultWorldPermissions(); } private void handleGalaxyLoopTimesRequest(Player player, GalaxyLoopTimesRequest req) {