reviewing the tags permission system
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Thu, 18 Nov 2010 19:16:19 +0000 (20:16 +0100)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Thu, 18 Nov 2010 19:16:19 +0000 (20:16 +0100)
tag_types don't have a min_role_id anymore but a set of roles
new methods AddRoleToTagType and DeleteRoleFromTagType to handle that
new file PLC/AuthorizeHelpers.py has helpers to implement authorizations
this is a checkpoint commit - remaining todo:
- slice tags methods are unchanged yet
- accessors and factory still use min_role_id

43 files changed:
PLC/Accessor.py
PLC/Faults.py
PLC/Ilinks.py
PLC/InterfaceTags.py
PLC/Interfaces.py
PLC/Methods/AddIlink.py
PLC/Methods/AddInterface.py
PLC/Methods/AddInterfaceTag.py
PLC/Methods/AddNodeTag.py
PLC/Methods/AddPerson.py
PLC/Methods/AddPersonTag.py
PLC/Methods/AddRoleToPerson.py
PLC/Methods/AddRoleToTagType.py [new file with mode: 0644]
PLC/Methods/AddSiteTag.py
PLC/Methods/AddSliceTag.py
PLC/Methods/AddTagType.py
PLC/Methods/DeleteIlink.py
PLC/Methods/DeleteInterface.py
PLC/Methods/DeleteInterfaceTag.py
PLC/Methods/DeleteNodeTag.py
PLC/Methods/DeletePersonTag.py
PLC/Methods/DeleteRoleFromPerson.py
PLC/Methods/DeleteRoleFromTagType.py [new file with mode: 0644]
PLC/Methods/DeleteSite.py
PLC/Methods/DeleteSiteTag.py
PLC/Methods/DeleteSliceTag.py
PLC/Methods/UpdateIlink.py
PLC/Methods/UpdateInterfaceTag.py
PLC/Methods/UpdateNodeTag.py
PLC/Methods/UpdatePersonTag.py
PLC/Methods/UpdateSiteTag.py
PLC/Methods/UpdateSliceTag.py
PLC/Methods/UpdateTagType.py
PLC/NodeTags.py
PLC/PersonTags.py
PLC/Roles.py
PLC/SiteTags.py
PLC/Sites.py
PLC/SliceTags.py
PLC/TagTypes.py
migrations/104-down-noderole.sql [new file with mode: 0644]
migrations/104-up-noderole.sql [new file with mode: 0644]
planetlab5.sql

index ea69ccb..0a499b5 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
index 3309cb6..cebe5c5 100644 (file)
@@ -5,8 +5,6 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 #
 # Copyright (C) 2004-2006 The Trustees of Princeton University
-# $Id$
-# $URL$
 #
 
 import xmlrpclib
index 8cd97ae..d99f13c 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
index 976a54e..4dcb531 100644 (file)
@@ -28,7 +28,6 @@ class InterfaceTag(Row):
         'tagname': TagType.fields['tagname'],
         'description': TagType.fields['description'],
         'category': TagType.fields['category'],
-        'min_role_id': TagType.fields['min_role_id'],
         'value': Parameter(str, "Interface setting value"),
         ### relations
 
index 2ea645f..c8ccd2e 100644 (file)
@@ -4,9 +4,6 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id$
-# $URL$
-#
 
 from types import StringTypes
 import socket
@@ -227,6 +224,12 @@ class Interface(Row):
     def update_last_updated(self, commit = True):
         self.update_timestamp('last_updated', commit)
 
+    def delete(self,commit=True):
+        ### need to cleanup ilinks
+        self.api.db.do("DELETE FROM ilink WHERE src_interface_id=%d OR dst_interface_id=%d" % \
+                           (self['interface_id'],self['interface_id']))
+        
+        Row.delete(self)
 
 class Interfaces(Table):
     """
index 6316805..c9d18b1 100644 (file)
@@ -1,10 +1,6 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 9423 $
-#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
@@ -13,9 +9,10 @@ from PLC.Auth import Auth
 from PLC.TagTypes import TagType, TagTypes
 from PLC.Ilinks import Ilink, Ilinks
 from PLC.Interfaces import Interface, Interfaces
-
 from PLC.Sites import Sites
 
+from PLC.AuthorizeHelpers import AuthorizeHelpers
+
 class AddIlink(Method):
     """
     Create a link between two interfaces
@@ -65,21 +62,18 @@ class AddIlink(Method):
             raise PLCInvalidArgument, "Ilink (%s,%d,%d) already exists and has value %r"\
                 %(tag_type['name'],src_if_id,dst_if_id,ilink['value'])
 
-        if 'admin' not in self.caller['roles']:
-#       # check permission : it not admin, is the user affiliated with the right site(s) ????
-#           # locate node
-#           node = Nodes (self.api,[node['node_id']])[0]
-#           # locate site
-#           site = Sites (self.api, [node['site_id']])[0]
-#           # check caller is affiliated with this site
-#           if self.caller['person_id'] not in site['person_ids']:
-#               raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified ilink, requires role %d",required_min_role
-
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.interface_belongs_to_person (self.api, src_if, self.caller):
+            pass
+        elif src_if_id != dst_if_id and AuthorizeHelpers.interface_belongs_to_person (self.api, dst_if, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must one either the src or dst interface"%self.name
+            
         ilink = Ilink(self.api)
         ilink['tag_type_id'] = tag_type['tag_type_id']
         ilink['src_interface_id'] = src_if_id
index e98d545..ec35fc7 100644 (file)
@@ -1,5 +1,6 @@
-# $Id$
-# $URL$
+#
+# Thierry Parmentelat - INRIA
+#
 from PLC.Faults import *
 from PLC.Auth import Auth
 from PLC.Method import Method
index 23aaeaf..912e147 100644 (file)
@@ -1,29 +1,25 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision$
-#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
+from PLC.Sites import Sites
+from PLC.Nodes import Nodes
+from PLC.Interfaces import Interface, Interfaces
 from PLC.TagTypes import TagType, TagTypes
 from PLC.InterfaceTags import InterfaceTag, InterfaceTags
-from PLC.Interfaces import Interface, Interfaces
-
-from PLC.Nodes import Nodes
-from PLC.Sites import Sites
 
 class AddInterfaceTag(Method):
     """
     Sets the specified setting for the specified interface
     to the specified value.
 
-    In general only tech(s), PI(s) and of course admin(s) are allowed to
-    do the change, but this is defined in the tag type object.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns the new interface_tag_id (> 0) if successful, faults
     otherwise.
@@ -42,9 +38,6 @@ class AddInterfaceTag(Method):
 
     returns = Parameter(int, 'New interface_tag_id (> 0) if successful')
 
-    object_type = 'Interface'
-
-
     def call(self, auth, interface_id, tag_type_id_or_name, value):
         interfaces = Interfaces(self.api, [interface_id])
         if not interfaces:
@@ -65,20 +58,16 @@ class AddInterfaceTag(Method):
             raise PLCInvalidArgument, "Interface %d already has setting %d"%(interface['interface_id'],
                                                                                tag_type['tag_type_id'])
 
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # locate node
-            node = Nodes (self.api,[interface['node_id']])[0]
-            # locate site
-            site = Sites (self.api, [node['site_id']])[0]
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified interface setting, requires role %d",required_min_role
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.interface_belongs_to_person (self.api, interface, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must belong in the same site as subject interface"%self.name
+        
 
         interface_tag = InterfaceTag(self.api)
         interface_tag['interface_id'] = interface['interface_id']
index f02181b..43e075f 100644 (file)
@@ -1,10 +1,6 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 9423 $
-#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
@@ -15,14 +11,16 @@ from PLC.Nodes import Node, Nodes
 from PLC.TagTypes import TagType, TagTypes
 from PLC.NodeTags import NodeTag, NodeTags
 
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class AddNodeTag(Method):
     """
     Sets the specified tag for the specified node
     to the specified value.
 
-    In general only tech(s), PI(s) and of course admin(s) are allowed to
-    do the change, but this is defined in the node tag type object.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns the new node_tag_id (> 0) if successful, faults
     otherwise.
@@ -42,9 +40,6 @@ class AddNodeTag(Method):
 
     returns = Parameter(int, 'New node_tag_id (> 0) if successful')
 
-    object_type = 'Node'
-
-
     def call(self, auth, node_id, tag_type_id_or_name, value):
         nodes = Nodes(self.api, [node_id])
         if not nodes:
@@ -63,22 +58,19 @@ class AddNodeTag(Method):
 
         if len(conflicts) :
             raise PLCInvalidArgument, "Node %d already has tag %d"%(node['node_id'],
-                                                                               tag_type['tag_type_id'])
-
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # locate node
-            node = Nodes (self.api,[node['node_id']])[0]
-            # locate site
-            site = Sites (self.api, [node['site_id']])[0]
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified node tag, requires role %d"%required_min_role
+                                                                    tag_type['tag_type_id'])
+
+
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.node_belongs_to_person (self.api, node, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must belong in the same site as subject node"%self.name
+
 
         node_tag = NodeTag(self.api)
         node_tag['node_id'] = node['node_id']
index 7c4d67d..8840975 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
index 7f22bee..6bba377 100644 (file)
@@ -1,28 +1,23 @@
-# $Id: AddPersonTag.py 14587 2009-07-19 13:18:50Z thierry $
-# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/AddPersonTag.py $
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 14587 $
-#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
+from PLC.Persons import Person, Persons
 from PLC.TagTypes import TagType, TagTypes
 from PLC.PersonTags import PersonTag, PersonTags
-from PLC.Persons import Person, Persons
 
-from PLC.Nodes import Nodes
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class AddPersonTag(Method):
     """
     Sets the specified setting for the specified person
     to the specified value.
 
-    In general only tech(s), PI(s) and of course admin(s) are allowed to
-    do the change, but this is defined in the tag type object.
+    Admins have full access.  Non-admins can change their own tags.
 
     Returns the new person_tag_id (> 0) if successful, faults
     otherwise.
@@ -41,9 +36,6 @@ class AddPersonTag(Method):
 
     returns = Parameter(int, 'New person_tag_id (> 0) if successful')
 
-    object_type = 'Person'
-
-
     def call(self, auth, person_id, tag_type_id_or_name, value):
         persons = Persons(self.api, [person_id])
         if not persons:
@@ -56,24 +48,22 @@ class AddPersonTag(Method):
         tag_type = tag_types[0]
 
         # checks for existence - does not allow several different settings
-        conflicts = PersonTags(self.api,
-                                        {'person_id':person['person_id'],
-                                         'tag_type_id':tag_type['tag_type_id']})
+        conflicts = PersonTags(self.api, {'person_id':person['person_id'],
+                                          'tag_type_id':tag_type['tag_type_id']})
 
         if len(conflicts) :
-            raise PLCInvalidArgument, "Person %d already has setting %d"%(person['person_id'],
-                                                                               tag_type['tag_type_id'])
-
-        # check permission : it not admin, is the user affiliated with the same site as this person
-        if 'admin' not in self.caller['roles']:
-            # check caller is affiliated with at least one of Person's sites
-            if len(set(person['site_ids']) & set(self.caller['site_ids'])) == 0:
-                raise PLCPermissionDenied, "Not a member of the person's sites: %s"%person['site_ids']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified person setting, requires role %d",required_min_role
+            raise PLCInvalidArgument, "Person %d (%s) already has setting %d"% \
+                (person['person_id'],person['email'], tag_type['tag_type_id'])
+
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        # user can change tags on self
+        elif AuthorizeHelpers.person_access_person (self.api, self.caller, person):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you can only change your own tags"%self.name
+
 
         person_tag = PersonTag(self.api)
         person_tag['person_id'] = person['person_id']
index 458b40b..1e47033 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
diff --git a/PLC/Methods/AddRoleToTagType.py b/PLC/Methods/AddRoleToTagType.py
new file mode 100644 (file)
index 0000000..947adcd
--- /dev/null
@@ -0,0 +1,58 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Auth import Auth
+from PLC.Parameter import Parameter, Mixed
+from PLC.TagTypes import TagType, TagTypes
+from PLC.Roles import Role, Roles
+
+class AddRoleToTagType(Method):
+    """
+    Add the specified role to the tagtype so that 
+    users with that role can tweak the tag.
+
+    Only admins can call this method
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin']
+
+    accepts = [
+        Auth(),
+        Mixed(Role.fields['role_id'],
+              Role.fields['name']),
+        Mixed(TagType.fields['tag_type_id'],
+              TagType.fields['tagname']),
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    def call(self, auth, role_id_or_name, tag_type_id_or_tagname):
+        # Get role
+        roles = Roles(self.api, [role_id_or_name])
+        if not roles:
+            raise PLCInvalidArgument, "Invalid role '%s'" % unicode(role_id_or_name)
+        role = roles[0]
+
+        # Get subject tag type
+        tag_types = TagTypes(self.api, [tag_type_id_or_tagname])
+        if not tag_types:
+            raise PLCInvalidArgument, "No such tag type"
+        tag_type = tag_types[0]
+
+        # Authenticated function
+        assert self.caller is not None
+
+        # Only admins 
+        if 'admin' not in self.caller['roles']: 
+            raise PLCInvalidArgument, "Not allowed to grant that role"
+
+        if role['role_id'] not in tag_type['role_ids']:
+            tag_type.add_role(role)
+
+        self.event_objects = {'TagType': [tag_type['tag_type_id']],
+                              'Role': [role['role_id']]}
+        self.message = "Role %d added to tag_type %d" % \
+            (role['role_id'], tag_type['tag_type_id'])
+
+        return 1
index 9bc5ef7..cde238f 100644 (file)
@@ -1,29 +1,26 @@
-# $Id: AddSiteTag.py 14587 2009-07-19 13:18:50Z thierry $
-# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/AddSiteTag.py $
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 14587 $
-#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
+from PLC.Sites import Site, Sites
+from PLC.Nodes import Nodes
 from PLC.TagTypes import TagType, TagTypes
 from PLC.SiteTags import SiteTag, SiteTags
-from PLC.Sites import Site, Sites
 
-from PLC.Nodes import Nodes
-from PLC.Sites import Sites
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class AddSiteTag(Method):
     """
     Sets the specified setting for the specified site
     to the specified value.
 
-    In general only tech(s), PI(s) and of course admin(s) are allowed to
-    do the change, but this is defined in the tag type object.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns the new site_tag_id (> 0) if successful, faults
     otherwise.
@@ -42,9 +39,6 @@ class AddSiteTag(Method):
 
     returns = Parameter(int, 'New site_tag_id (> 0) if successful')
 
-    object_type = 'Site'
-
-
     def call(self, auth, site_id, tag_type_id_or_name, value):
         sites = Sites(self.api, [site_id])
         if not sites:
@@ -58,26 +52,23 @@ class AddSiteTag(Method):
 
         # checks for existence - does not allow several different settings
         conflicts = SiteTags(self.api,
-                                        {'site_id':site['site_id'],
-                                         'tag_type_id':tag_type['tag_type_id']})
+                             {'site_id':site['site_id'],
+                              'tag_type_id':tag_type['tag_type_id']})
 
         if len(conflicts) :
             raise PLCInvalidArgument, "Site %d already has setting %d"%(site['site_id'],
-                                                                               tag_type['tag_type_id'])
-
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # locate site
-            site = Sites (self.api, site_id)[0]
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified site setting, requires role %d",required_min_role
-
+                                                                        tag_type['tag_type_id'])
+
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.person_belongs_to_site (self.api, self.caller, site):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must be part of the subject site"%self.name
+            
         site_tag = SiteTag(self.api)
         site_tag['site_id'] = site['site_id']
         site_tag['tag_type_id'] = tag_type['tag_type_id']
index e55aa7c..a01bb12 100644 (file)
@@ -1,15 +1,19 @@
-# $Id$
-# $URL$
+#
+# Thierry Parmentelat - INRIA
+#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
 from PLC.TagTypes import TagType, TagTypes
 from PLC.Slices import Slice, Slices
 from PLC.Nodes import Node, Nodes
 from PLC.SliceTags import SliceTag, SliceTags
 from PLC.NodeGroups import NodeGroup, NodeGroups
 from PLC.InitScripts import InitScript, InitScripts
-from PLC.Auth import Auth
+
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class AddSliceTag(Method):
     """
@@ -122,8 +126,8 @@ class AddSliceTag(Method):
 
         # Check if slice attribute alreay exists
         slice_tags_check = SliceTags(self.api, {'slice_id': slice['slice_id'],
-                                                            'tagname': tag_type['tagname'],
-                                                            'value': value})
+                                                'tagname': tag_type['tagname'],
+                                                'value': value})
         for slice_tag_check in slice_tags_check:
             # do not compare between slice tag and sliver tag
             if 'node_id' not in slice_tag and slice_tag_check['node_id'] is not None:
index 336edb3..4319c12 100644 (file)
@@ -14,7 +14,7 @@ from PLC.TagTypes import TagType, TagTypes
 from PLC.Auth import Auth
 
 can_update = lambda (field, value): field in \
-             ['tagname', 'description', 'category', 'min_role_id']
+             ['tagname', 'description', 'category']
 
 class AddTagType(Method):
     """
index 40780ad..6452b0d 100644 (file)
@@ -1,10 +1,6 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 9423 $
-#
 
 from PLC.Faults import *
 from PLC.Method import Method
@@ -17,6 +13,8 @@ from PLC.Nodes import Node, Nodes
 from PLC.Sites import Site, Sites
 from PLC.TagTypes import TagType, TagTypes
 
+from PLC.AuthorizeHelpers import AuthorizeHelpers
+
 class DeleteIlink(Method):
     """
     Deletes the specified ilink
@@ -46,29 +44,24 @@ class DeleteIlink(Method):
             raise PLCInvalidArgument, "No such ilink %r"%ilink_id
         ilink = ilinks[0]
 
+        src_if=Interfaces(self.api,ilink['src_interface_id'])[0]
+        dst_if=Interfaces(self.api,ilink['dst_interface_id'])[0]
+        
         tag_type_id = ilink['tag_type_id']
         tag_type = TagTypes (self.api,[tag_type_id])[0]
-        required_min_role = tag_type ['min_role_id']
-
-        # check permission : it not admin, is the user affiliated with the right site<S>
-        if 'admin' not in self.caller['roles']:
-            for key in ['src_interface_id','dst_interface_id']:
-                # locate interface
-                interface_id=ilink[key]
-                interface = Interfaces (self.api,interface_id)[0]
-                node_id=interface['node_id']
-                node = Nodes (self.api,node_id) [0]
-                # locate site
-                site_id = node['site_id']
-                site = Sites (self.api, [site_id]) [0]
-                # check caller is affiliated with this site
-                if self.caller['person_id'] not in site['person_ids']:
-                    raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-                if required_min_role is not None and \
-                        min(self.caller['role_ids']) > required_min_role:
-                    raise PLCPermissionDenied, "Not allowed to modify the specified ilink, requires role %d",required_min_role
 
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.interface_belongs_to_person (self.api, src_if, self.caller):
+            pass
+        elif src_if_id != dst_if_id and AuthorizeHelpers.interface_belongs_to_person (self.api, dst_if, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must own either the src or dst interface"%self.name
+            
         ilink.delete()
         self.object_ids = [ilink['src_interface_id'],ilink['dst_interface_id']]
 
index 4475f5d..229a0d7 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
index 67b2bbb..187d25e 100644 (file)
@@ -1,34 +1,29 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision$
-#
-
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
-from PLC.InterfaceTags import InterfaceTag, InterfaceTags
+from PLC.Sites import Sites
+from PLC.Nodes import Nodes
 from PLC.Interfaces import Interface, Interfaces
-
-from PLC.Nodes import Node, Nodes
-from PLC.Sites import Site, Sites
+from PLC.TagTypes import TagType, TagTypes
+from PLC.InterfaceTags import InterfaceTag, InterfaceTags
 
 class DeleteInterfaceTag(Method):
     """
     Deletes the specified interface setting
 
-    Attributes may require the caller to have a particular role in order
-    to be deleted, depending on the related tag type.
-    Admins may delete attributes of any slice or sliver.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns 1 if successful, faults otherwise.
     """
 
-    roles = ['admin', 'pi', 'user']
+    roles = ['admin', 'pi', 'user', 'tech']
 
     accepts = [
         Auth(),
@@ -37,37 +32,25 @@ class DeleteInterfaceTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Interface'
-
-
     def call(self, auth, interface_tag_id):
         interface_tags = InterfaceTags(self.api, [interface_tag_id])
         if not interface_tags:
             raise PLCInvalidArgument, "No such interface tag %r"%interface_tag_id
         interface_tag = interface_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        interfaces = Interfaces(self.api, [interface_tag['interface_id']])
-        if not interfaces:
-            raise PLCInvalidArgument, "No such interface %r"%interface_tag['interface_id']
-        interface = interfaces[0]
-
-        assert interface_tag['interface_tag_id'] in interface['interface_tag_ids']
-
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # locate node
-            node = Nodes (self.api,[interface['node_id']])[0]
-            # locate site
-            site = Sites (self.api, [node['site_id']])[0]
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified interface setting, requires role %d",required_min_role
+        tag_type_id = interface_tag['tag_type_id']
+        tag_type = TagTypes (self.api,[tag_type_id])[0]
+        interface = Interfaces (self.api, interface_tag['interface_id'])
+
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.interface_belongs_to_person (self.api, interface, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must belong in the same site as subject interface"%self.name
 
         interface_tag.delete()
         self.object_ids = [interface_tag['interface_tag_id']]
index 9b3fdb8..bd99f0e 100644 (file)
@@ -1,34 +1,31 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 9423 $
-#
 
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
-from PLC.NodeTags import NodeTag, NodeTags
+from PLC.Sites import Site, Sites
 from PLC.Nodes import Node, Nodes
+from PLC.TagTypes import TagType, TagTypes
+from PLC.NodeTags import NodeTag, NodeTags
 
-from PLC.Nodes import Node, Nodes
-from PLC.Sites import Site, Sites
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class DeleteNodeTag(Method):
     """
     Deletes the specified node tag
 
-    Attributes may require the caller to have a particular role in order
-    to be deleted, depending on the related node tag type.
-    Admins may delete attributes of any slice or sliver.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns 1 if successful, faults otherwise.
     """
 
-    roles = ['admin', 'pi', 'user']
+    roles = ['admin', 'pi', 'user', 'tech']
 
     accepts = [
         Auth(),
@@ -37,37 +34,25 @@ class DeleteNodeTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Node'
-
-
     def call(self, auth, node_tag_id):
         node_tags = NodeTags(self.api, [node_tag_id])
         if not node_tags:
             raise PLCInvalidArgument, "No such node tag %r"%node_tag_id
         node_tag = node_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        nodes = Nodes(self.api, [node_tag['node_id']])
-        if not nodes:
-            raise PLCInvalidArgument, "No such node %r"%node_tag['node_id']
-        node = nodes[0]
-
-        assert node_tag['node_tag_id'] in node['node_tag_ids']
-
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # locate node
-            node = Nodes (self.api,[node['node_id']])[0]
-            # locate site
-            site = Sites (self.api, [node['site_id']])[0]
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified node tag, requires role %d",required_min_role
+        tag_type_id = node_tag['tag_type_id']
+        tag_type = TagTypes (self.api,[tag_type_id])[0]
+        node = Nodes (self.api, node_tag['node_id'])
+
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.node_belongs_to_person (self.api, node, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must belong in the same site as subject node"%self.name
 
         node_tag.delete()
         self.object_ids = [node_tag['node_tag_id']]
index e4abcdd..19ab5fe 100644 (file)
@@ -1,11 +1,6 @@
-# $Id: DeletePersonTag.py 14587 2009-07-19 13:18:50Z thierry $
-# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/DeletePersonTag.py $
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 14587 $
-#
-
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
@@ -14,16 +9,13 @@ from PLC.Auth import Auth
 from PLC.PersonTags import PersonTag, PersonTags
 from PLC.Persons import Person, Persons
 
-from PLC.Nodes import Node, Nodes
-from PLC.Persons import Person, Persons
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class DeletePersonTag(Method):
     """
     Deletes the specified person setting
 
-    Attributes may require the caller to have a particular role in order
-    to be deleted, depending on the related tag type.
-    Admins may delete attributes of any slice or sliver.
+    Admins have full access.  Non-admins can change their own tags.
 
     Returns 1 if successful, faults otherwise.
     """
@@ -37,33 +29,22 @@ class DeletePersonTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Person'
-
-
     def call(self, auth, person_tag_id):
         person_tags = PersonTags(self.api, [person_tag_id])
         if not person_tags:
             raise PLCInvalidArgument, "No such person tag %r"%person_tag_id
         person_tag = person_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        persons = Persons(self.api, [person_tag['person_id']])
-        if not persons:
-            raise PLCInvalidArgument, "No such person %r"%person_tag['person_id']
-        person = persons[0]
-
-        assert person_tag['person_tag_id'] in person['person_tag_ids']
-
-        # check permission : it not admin, is the user affiliated with the right person
-        if 'admin' not in self.caller['roles']:
-            # check caller is affiliated with this person's site
-            if len(set(person['site_ids']) & set(self.caller['site_ids'])) == 0:
-                raise PLCPermissionDenied, "Not a member of the person's sites: %s"%person['site_ids']
+        person = Persons (self.api, person_tag['person_id'])[0]
 
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified person setting, requires role %d",required_min_role
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        # user can change tags on self
+        elif AuthorizeHelpers.person_access_person (self.api, self.caller, person):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you can only change your own tags"%self.name
 
         person_tag.delete()
         self.object_ids = [person_tag['person_tag_id']]
index 28dedfb..4deacf6 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
diff --git a/PLC/Methods/DeleteRoleFromTagType.py b/PLC/Methods/DeleteRoleFromTagType.py
new file mode 100644 (file)
index 0000000..cdce6fa
--- /dev/null
@@ -0,0 +1,59 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.TagTypes import TagType, TagTypes
+from PLC.Auth import Auth
+from PLC.Roles import Role, Roles
+
+class DeleteRoleFromTagType(Method):
+    """
+    Delete the specified role from the tagtype so that 
+    users with that role can no longer tweak the tag.
+
+    Only admins can call this method
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin']
+
+    accepts = [
+        Auth(),
+        Mixed(Role.fields['role_id'],
+              Role.fields['name']),
+        Mixed(TagType.fields['tag_type_id'],
+              TagType.fields['tagname']),
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    def call(self, auth, role_id_or_name, tag_type_id_or_tagname):
+        # Get role
+        roles = Roles(self.api, [role_id_or_name])
+        if not roles:
+            raise PLCInvalidArgument, "Invalid role '%s'" % unicode(role_id_or_name)
+        role = roles[0]
+
+        # Get subject tag type
+        tag_types = TagTypes(self.api, [tag_type_id_or_tagname])
+        if not tag_types:
+            raise PLCInvalidArgument, "No such tag type"
+        tag_type = tag_types[0]
+
+        # Authenticated function
+        assert self.caller is not None
+
+        # Only admins 
+        if 'admin' not in self.caller['roles']: 
+            raise PLCInvalidArgument, "Not allowed to revoke that role"
+
+        if role['role_id'] in tag_type['role_ids']:
+            tag_type.remove_role(role)
+
+        # Logging variables
+        self.event_objects = {'TagType': [tag_type['tag_type_id']],
+                              'Role': [role['role_id']]}
+        self.message = "Role %d revoked from tag_type %d" % \
+                       (role['role_id'], tag_type['tag_type_id'])
+
+        return 1
index 9776275..db2b294 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
index ab22400..f0e87c3 100644 (file)
@@ -1,29 +1,25 @@
-# $Id: DeleteSiteTag.py 14587 2009-07-19 13:18:50Z thierry $
-# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/DeleteSiteTag.py $
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 14587 $
-#
-
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
-from PLC.SiteTags import SiteTag, SiteTags
 from PLC.Sites import Site, Sites
+from PLC.Nodes import Nodes
+from PLC.TagTypes import TagType, TagTypes
+from PLC.SiteTags import SiteTag, SiteTags
 
-from PLC.Nodes import Node, Nodes
-from PLC.Sites import Site, Sites
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class DeleteSiteTag(Method):
     """
     Deletes the specified site setting
 
-    Attributes may require the caller to have a particular role in order
-    to be deleted, depending on the related tag type.
-    Admins may delete attributes of any slice or sliver.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns 1 if successful, faults otherwise.
     """
@@ -37,34 +33,26 @@ class DeleteSiteTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Site'
-
-
     def call(self, auth, site_tag_id):
         site_tags = SiteTags(self.api, [site_tag_id])
         if not site_tags:
             raise PLCInvalidArgument, "No such site tag %r"%site_tag_id
         site_tag = site_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        sites = Sites(self.api, [site_tag['site_id']])
-        if not sites:
-            raise PLCInvalidArgument, "No such site %r"%site_tag['site_id']
-        site = sites[0]
-
-        assert site_tag['site_tag_id'] in site['site_tag_ids']
-
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified site setting, requires role %d",required_min_role
-
+        tag_type_id = site_tag['tag_type_id']
+        tag_type = TagTypes (self.api,[tag_type_id])[0]
+        site = Sites (self.api, site_tag['site_id'])
+        
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.person_belongs_to_site (self.api, self.caller, site):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must be part of the subject site"%self.name
+            
         site_tag.delete()
         self.object_ids = [site_tag['site_tag_id']]
 
index 9e2ad06..e63393e 100644 (file)
@@ -1,12 +1,16 @@
-# $Id$
-# $URL$
+#
+# Thierry Parmentelat - INRIA
+#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
 from PLC.SliceTags import SliceTag, SliceTags
 from PLC.Slices import Slice, Slices
 from PLC.Nodes import Node, Nodes
-from PLC.Auth import Auth
+
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class DeleteSliceTag(Method):
     """
@@ -21,7 +25,7 @@ class DeleteSliceTag(Method):
     Returns 1 if successful, faults otherwise.
     """
 
-    roles = ['admin', 'pi', 'user']
+    roles = ['admin', 'pi', 'user', 'tech']
 
     accepts = [
         Auth(),
index dae4633..05daf17 100644 (file)
@@ -1,10 +1,6 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 9423 $
-#
 
 from PLC.Faults import *
 from PLC.Method import Method
@@ -13,9 +9,11 @@ from PLC.Auth import Auth
 
 from PLC.Ilinks import Ilink, Ilinks
 from PLC.Interfaces import Interface, Interfaces
-
+from PLC.TagTypes import TagType, TagTypes
 from PLC.Sites import Sites
 
+from PLC.AuthorizeHelpers import AuthorizeHelpers
+
 class UpdateIlink(Method):
     """
     Updates the value of an existing ilink
@@ -43,9 +41,23 @@ class UpdateIlink(Method):
             raise PLCInvalidArgument, "No such ilink %r"%ilink_id
         ilink = ilinks[0]
 
-        # xxx see AddIlink for this - should be written once in the Ilink class I guess
-        # checks rights and stuff
+        src_if=Interfaces(self.api,ilink['src_interface_id'])[0]
+        dst_if=Interfaces(self.api,ilink['dst_interface_id'])[0]
+        tag_type_id = ilink['tag_type_id']
+        tag_type = TagTypes (self.api,[tag_type_id])[0]
 
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.interface_belongs_to_person (self.api, src_if, self.caller):
+            pass
+        elif src_if_id != dst_if_id and AuthorizeHelpers.interface_belongs_to_person (self.api, dst_if, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must own either the src or dst interface"%self.name
+            
         ilink['value'] = value
         ilink.sync()
 
index 4a3f02f..e29041f 100644 (file)
@@ -1,27 +1,24 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision$
-#
-
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
-from PLC.InterfaceTags import InterfaceTag, InterfaceTags
-from PLC.Interfaces import Interface, Interfaces
-
-from PLC.Nodes import Nodes
 from PLC.Sites import Sites
+from PLC.Nodes import Nodes
+from PLC.Interfaces import Interface, Interfaces
+from PLC.TagTypes import TagType, TagTypes
+from PLC.InterfaceTags import InterfaceTag, InterfaceTags
 
 class UpdateInterfaceTag(Method):
     """
     Updates the value of an existing interface setting
 
-    Access rights depend on the tag type.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns 1 if successful, faults otherwise.
     """
@@ -36,36 +33,26 @@ class UpdateInterfaceTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Interface'
-
     def call(self, auth, interface_tag_id, value):
         interface_tags = InterfaceTags(self.api, [interface_tag_id])
         if not interface_tags:
             raise PLCInvalidArgument, "No such interface setting %r"%interface_tag_id
         interface_tag = interface_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        interfaces = Interfaces(self.api, [interface_tag['interface_id']])
-        if not interfaces:
-            raise PLCInvalidArgument, "No such interface %r"%interface_tag['interface_id']
-        interface = interfaces[0]
-
-        assert interface_tag['interface_tag_id'] in interface['interface_tag_ids']
+        tag_type_id = interface_tag['tag_type_id']
+        tag_type = TagTypes (self.api,[tag_type_id])[0]
+        interface = Interfaces (self.api, interface_tag['interface_id'])
 
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # locate node
-            node = Nodes (self.api,[interface['node_id']])[0]
-            # locate site
-            site = Sites (self.api, [node['site_id']])[0]
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.interface_belongs_to_person (self.api, interface, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must belong in the same site as subject interface"%self.name
 
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified interface setting, requires role %d",required_min_role
 
         interface_tag['value'] = value
         interface_tag.sync()
index a783f16..3cc7891 100644 (file)
@@ -1,10 +1,6 @@
-# $Id$
-# $URL$
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 9423 $
-#
 
 from PLC.Faults import *
 from PLC.Method import Method
@@ -13,13 +9,18 @@ from PLC.Auth import Auth
 
 from PLC.Sites import Sites
 from PLC.Nodes import Node, Nodes
+from PLC.TagTypes import TagType, TagTypes
 from PLC.NodeTags import NodeTag, NodeTags
 
+from PLC.AuthorizeHelpers import AuthorizeHelpers
+
 class UpdateNodeTag(Method):
     """
     Updates the value of an existing node tag
 
-    Access rights depend on the node tag type.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns 1 if successful, faults otherwise.
     """
@@ -34,36 +35,26 @@ class UpdateNodeTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Node'
-
     def call(self, auth, node_tag_id, value):
         node_tags = NodeTags(self.api, [node_tag_id])
         if not node_tags:
             raise PLCInvalidArgument, "No such node tag %r"%node_tag_id
         node_tag = node_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        nodes = Nodes(self.api, [node_tag['node_id']])
-        if not nodes:
-            raise PLCInvalidArgument, "No such node %r"%node_tag['node_id']
-        node = nodes[0]
-
-        assert node_tag['node_tag_id'] in node['node_tag_ids']
+        tag_type_id = node_tag['tag_type_id']
+        tag_type = TagTypes (self.api,[tag_type_id])[0]
+        node = Nodes (self.api, node_tag['node_id'])
 
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # locate node
-            node = Nodes (self.api,[node['node_id']])[0]
-            # locate site
-            site = Sites (self.api, [node['site_id']])[0]
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.node_belongs_to_person (self.api, node, self.caller):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must belong in the same site as subject node"%self.name
 
-            required_min_role = node_tag['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified node tag, requires role %d",required_min_role
 
         node_tag['value'] = value
         node_tag.sync()
index 4162684..97862ef 100644 (file)
@@ -1,9 +1,6 @@
-# $Id: UpdatePersonTag.py 14587 2009-07-19 13:18:50Z thierry $
-# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/UpdatePersonTag.py $
 #
-# $Revision: 14587 $
+# Thierry Parmentelat - INRIA
 #
-
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
@@ -12,14 +9,13 @@ from PLC.Auth import Auth
 from PLC.PersonTags import PersonTag, PersonTags
 from PLC.Persons import Person, Persons
 
-from PLC.Nodes import Nodes
-from PLC.Persons import Persons
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class UpdatePersonTag(Method):
     """
     Updates the value of an existing person setting
 
-    Access rights depend on the tag type.
+    Admins have full access.  Non-admins can change their own tags.
 
     Returns 1 if successful, faults otherwise.
     """
@@ -34,34 +30,22 @@ class UpdatePersonTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Person'
-
     def call(self, auth, person_tag_id, value):
         person_tags = PersonTags(self.api, [person_tag_id])
         if not person_tags:
             raise PLCInvalidArgument, "No such person setting %r"%person_tag_id
         person_tag = person_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        persons = Persons(self.api, [person_tag['person_id']])
-        if not persons:
-            raise PLCInvalidArgument, "No such person %r"%person_tag['person_id']
-        person = persons[0]
-
-        assert person_tag['person_tag_id'] in person['person_tag_ids']
-
-        # check permission : it not admin, is the user affiliated with the right person
-        if 'admin' not in self.caller['roles']:
-            # check caller is affiliated with this person's person
-            if not self.caller.can_update(person):
-                raise PLCPermissionDenied, "person_id %s doesn't have access to person_tag_id %s" % (
-                    person['person_id'],
-                    person_tag['person_tag_id'])
+        person = Persons (self.api, person_tag['person_id'])[0]
 
-            required_min_role = person_tag['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified person setting, requires role %d" % required_min_role
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        # user can change tags on self
+        elif AuthorizeHelpers.person_access_person (self.api, self.caller, person):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you can only change your own tags"%self.name
 
         person_tag['value'] = value
         person_tag.sync()
index 06919bf..901a875 100644 (file)
@@ -1,25 +1,25 @@
-# $Id: UpdateSiteTag.py 14587 2009-07-19 13:18:50Z thierry $
-# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/UpdateSiteTag.py $
 #
-# $Revision: 14587 $
+# Thierry Parmentelat - INRIA
 #
-
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Auth import Auth
 
-from PLC.SiteTags import SiteTag, SiteTags
 from PLC.Sites import Site, Sites
-
 from PLC.Nodes import Nodes
-from PLC.Sites import Sites
+from PLC.TagTypes import TagType, TagTypes
+from PLC.SiteTags import SiteTag, SiteTags
+
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class UpdateSiteTag(Method):
     """
     Updates the value of an existing site setting
 
-    Access rights depend on the tag type.
+    Admins have full access.  Non-admins need to 
+    (1) have at least one of the roles attached to the tagtype, 
+    and (2) belong in the same site as the tagged subject.
 
     Returns 1 if successful, faults otherwise.
     """
@@ -34,33 +34,26 @@ class UpdateSiteTag(Method):
 
     returns = Parameter(int, '1 if successful')
 
-    object_type = 'Site'
-
     def call(self, auth, site_tag_id, value):
         site_tags = SiteTags(self.api, [site_tag_id])
         if not site_tags:
             raise PLCInvalidArgument, "No such site setting %r"%site_tag_id
         site_tag = site_tags[0]
 
-        ### reproducing a check from UpdateSliceTag, looks dumb though
-        sites = Sites(self.api, [site_tag['site_id']])
-        if not sites:
-            raise PLCInvalidArgument, "No such site %r"%site_tag['site_id']
-        site = sites[0]
-
-        assert site_tag['site_tag_id'] in site['site_tag_ids']
-
-        # check permission : it not admin, is the user affiliated with the right site
-        if 'admin' not in self.caller['roles']:
-            # check caller is affiliated with this site
-            if self.caller['person_id'] not in site['person_ids']:
-                raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
-
-            required_min_role = tag_type ['min_role_id']
-            if required_min_role is not None and \
-                    min(self.caller['role_ids']) > required_min_role:
-                raise PLCPermissionDenied, "Not allowed to modify the specified site setting, requires role %d",required_min_role
-
+        tag_type_id = site_tag['tag_type_id']
+        tag_type = TagTypes (self.api,[tag_type_id])[0]
+        site = Sites (self.api, site_tag['site_id'])
+        
+        # check authorizations
+        if 'admin' in self.caller['roles']:
+            pass
+        elif not AuthorizeHelpers.person_access_tag_type (self.api, self.caller, tag_type):
+            raise PLCPermissionDenied, "%s, no permission to use this tag type"%self.name
+        elif AuthorizeHelpers.person_belongs_to_site (self.api, self.caller, site):
+            pass
+        else:
+            raise PLCPermissionDenied, "%s: you must be part of the subject site"%self.name
+            
         site_tag['value'] = value
         site_tag.sync()
 
index 76a6354..e8b8b33 100644 (file)
@@ -1,13 +1,17 @@
-# $Id$
-# $URL$
+#
+# Thierry Parmentelat - INRIA
+#
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
 from PLC.SliceTags import SliceTag, SliceTags
 from PLC.Nodes import Node
 from PLC.Slices import Slice, Slices
 from PLC.InitScripts import InitScript, InitScripts
-from PLC.Auth import Auth
+
+from PLC.AuthorizeHelpers import AuthorizeHelpers
 
 class UpdateSliceTag(Method):
     """
index bad4ba2..56bd61f 100644 (file)
@@ -8,7 +8,7 @@ from PLC.TagTypes import TagType, TagTypes
 from PLC.Auth import Auth
 
 can_update = lambda (field, value): field in \
-             ['tagname', 'description', 'category', 'min_role_id']
+             ['tagname', 'description', 'category']
 
 class UpdateTagType(Method):
     """
index c289dee..7f69e32 100644 (file)
@@ -25,7 +25,6 @@ class NodeTag(Row):
         'tagname': TagType.fields['tagname'],
         'description': TagType.fields['description'],
         'category': TagType.fields['category'],
-        'min_role_id': TagType.fields['min_role_id'],
         }
 
 class NodeTags(Table):
index be1e86e..30869af 100644 (file)
@@ -26,7 +26,6 @@ class PersonTag(Row):
         'tagname': TagType.fields['tagname'],
         'description': TagType.fields['description'],
         'category': TagType.fields['category'],
-        'min_role_id': TagType.fields['min_role_id'],
         'value': Parameter(str, "Person setting value"),
         ### relations
 
index 03d9884..4aa4d6b 100644 (file)
@@ -22,7 +22,7 @@ class Role(Row):
 
     table_name = 'roles'
     primary_key = 'role_id'
-    join_tables = ['person_role', ('tag_types', 'min_role_id')]
+    join_tables = ['person_role', 'tag_type_role' ]
     fields = {
         'role_id': Parameter(int, "Role identifier"),
         'name': Parameter(str, "Role", max = 100),
index c70a46c..45cbfd9 100644 (file)
@@ -26,7 +26,6 @@ class SiteTag(Row):
         'tagname': TagType.fields['tagname'],
         'description': TagType.fields['description'],
         'category': TagType.fields['category'],
-        'min_role_id': TagType.fields['min_role_id'],
         'value': Parameter(str, "Site setting value"),
         ### relations
 
index fca3b14..cfb26ad 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-# $URL$
 from types import StringTypes
 import string
 
index a320e62..78273ca 100644 (file)
@@ -28,7 +28,6 @@ class SliceTag(Row):
         'tagname': TagType.fields['tagname'],
         'description': TagType.fields['description'],
         'category': TagType.fields['category'],
-        'min_role_id': TagType.fields['min_role_id'],
         'value': Parameter(str, "Slice attribute value"),
         }
 
index d4d4cd7..c045a63 100644 (file)
@@ -22,13 +22,14 @@ class TagType (Row):
 
     table_name = 'tag_types'
     primary_key = 'tag_type_id'
-    join_tables = ['node_tag', 'interface_tag', 'slice_tag', 'site_tag', 'person_tag' ]
+    join_tables = ['tag_type_role', 'node_tag', 'interface_tag', 'slice_tag', 'site_tag', 'person_tag' ]
     fields = {
         'tag_type_id': Parameter(int, "Node tag type identifier"),
         'tagname': Parameter(str, "Node tag type name", max = 100),
         'description': Parameter(str, "Node tag type description", max = 254),
         'category' : Parameter (str, "Node tag category", max=64, optional=True),
-        'min_role_id': Parameter(int, "Minimum (least powerful) role that can set or change this attribute"),
+        'role_ids': Parameter([int], "List of role identifiers"),
+        'roles': Parameter([str], "List of roles"),
         }
 
     def validate_name(self, name):
@@ -43,12 +44,9 @@ class TagType (Row):
 
         return name
 
-    def validate_min_role_id(self, role_id):
-        roles = [row['role_id'] for row in Roles(self.api)]
-        if role_id not in roles:
-            raise PLCInvalidArgument, "Invalid role"
+    add_role = Row.add_object(Role, 'tag_type_role')
+    remove_role = Row.remove_object(Role, 'tag_type_role')
 
-        return role_id
 
 class TagTypes(Table):
     """
@@ -59,7 +57,7 @@ class TagTypes(Table):
     def __init__(self, api, tag_type_filter = None, columns = None):
         Table.__init__(self, api, TagType, columns)
 
-        sql = "SELECT %s FROM tag_types WHERE True" % \
+        sql = "SELECT %s FROM view_tag_types WHERE True" % \
               ", ".join(self.columns)
 
         if tag_type_filter is not None:
diff --git a/migrations/104-down-noderole.sql b/migrations/104-down-noderole.sql
new file mode 100644 (file)
index 0000000..f7d7220
--- /dev/null
@@ -0,0 +1,20 @@
+-- reverting....
+-- DELETE from roles WHERE name='node';
+
+-- recreate the min_role_id column
+ALTER TABLE tag_types ADD COLUMN min_role_id integer REFERENCES roles;
+
+-- compute the highest role available for each tag_type and store it as min_role_id
+-- xxx todo
+
+--- tmp - set to something so we can run down&up again
+UPDATE tag_types SET min_role_id=10;
+UPDATE tag_types SET min_role_id=20 WHERE tag_type_id%2=0;
+
+DROP TABLE tag_type_role CASCADE;
+-- done by cascade
+--DROP VIEW view_tag_types;
+--DROP VIEW tag_type_roles;
+
+--------------------
+UPDATE plc_db_version SET subversion = 103;
diff --git a/migrations/104-up-noderole.sql b/migrations/104-up-noderole.sql
new file mode 100644 (file)
index 0000000..c31af7a
--- /dev/null
@@ -0,0 +1,122 @@
+-- changing the permission model on tags
+-- we replace the single 'min_role_id' field attached to tag_types
+-- with a set of roles
+
+
+-- create a separate table to keep the tag-type x role relationship
+CREATE TABLE tag_type_role (
+    tag_type_id integer REFERENCES tag_types NOT NULL, -- tag_type ID
+    role_id integer REFERENCES roles NOT NULL,         -- role ID
+    PRIMARY KEY (tag_type_id, role_id)
+);
+CREATE INDEX tag_type_role_tag_type_id_idx ON tag_type_role (tag_type_id);
+CREATE INDEX tag_type_role_role_id_idx ON tag_type_role (role_id);
+
+-- fill this from the former min_role_id field in the tag_types table
+-- add all roles lower or equal to the min_role_id
+INSERT INTO tag_type_role ("tag_type_id","role_id") SELECT tag_type_id,role_id FROM tag_types,roles where role_id<=min_role_id;
+
+-- we can now drop the min_role_id column
+ALTER TABLE tag_types DROP COLUMN min_role_id CASCADE;
+
+-- create views to expose roles
+CREATE OR REPLACE VIEW tag_type_roles AS
+SELECT tag_type_id,
+array_accum(role_id) AS role_ids,
+array_accum(roles.name) AS roles
+FROM tag_type_role 
+LEFT JOIN roles USING (role_id)
+GROUP BY tag_type_id;
+
+CREATE OR REPLACE VIEW view_tag_types AS
+SELECT 
+tag_types.tag_type_id,
+tag_types.tagname,
+tag_types.description,
+tag_types.category,
+COALESCE((SELECT role_ids FROM tag_type_roles WHERE tag_type_roles.tag_type_id = tag_types.tag_type_id), '{}') AS role_ids,
+COALESCE((SELECT roles FROM tag_type_roles WHERE tag_type_roles.tag_type_id = tag_types.tag_type_id), '{}') AS roles
+FROM tag_types; 
+
+
+-- remove min_role_id from the object views
+CREATE OR REPLACE VIEW view_person_tags AS
+SELECT
+person_tag.person_tag_id,
+person_tag.person_id,
+persons.email,
+tag_types.tag_type_id,
+tag_types.tagname,
+tag_types.description,
+tag_types.category,
+person_tag.value
+FROM person_tag 
+INNER JOIN tag_types USING (tag_type_id)
+INNER JOIN persons USING (person_id);
+
+CREATE OR REPLACE VIEW view_site_tags AS
+SELECT
+site_tag.site_tag_id,
+site_tag.site_id,
+sites.login_base,
+tag_types.tag_type_id,
+tag_types.tagname,
+tag_types.description,
+tag_types.category,
+site_tag.value
+FROM site_tag 
+INNER JOIN tag_types USING (tag_type_id)
+INNER JOIN sites USING (site_id);
+
+CREATE OR REPLACE VIEW view_interface_tags AS
+SELECT
+interface_tag.interface_tag_id,
+interface_tag.interface_id,
+interfaces.ip,
+tag_types.tag_type_id,
+tag_types.tagname,
+tag_types.description,
+tag_types.category,
+interface_tag.value
+FROM interface_tag
+INNER JOIN tag_types USING (tag_type_id)
+INNER JOIN interfaces USING (interface_id);
+
+CREATE OR REPLACE VIEW view_node_tags AS
+SELECT
+node_tag.node_tag_id,
+node_tag.node_id,
+nodes.hostname,
+tag_types.tag_type_id,
+tag_types.tagname,
+tag_types.description,
+tag_types.category,
+node_tag.value
+FROM node_tag 
+INNER JOIN tag_types USING (tag_type_id)
+INNER JOIN nodes USING (node_id);
+
+CREATE OR REPLACE VIEW view_slice_tags AS
+SELECT
+slice_tag.slice_tag_id,
+slice_tag.slice_id,
+slice_tag.node_id,
+slice_tag.nodegroup_id,
+tag_types.tag_type_id,
+tag_types.tagname,
+tag_types.description,
+tag_types.category,
+slice_tag.value,
+slices.name
+FROM slice_tag
+INNER JOIN tag_types USING (tag_type_id)
+INNER JOIN slices USING (slice_id);
+
+-- same for ilinks
+CREATE OR REPLACE VIEW view_ilinks AS
+SELECT * FROM tag_types 
+INNER JOIN ilink USING (tag_type_id);
+
+-- INSERT INTO roles (role_id, name) VALUES (50, 'node');
+
+UPDATE plc_db_version SET subversion = 104;
index 6ac84c5..4083039 100644 (file)
@@ -69,6 +69,8 @@ CREATE TABLE tag_types (
     tag_type_id serial PRIMARY KEY,                    -- ID
     tagname text UNIQUE NOT NULL,                      -- Tag Name
     description text,                                  -- Optional Description
+-- this is deprecated -- see migrations/104*
+-- starting with subversion 104, a tag type has a SET OF roles attached to it
     min_role_id integer REFERENCES roles DEFAULT 10,   -- set minimal role required
     category text NOT NULL DEFAULT 'general'           -- Free text for grouping tags together
 ) WITH OIDS;