Add PersonTags as well. Performed a simple test but not extensive regression
authorStephen Soltesz <soltesz@cs.princeton.edu>
Tue, 1 Dec 2009 17:08:37 +0000 (17:08 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Tue, 1 Dec 2009 17:08:37 +0000 (17:08 +0000)
tests (i.e. by non-admin users, etc)

PLC/Methods/AddPersonTag.py [new file with mode: 0644]
PLC/Methods/DeletePersonTag.py [new file with mode: 0644]
PLC/Methods/GetPersonTags.py [new file with mode: 0644]
PLC/Methods/UpdatePersonTag.py [new file with mode: 0644]
PLC/Methods/__init__.py
PLC/PersonTags.py [new file with mode: 0644]
PLC/Persons.py
PLC/TagTypes.py
PLC/__init__.py

diff --git a/PLC/Methods/AddPersonTag.py b/PLC/Methods/AddPersonTag.py
new file mode 100644 (file)
index 0000000..6824657
--- /dev/null
@@ -0,0 +1,86 @@
+# $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.TagTypes import TagType, TagTypes
+from PLC.PersonTags import PersonTag, PersonTags
+from PLC.Persons import Person, Persons
+
+from PLC.Nodes import Nodes
+
+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.
+
+    Returns the new person_tag_id (> 0) if successful, faults
+    otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech', 'user']
+
+    accepts = [
+        Auth(),
+        # no other way to refer to a person
+        PersonTag.fields['person_id'],
+        Mixed(TagType.fields['tag_type_id'],
+              TagType.fields['tagname']),
+        PersonTag.fields['value'],
+        ]
+
+    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:
+            raise PLCInvalidArgument, "No such person %r"%person_id
+        person = persons[0]
+
+        tag_types = TagTypes(self.api, [tag_type_id_or_name])
+        if not tag_types:
+            raise PLCInvalidArgument, "No such tag type %r"%tag_type_id_or_name
+        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']})
+
+        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
+
+        person_tag = PersonTag(self.api)
+        person_tag['person_id'] = person['person_id']
+        person_tag['tag_type_id'] = tag_type['tag_type_id']
+        person_tag['value'] = value
+
+        person_tag.sync()
+       self.object_ids = [person_tag['person_tag_id']]
+
+        return person_tag['person_tag_id']
diff --git a/PLC/Methods/DeletePersonTag.py b/PLC/Methods/DeletePersonTag.py
new file mode 100644 (file)
index 0000000..51a1432
--- /dev/null
@@ -0,0 +1,71 @@
+# $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
+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
+
+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.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'user']
+
+    accepts = [
+        Auth(),
+        PersonTag.fields['person_tag_id']
+        ]
+
+    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']
+           
+           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
+
+        person_tag.delete()
+       self.object_ids = [person_tag['person_tag_id']]
+
+        return 1
diff --git a/PLC/Methods/GetPersonTags.py b/PLC/Methods/GetPersonTags.py
new file mode 100644 (file)
index 0000000..516638f
--- /dev/null
@@ -0,0 +1,44 @@
+# $Id: GetPersonTags.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/GetPersonTags.py $
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 14587 $
+#
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Filter import Filter
+from PLC.Auth import Auth
+
+from PLC.PersonTags import PersonTag, PersonTags
+
+class GetPersonTags(Method):
+    """
+    Returns an array of structs containing details about
+    persons and related settings.
+
+    If person_tag_filter is specified and is an array of
+    person setting identifiers, only person settings matching
+    the filter will be returned. If return_fields is specified, only
+    the specified details will be returned.
+    """
+
+    roles = ['admin', 'pi', 'user', 'node']
+
+    accepts = [
+        Auth(),
+        Mixed([PersonTag.fields['person_tag_id']],
+              Parameter(int,"Person setting id"),
+              Filter(PersonTag.fields)),
+        Parameter([str], "List of fields to return", nullok = True)
+        ]
+
+    returns = [PersonTag.fields]
+    
+
+    def call(self, auth, person_tag_filter = None, return_fields = None):
+
+        person_tags = PersonTags(self.api, person_tag_filter, return_fields)
+
+        return person_tags
diff --git a/PLC/Methods/UpdatePersonTag.py b/PLC/Methods/UpdatePersonTag.py
new file mode 100644 (file)
index 0000000..928b54f
--- /dev/null
@@ -0,0 +1,68 @@
+# $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 $
+#
+
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+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
+
+class UpdatePersonTag(Method):
+    """
+    Updates the value of an existing person setting
+
+    Access rights depend on the tag type.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech', 'user']
+
+    accepts = [
+        Auth(),
+        PersonTag.fields['person_tag_id'],
+        PersonTag.fields['value']
+        ]
+
+    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 len(set(person['person_ids']) & set(self.caller['person_ids'])) == 0:
+               raise PLCPermissionDenied, "Not a member of the person's persons: %s"%person['person_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
+
+        person_tag['value'] = value
+        person_tag.sync()
+
+       self.object_ids = [person_tag['person_tag_id']]
+        return 1
index 50c1dea..52fb070 100644 (file)
@@ -25,6 +25,7 @@ AddPCUType
 AddPeer
 AddPerson
 AddPersonKey
+AddPersonTag
 AddPersonToSite
 AddPersonToSlice
 AddRole
@@ -75,6 +76,7 @@ DeletePeer
 DeletePerson
 DeletePersonFromSite
 DeletePersonFromSlice
+DeletePersonTag
 DeleteRole
 DeleteRoleFromPerson
 DeleteSession
@@ -113,6 +115,7 @@ GetPCUs
 GetPeerData
 GetPeerName
 GetPeers
+GetPersonTags
 GetPersons
 GetPlcRelease
 GetRoles
@@ -172,6 +175,7 @@ UpdatePCUProtocolType
 UpdatePCUType
 UpdatePeer
 UpdatePerson
+UpdatePersonTag
 UpdateSite
 UpdateSiteTag
 UpdateSlice
diff --git a/PLC/PersonTags.py b/PLC/PersonTags.py
new file mode 100644 (file)
index 0000000..1809470
--- /dev/null
@@ -0,0 +1,61 @@
+# $Id: PersonTags.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/PersonTags.py $
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 14587 $
+#
+from PLC.Faults import *
+from PLC.Parameter import Parameter
+from PLC.Filter import Filter
+from PLC.Table import Row, Table
+from PLC.TagTypes import TagType, TagTypes
+from PLC.Persons import Person
+
+class PersonTag(Row):
+    """
+    Representation of a row in the person_tag.
+    To use, instantiate with a dict of values.
+    """
+
+    table_name = 'person_tag'
+    primary_key = 'person_tag_id'
+    fields = {
+        'person_tag_id': Parameter(int, "Person setting identifier"),
+        'person_id': Person.fields['person_id'],
+        'email': Person.fields['email'],
+        'tag_type_id': TagType.fields['tag_type_id'],
+        '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
+       
+        }
+
+class PersonTags(Table):
+    """
+    Representation of row(s) from the person_tag table in the
+    database.
+    """
+
+    def __init__(self, api, person_tag_filter = None, columns = None):
+        Table.__init__(self, api, PersonTag, columns)
+
+        sql = "SELECT %s FROM view_person_tags WHERE True" % \
+              ", ".join(self.columns)
+
+        if person_tag_filter is not None:
+            if isinstance(person_tag_filter, (list, tuple, set)):
+                person_tag_filter = Filter(PersonTag.fields, {'person_tag_id': person_tag_filter})
+            elif isinstance(person_tag_filter, dict):
+                person_tag_filter = Filter(PersonTag.fields, person_tag_filter)
+            elif isinstance(person_tag_filter, int):
+                person_tag_filter = Filter(PersonTag.fields, {'person_tag_id': [person_tag_filter]})
+            else:
+                raise PLCInvalidArgument, "Wrong person setting filter %r"%person_tag_filter
+            sql += " AND (%s) %s" % person_tag_filter.sql(api)
+
+
+        self.selectall(sql)
index 862d6ba..678697d 100644 (file)
@@ -57,6 +57,7 @@ class Person(Row):
         'slice_ids': Parameter([int], "List of slice identifiers"),
         'peer_id': Parameter(int, "Peer to which this user belongs", nullok = True),
         'peer_person_id': Parameter(int, "Foreign user identifier at peer", nullok = True),
+        'person_tag_ids' : Parameter ([int], "List of tags attached to this person"),
         }
     related_fields = {
        'roles': [Mixed(Parameter(int, "Role identifier"),
@@ -68,6 +69,10 @@ class Person(Row):
        'slices': [Mixed(Parameter(int, "Slice identifier"),
                         Parameter(str, "Slice name"))]
        }       
+    view_tags_name = "view_person_tags"
+    # tags are used by the Add/Get/Update methods to expose tags
+    # this is initialized here and updated by the accessors factory
+    tags = { }
 
     def validate_email(self, email):
         """
@@ -380,8 +385,13 @@ class Persons(Table):
     def __init__(self, api, person_filter = None, columns = None):
         Table.__init__(self, api, Person, columns)
 
-       sql = "SELECT %s FROM view_persons WHERE deleted IS False" % \
-              ", ".join(self.columns)
+        view = "view_persons"
+        for tagname in self.tag_columns:
+            view= "%s left join %s using (%s)"%(view,Person.tagvalue_view_name(tagname),
+                                                Person.primary_key)
+            
+        sql = "SELECT %s FROM %s WHERE deleted IS False" % \
+            (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
 
         if person_filter is not None:
             if isinstance(person_filter, (list, tuple, set)):
index 39306f5..1f4835c 100644 (file)
@@ -23,7 +23,7 @@ class TagType (Row):
 
     table_name = 'tag_types'
     primary_key = 'tag_type_id'
-    join_tables = ['node_tag', 'interface_tag', 'slice_tag', 'site_tag' ]
+    join_tables = ['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),
index 5ee17c4..0be5ac4 100644 (file)
@@ -34,6 +34,7 @@ PCUs
 POD
 Parameter
 Peers
+PersonTags
 Persons
 PostgreSQL
 PyCurl