From 0c28b6c095054293cc35c75a7a601486a4c249ff Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Thu, 18 Nov 2010 20:16:19 +0100 Subject: [PATCH] reviewing the tags permission system 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 --- PLC/Accessor.py | 2 - PLC/Faults.py | 2 - PLC/Ilinks.py | 2 - PLC/InterfaceTags.py | 1 - PLC/Interfaces.py | 9 +- PLC/Methods/AddIlink.py | 34 +++----- PLC/Methods/AddInterface.py | 5 +- PLC/Methods/AddInterfaceTag.py | 43 ++++------ PLC/Methods/AddNodeTag.py | 42 ++++----- PLC/Methods/AddPerson.py | 2 - PLC/Methods/AddPersonTag.py | 44 ++++------ PLC/Methods/AddRoleToPerson.py | 2 - PLC/Methods/AddRoleToTagType.py | 58 +++++++++++++ PLC/Methods/AddSiteTag.py | 49 +++++------ PLC/Methods/AddSliceTag.py | 14 +-- PLC/Methods/AddTagType.py | 2 +- PLC/Methods/DeleteIlink.py | 41 ++++----- PLC/Methods/DeleteInterface.py | 2 - PLC/Methods/DeleteInterfaceTag.py | 59 +++++-------- PLC/Methods/DeleteNodeTag.py | 57 +++++-------- PLC/Methods/DeletePersonTag.py | 41 +++------ PLC/Methods/DeleteRoleFromPerson.py | 2 - PLC/Methods/DeleteRoleFromTagType.py | 59 +++++++++++++ PLC/Methods/DeleteSite.py | 2 - PLC/Methods/DeleteSiteTag.py | 54 +++++------- PLC/Methods/DeleteSliceTag.py | 12 ++- PLC/Methods/UpdateIlink.py | 26 ++++-- PLC/Methods/UpdateInterfaceTag.py | 51 +++++------ PLC/Methods/UpdateNodeTag.py | 45 ++++------ PLC/Methods/UpdatePersonTag.py | 40 +++------ PLC/Methods/UpdateSiteTag.py | 51 +++++------ PLC/Methods/UpdateSliceTag.py | 10 ++- PLC/Methods/UpdateTagType.py | 2 +- PLC/NodeTags.py | 1 - PLC/PersonTags.py | 1 - PLC/Roles.py | 2 +- PLC/SiteTags.py | 1 - PLC/Sites.py | 2 - PLC/SliceTags.py | 1 - PLC/TagTypes.py | 14 ++- migrations/104-down-noderole.sql | 20 +++++ migrations/104-up-noderole.sql | 122 +++++++++++++++++++++++++++ planetlab5.sql | 2 + 43 files changed, 568 insertions(+), 463 deletions(-) create mode 100644 PLC/Methods/AddRoleToTagType.py create mode 100644 PLC/Methods/DeleteRoleFromTagType.py create mode 100644 migrations/104-down-noderole.sql create mode 100644 migrations/104-up-noderole.sql diff --git a/PLC/Accessor.py b/PLC/Accessor.py index ea69ccb..0a499b5 100644 --- a/PLC/Accessor.py +++ b/PLC/Accessor.py @@ -1,5 +1,3 @@ -# $Id$ -# $URL$ # # Thierry Parmentelat - INRIA # diff --git a/PLC/Faults.py b/PLC/Faults.py index 3309cb6..cebe5c5 100644 --- a/PLC/Faults.py +++ b/PLC/Faults.py @@ -5,8 +5,6 @@ # Mark Huang # # Copyright (C) 2004-2006 The Trustees of Princeton University -# $Id$ -# $URL$ # import xmlrpclib diff --git a/PLC/Ilinks.py b/PLC/Ilinks.py index 8cd97ae..d99f13c 100644 --- a/PLC/Ilinks.py +++ b/PLC/Ilinks.py @@ -1,5 +1,3 @@ -# $Id$ -# $URL$ # # Thierry Parmentelat - INRIA # diff --git a/PLC/InterfaceTags.py b/PLC/InterfaceTags.py index 976a54e..4dcb531 100644 --- a/PLC/InterfaceTags.py +++ b/PLC/InterfaceTags.py @@ -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 diff --git a/PLC/Interfaces.py b/PLC/Interfaces.py index 2ea645f..c8ccd2e 100644 --- a/PLC/Interfaces.py +++ b/PLC/Interfaces.py @@ -4,9 +4,6 @@ # Mark Huang # 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): """ diff --git a/PLC/Methods/AddIlink.py b/PLC/Methods/AddIlink.py index 6316805..c9d18b1 100644 --- a/PLC/Methods/AddIlink.py +++ b/PLC/Methods/AddIlink.py @@ -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 diff --git a/PLC/Methods/AddInterface.py b/PLC/Methods/AddInterface.py index e98d545..ec35fc7 100644 --- a/PLC/Methods/AddInterface.py +++ b/PLC/Methods/AddInterface.py @@ -1,5 +1,6 @@ -# $Id$ -# $URL$ +# +# Thierry Parmentelat - INRIA +# from PLC.Faults import * from PLC.Auth import Auth from PLC.Method import Method diff --git a/PLC/Methods/AddInterfaceTag.py b/PLC/Methods/AddInterfaceTag.py index 23aaeaf..912e147 100644 --- a/PLC/Methods/AddInterfaceTag.py +++ b/PLC/Methods/AddInterfaceTag.py @@ -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'] diff --git a/PLC/Methods/AddNodeTag.py b/PLC/Methods/AddNodeTag.py index f02181b..43e075f 100644 --- a/PLC/Methods/AddNodeTag.py +++ b/PLC/Methods/AddNodeTag.py @@ -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'] diff --git a/PLC/Methods/AddPerson.py b/PLC/Methods/AddPerson.py index 7c4d67d..8840975 100644 --- a/PLC/Methods/AddPerson.py +++ b/PLC/Methods/AddPerson.py @@ -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/AddPersonTag.py b/PLC/Methods/AddPersonTag.py index 7f22bee..6bba377 100644 --- a/PLC/Methods/AddPersonTag.py +++ b/PLC/Methods/AddPersonTag.py @@ -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'] diff --git a/PLC/Methods/AddRoleToPerson.py b/PLC/Methods/AddRoleToPerson.py index 458b40b..1e47033 100644 --- a/PLC/Methods/AddRoleToPerson.py +++ b/PLC/Methods/AddRoleToPerson.py @@ -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 index 0000000..947adcd --- /dev/null +++ b/PLC/Methods/AddRoleToTagType.py @@ -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 diff --git a/PLC/Methods/AddSiteTag.py b/PLC/Methods/AddSiteTag.py index 9bc5ef7..cde238f 100644 --- a/PLC/Methods/AddSiteTag.py +++ b/PLC/Methods/AddSiteTag.py @@ -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'] diff --git a/PLC/Methods/AddSliceTag.py b/PLC/Methods/AddSliceTag.py index e55aa7c..a01bb12 100644 --- a/PLC/Methods/AddSliceTag.py +++ b/PLC/Methods/AddSliceTag.py @@ -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: diff --git a/PLC/Methods/AddTagType.py b/PLC/Methods/AddTagType.py index 336edb3..4319c12 100644 --- a/PLC/Methods/AddTagType.py +++ b/PLC/Methods/AddTagType.py @@ -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): """ diff --git a/PLC/Methods/DeleteIlink.py b/PLC/Methods/DeleteIlink.py index 40780ad..6452b0d 100644 --- a/PLC/Methods/DeleteIlink.py +++ b/PLC/Methods/DeleteIlink.py @@ -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 - 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']] diff --git a/PLC/Methods/DeleteInterface.py b/PLC/Methods/DeleteInterface.py index 4475f5d..229a0d7 100644 --- a/PLC/Methods/DeleteInterface.py +++ b/PLC/Methods/DeleteInterface.py @@ -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/DeleteInterfaceTag.py b/PLC/Methods/DeleteInterfaceTag.py index 67b2bbb..187d25e 100644 --- a/PLC/Methods/DeleteInterfaceTag.py +++ b/PLC/Methods/DeleteInterfaceTag.py @@ -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']] diff --git a/PLC/Methods/DeleteNodeTag.py b/PLC/Methods/DeleteNodeTag.py index 9b3fdb8..bd99f0e 100644 --- a/PLC/Methods/DeleteNodeTag.py +++ b/PLC/Methods/DeleteNodeTag.py @@ -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']] diff --git a/PLC/Methods/DeletePersonTag.py b/PLC/Methods/DeletePersonTag.py index e4abcdd..19ab5fe 100644 --- a/PLC/Methods/DeletePersonTag.py +++ b/PLC/Methods/DeletePersonTag.py @@ -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']] diff --git a/PLC/Methods/DeleteRoleFromPerson.py b/PLC/Methods/DeleteRoleFromPerson.py index 28dedfb..4deacf6 100644 --- a/PLC/Methods/DeleteRoleFromPerson.py +++ b/PLC/Methods/DeleteRoleFromPerson.py @@ -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 index 0000000..cdce6fa --- /dev/null +++ b/PLC/Methods/DeleteRoleFromTagType.py @@ -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 diff --git a/PLC/Methods/DeleteSite.py b/PLC/Methods/DeleteSite.py index 9776275..db2b294 100644 --- a/PLC/Methods/DeleteSite.py +++ b/PLC/Methods/DeleteSite.py @@ -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/DeleteSiteTag.py b/PLC/Methods/DeleteSiteTag.py index ab22400..f0e87c3 100644 --- a/PLC/Methods/DeleteSiteTag.py +++ b/PLC/Methods/DeleteSiteTag.py @@ -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']] diff --git a/PLC/Methods/DeleteSliceTag.py b/PLC/Methods/DeleteSliceTag.py index 9e2ad06..e63393e 100644 --- a/PLC/Methods/DeleteSliceTag.py +++ b/PLC/Methods/DeleteSliceTag.py @@ -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(), diff --git a/PLC/Methods/UpdateIlink.py b/PLC/Methods/UpdateIlink.py index dae4633..05daf17 100644 --- a/PLC/Methods/UpdateIlink.py +++ b/PLC/Methods/UpdateIlink.py @@ -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() diff --git a/PLC/Methods/UpdateInterfaceTag.py b/PLC/Methods/UpdateInterfaceTag.py index 4a3f02f..e29041f 100644 --- a/PLC/Methods/UpdateInterfaceTag.py +++ b/PLC/Methods/UpdateInterfaceTag.py @@ -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() diff --git a/PLC/Methods/UpdateNodeTag.py b/PLC/Methods/UpdateNodeTag.py index a783f16..3cc7891 100644 --- a/PLC/Methods/UpdateNodeTag.py +++ b/PLC/Methods/UpdateNodeTag.py @@ -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() diff --git a/PLC/Methods/UpdatePersonTag.py b/PLC/Methods/UpdatePersonTag.py index 4162684..97862ef 100644 --- a/PLC/Methods/UpdatePersonTag.py +++ b/PLC/Methods/UpdatePersonTag.py @@ -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() diff --git a/PLC/Methods/UpdateSiteTag.py b/PLC/Methods/UpdateSiteTag.py index 06919bf..901a875 100644 --- a/PLC/Methods/UpdateSiteTag.py +++ b/PLC/Methods/UpdateSiteTag.py @@ -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() diff --git a/PLC/Methods/UpdateSliceTag.py b/PLC/Methods/UpdateSliceTag.py index 76a6354..e8b8b33 100644 --- a/PLC/Methods/UpdateSliceTag.py +++ b/PLC/Methods/UpdateSliceTag.py @@ -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): """ diff --git a/PLC/Methods/UpdateTagType.py b/PLC/Methods/UpdateTagType.py index bad4ba2..56bd61f 100644 --- a/PLC/Methods/UpdateTagType.py +++ b/PLC/Methods/UpdateTagType.py @@ -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): """ diff --git a/PLC/NodeTags.py b/PLC/NodeTags.py index c289dee..7f69e32 100644 --- a/PLC/NodeTags.py +++ b/PLC/NodeTags.py @@ -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): diff --git a/PLC/PersonTags.py b/PLC/PersonTags.py index be1e86e..30869af 100644 --- a/PLC/PersonTags.py +++ b/PLC/PersonTags.py @@ -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 diff --git a/PLC/Roles.py b/PLC/Roles.py index 03d9884..4aa4d6b 100644 --- a/PLC/Roles.py +++ b/PLC/Roles.py @@ -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), diff --git a/PLC/SiteTags.py b/PLC/SiteTags.py index c70a46c..45cbfd9 100644 --- a/PLC/SiteTags.py +++ b/PLC/SiteTags.py @@ -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 diff --git a/PLC/Sites.py b/PLC/Sites.py index fca3b14..cfb26ad 100644 --- a/PLC/Sites.py +++ b/PLC/Sites.py @@ -1,5 +1,3 @@ -# $Id$ -# $URL$ from types import StringTypes import string diff --git a/PLC/SliceTags.py b/PLC/SliceTags.py index a320e62..78273ca 100644 --- a/PLC/SliceTags.py +++ b/PLC/SliceTags.py @@ -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"), } diff --git a/PLC/TagTypes.py b/PLC/TagTypes.py index d4d4cd7..c045a63 100644 --- a/PLC/TagTypes.py +++ b/PLC/TagTypes.py @@ -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 index 0000000..f7d7220 --- /dev/null +++ b/migrations/104-down-noderole.sql @@ -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 index 0000000..c31af7a --- /dev/null +++ b/migrations/104-up-noderole.sql @@ -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; diff --git a/planetlab5.sql b/planetlab5.sql index 6ac84c5..4083039 100644 --- a/planetlab5.sql +++ b/planetlab5.sql @@ -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; -- 2.43.0