diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 2fe8b465d8..239b884363 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -117,6 +117,7 @@ what it is today. * nornalbion * Omar Vera Ustariz (IBM) * openlifegrid.com +* Oren Hurvitz (Kitely) * otakup0pe * ralphos * RemedyTomm diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index fe1525b28f..e5b9dcb4b3 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -269,13 +269,15 @@ namespace OpenSim m_console.Commands.AddCommand("region", false, "save oar", //"save oar [-v|--version=] [-p|--profile=] []", - "save oar [-p|--profile=] [--noassets] []", + "save oar [-p|--profile=] [--noassets] [--perm=] []", "Save a region's data to an OAR archive.", // "-v|--version= generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine "-p|--profile= adds the url of the profile service to the saved user information." + Environment.NewLine - + " The OAR path must be a filesystem path." - + " If this is not given then the oar is saved to region.oar in the current directory." + Environment.NewLine - + "--noassets stops assets being saved to the OAR.", + + "--noassets stops assets being saved to the OAR." + Environment.NewLine + + "--perm stops objects with insufficient permissions from being saved to the OAR." + Environment.NewLine + + " can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer" + Environment.NewLine + + "The OAR path must be a filesystem path." + + " If this is not given then the oar is saved to region.oar in the current directory.", SaveOar); m_console.Commands.AddCommand("region", false, "edit scale", diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs index 10a83ee826..b895afe85d 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs @@ -127,6 +127,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver EntityBase[] entities = m_scene.GetEntities(); List sceneObjects = new List(); + + string checkPermissions = null; + int numObjectsSkippedPermissions = 0; + Object temp; + if (options.TryGetValue("checkPermissions", out temp)) + checkPermissions = (string)temp; // Filter entities so that we only have scene objects. // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods @@ -136,9 +142,19 @@ namespace OpenSim.Region.CoreModules.World.Archiver if (entity is SceneObjectGroup) { SceneObjectGroup sceneObject = (SceneObjectGroup)entity; - + if (!sceneObject.IsDeleted && !sceneObject.IsAttachment) - sceneObjects.Add((SceneObjectGroup)entity); + { + if (!CanUserArchiveObject(m_scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, checkPermissions)) + { + // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR. + ++numObjectsSkippedPermissions; + } + else + { + sceneObjects.Add(sceneObject); + } + } } } @@ -159,7 +175,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver { m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified"); } - + + if (numObjectsSkippedPermissions > 0) + { + m_log.DebugFormat( + "[ARCHIVER]: {0} scene objects skipped due to lack of permissions", + numObjectsSkippedPermissions); + } + // Make sure that we also request terrain texture assets RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings; @@ -210,6 +233,83 @@ namespace OpenSim.Region.CoreModules.World.Archiver } } + /// + /// Checks whether the user has permission to export an object group to an OAR. + /// + /// The user + /// The object group + /// Which permissions to check: "C" = Copy, "T" = Transfer + /// Whether the user is allowed to export the object to an OAR + private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions) + { + if (checkPermissions == null) + return true; + + IPermissionsModule module = m_scene.RequestModuleInterface(); + if (module == null) + return true; // this shouldn't happen + + // Check whether the user is permitted to export all of the parts in the SOG. If any + // part can't be exported then the entire SOG can't be exported. + + bool permitted = true; + //int primNumber = 1; + + foreach (SceneObjectPart obj in objGroup.Parts) + { + uint perm; + PermissionClass permissionClass = module.GetPermissionClass(user, obj); + switch (permissionClass) + { + case PermissionClass.Owner: + perm = obj.BaseMask; + break; + case PermissionClass.Group: + perm = obj.GroupMask | obj.EveryoneMask; + break; + case PermissionClass.Everyone: + default: + perm = obj.EveryoneMask; + break; + } + + bool canCopy = (perm & (uint)PermissionMask.Copy) != 0; + bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0; + + // Special case: if Everyone can copy the object then this implies it can also be + // Transferred. + // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask + // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer + // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied. + if (permissionClass != PermissionClass.Owner) + { + canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0; + } + + + bool partPermitted = true; + if (checkPermissions.Contains("C") && !canCopy) + partPermitted = false; + if (checkPermissions.Contains("T") && !canTransfer) + partPermitted = false; + + //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount); + //m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, permitted={8}", + // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask, + // permissionClass, checkPermissions, canCopy, canTransfer, permitted); + + if (!partPermitted) + { + permitted = false; + break; + } + + //++primNumber; + } + + return permitted; + } + /// /// Create the control file for the most up to date archive /// diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs index 08eb80cc2e..f44a3bac05 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs @@ -128,6 +128,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver // ops.Add("v|version=", delegate(string v) { options["version"] = v; }); ops.Add("p|profile=", delegate(string v) { options["profile"] = v; }); ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; }); + ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; }); List mainParams = ops.Parse(cmdparams); diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs index b9bd9a4ca9..3b661ed71c 100644 --- a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs @@ -39,7 +39,7 @@ using OpenSim.Services.Interfaces; namespace OpenSim.Region.CoreModules.World.Permissions { - public class PermissionsModule : IRegionModule + public class PermissionsModule : IRegionModule, IPermissionsModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -150,6 +150,8 @@ namespace OpenSim.Region.CoreModules.World.Permissions else m_log.Debug("[PERMISSIONS]: Enabling all region service permission checks"); + scene.RegisterModuleInterface(this); + //Register functions with Scene External Checks! m_scene.Permissions.OnBypassPermissions += BypassPermissions; m_scene.Permissions.OnSetBypassPermissions += SetBypassPermissions; @@ -574,46 +576,18 @@ namespace OpenSim.Region.CoreModules.World.Permissions if (objectOwner != UUID.Zero) objectEveryoneMask |= (uint)PrimFlags.ObjectAnyOwner; - if (m_bypassPermissions) - return objectOwnerMask; + PermissionClass permissionClass = GetPermissionClass(user, task); - // Object owners should be able to edit their own content - if (user == objectOwner) - return objectOwnerMask; - - if (IsFriendWithPerms(user, objectOwner)) + switch (permissionClass) { - return objectOwnerMask; - } - // Estate users should be able to edit anything in the sim if RegionOwnerIsGod is set - if (m_RegionOwnerIsGod && IsEstateManager(user) && !IsAdministrator(objectOwner)) - { - return objectOwnerMask; - } - - // Admin should be able to edit anything in the sim (including admin objects) - if (IsAdministrator(user)) - { - return objectOwnerMask; - } - - // Users should be able to edit what is over their land. - Vector3 taskPos = task.AbsolutePosition; - ILandObject parcel = m_scene.LandChannel.GetLandObject(taskPos.X, taskPos.Y); - if (parcel != null && parcel.LandData.OwnerID == user && m_ParcelOwnerIsGod) - { - // Admin objects should not be editable by the above - if (!IsAdministrator(objectOwner)) - { + case PermissionClass.Owner: return objectOwnerMask; - } + case PermissionClass.Group: + return objectGroupMask | objectEveryoneMask; + case PermissionClass.Everyone: + default: + return objectEveryoneMask; } - - // Group permissions - if ((task.GroupID != UUID.Zero) && IsGroupMember(task.GroupID, user, 0)) - return objectGroupMask | objectEveryoneMask; - - return objectEveryoneMask; } private uint ApplyObjectModifyMasks(uint setPermissionMask, uint objectFlagsMask) @@ -644,6 +618,47 @@ namespace OpenSim.Region.CoreModules.World.Permissions return objectFlagsMask; } + public PermissionClass GetPermissionClass(UUID user, SceneObjectPart obj) + { + if (obj == null) + return PermissionClass.Everyone; + + if (m_bypassPermissions) + return PermissionClass.Owner; + + // Object owners should be able to edit their own content + UUID objectOwner = obj.OwnerID; + if (user == objectOwner) + return PermissionClass.Owner; + + if (IsFriendWithPerms(user, objectOwner)) + return PermissionClass.Owner; + + // Estate users should be able to edit anything in the sim if RegionOwnerIsGod is set + if (m_RegionOwnerIsGod && IsEstateManager(user) && !IsAdministrator(objectOwner)) + return PermissionClass.Owner; + + // Admin should be able to edit anything in the sim (including admin objects) + if (IsAdministrator(user)) + return PermissionClass.Owner; + + // Users should be able to edit what is over their land. + Vector3 taskPos = obj.AbsolutePosition; + ILandObject parcel = m_scene.LandChannel.GetLandObject(taskPos.X, taskPos.Y); + if (parcel != null && parcel.LandData.OwnerID == user && m_ParcelOwnerIsGod) + { + // Admin objects should not be editable by the above + if (!IsAdministrator(objectOwner)) + return PermissionClass.Owner; + } + + // Group permissions + if ((obj.GroupID != UUID.Zero) && IsGroupMember(obj.GroupID, user, 0)) + return PermissionClass.Group; + + return PermissionClass.Everyone; + } + /// /// General permissions checks for any operation involving an object. These supplement more specific checks /// implemented by callers. diff --git a/OpenSim/Region/Framework/Interfaces/IPermissionsModule.cs b/OpenSim/Region/Framework/Interfaces/IPermissionsModule.cs new file mode 100644 index 0000000000..1ed978bb44 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IPermissionsModule.cs @@ -0,0 +1,54 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using OpenMetaverse; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + /// + /// Which set of permissions a user has. + /// + public enum PermissionClass + { + Owner, + Group, + Everyone + }; + + public interface IPermissionsModule + { + + /// + /// Returns the type of permissions that the user has over an object. + /// + /// The user + /// The object + /// The type of permissions the user has over the object + PermissionClass GetPermissionClass(UUID user, SceneObjectPart obj); + } +}