fix roles interface
authorMark Huang <mlhuang@cs.princeton.edu>
Tue, 10 Oct 2006 21:54:59 +0000 (21:54 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Tue, 10 Oct 2006 21:54:59 +0000 (21:54 +0000)
12 files changed:
PLC/Attributes.py
PLC/Methods/AddRole.py [new file with mode: 0644]
PLC/Methods/AddRoleToPerson.py
PLC/Methods/AdmGetAllRoles.py
PLC/Methods/AdmGrantRoleToPerson.py
PLC/Methods/AdmIsPersonInRole.py
PLC/Methods/AdmRevokeRoleFromPerson.py
PLC/Methods/DeleteRole.py [new file with mode: 0644]
PLC/Methods/DeleteRoleFromPerson.py
PLC/Methods/GetRoles.py [new file with mode: 0644]
PLC/Methods/__init__.py
PLC/Roles.py

index 5bd3cf1..77623d2 100644 (file)
@@ -3,7 +3,7 @@ from types import StringTypes
 from PLC.Faults import *
 from PLC.Parameter import Parameter
 from PLC.Table import Row, Table
-from PLC.Roles import Roles
+from PLC.Roles import Role, Roles
 
 class Attribute(Row):
     """
diff --git a/PLC/Methods/AddRole.py b/PLC/Methods/AddRole.py
new file mode 100644 (file)
index 0000000..109374c
--- /dev/null
@@ -0,0 +1,30 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Roles import Role, Roles
+from PLC.Auth import PasswordAuth
+
+class AddRole(Method):
+    """
+    Adds a new role.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin']
+
+    accepts = [
+        PasswordAuth(),
+        Role.fields['role_id'],
+        Role.fields['name']
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    def call(self, auth, role_id, name):
+        role = Role(self.api)
+        role['role_id'] = role_id
+        role['name'] = name
+        role.sync(insert = True)
+
+        return 1
index 25b54e2..2c63d5e 100644 (file)
@@ -29,7 +29,11 @@ class AddRoleToPerson(Method):
 
     def call(self, auth, person_id_or_email, role_id_or_name):
         # Get all roles
-        roles = Roles(self.api)
+        roles = {}
+        for role_id, role in Roles(self.api).iteritems():
+            roles[role_id] = role['name']
+            roles[role['name']] = role_id
+
         if role_id_or_name not in roles:
             raise PLCInvalidArgument, "Invalid role identifier or name"
 
index 6a80dd3..ea6c18f 100644 (file)
@@ -1,7 +1,7 @@
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter
-from PLC.Roles import Roles
+from PLC.Roles import Role, Roles
 from PLC.Auth import PasswordAuth
 
 class AdmGetAllRoles(Method):
@@ -20,13 +20,11 @@ class AdmGetAllRoles(Method):
     returns = dict
 
     def call(self, auth):
-        roles = Roles(self.api)
+        roles_list = Roles(self.api).values()
 
-        # Just the role_id: name mappings
-        roles = dict(filter(lambda (role_id, name): isinstance(role_id, (int, long)), \
-                            roles.items()))
+        roles_dict = {}
+        for role in roles_list:
+            # Stringify the keys!
+            roles_dict[str(role['role_id'])] = role['name']
 
-        # Stringify the keys!
-        keys = map(str, roles.keys())
-
-        return dict(zip(keys, roles.values()))
+        return roles_dict
index 973d107..7e10f88 100644 (file)
@@ -29,7 +29,11 @@ class AdmGrantRoleToPerson(Method):
 
     def call(self, auth, person_id_or_email, role_id_or_name):
         # Get all roles
-        roles = Roles(self.api)
+        roles = {}
+        for role_id, role in Roles(self.api).iteritems():
+            roles[role_id] = role['name']
+            roles[role['name']] = role_id
+
         if role_id_or_name not in roles:
             raise PLCInvalidArgument, "Invalid role identifier or name"
 
index ae3e04b..24a0059 100644 (file)
@@ -5,7 +5,7 @@ from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Persons import Person, Persons
 from PLC.Auth import PasswordAuth
-from PLC.Roles import Roles
+from PLC.Roles import Role, Roles
 
 class AdmIsPersonInRole(Method):
     """
@@ -35,7 +35,11 @@ class AdmIsPersonInRole(Method):
         # work.
 
         # Only allow PI roles to be checked
-        roles = Roles(self.api)
+        roles = {}
+        for role_id, role in Roles(self.api).iteritems():
+            roles[role_id] = role['name']
+            roles[role['name']] = role_id
+
         if role_id_or_name not in roles:
             raise PLCInvalidArgument, "Invalid role identifier or name"
 
index 61dbda5..cb7c2df 100644 (file)
@@ -21,16 +21,26 @@ class AdmRevokeRoleFromPerson(Method):
         PasswordAuth(),
         Mixed(Person.fields['person_id'],
               Person.fields['email']),
-        Parameter(int, 'Role ID')
+        Mixed(Parameter(int, "Role identifier"),
+              Parameter(str, "Role name"))
         ]
 
     returns = Parameter(int, '1 if successful')
 
-    def call(self, auth, person_id_or_email, role_id):
+    def call(self, auth, person_id_or_email, role_id_or_name):
         # Get all roles
-        roles = Roles(self.api)
-        if role_id not in roles:
-            raise PLCInvalidArgument, "Invalid role ID"
+        roles = {}
+        for role_id, role in Roles(self.api).iteritems():
+            roles[role_id] = role['name']
+            roles[role['name']] = role_id
+
+        if role_id_or_name not in roles:
+            raise PLCInvalidArgument, "Invalid role identifier or name"
+
+        if isinstance(role_id_or_name, int):
+            role_id = role_id_or_name
+        else:
+            role_id = roles[role_id_or_name]
 
         # Get account information
         persons = Persons(self.api, [person_id_or_email])
diff --git a/PLC/Methods/DeleteRole.py b/PLC/Methods/DeleteRole.py
new file mode 100644 (file)
index 0000000..38287a9
--- /dev/null
@@ -0,0 +1,36 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Roles import Role, Roles
+from PLC.Auth import PasswordAuth
+
+class DeleteRole(Method):
+    """
+    Deletes a role.
+
+    WARNING: This will remove the specified role from all accounts
+    that possess it, and from all node and slice attributes that refer
+    to it.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin']
+
+    accepts = [
+        PasswordAuth(),
+        Mixed(Role.fields['role_id'],
+              Role.fields['name'])
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    def call(self, auth, role_id_or_name):
+        roles = Roles(self.api, [role_id_or_name])
+        if not roles:
+            raise PLCInvalidArgument, "No such role"
+        role = roles.values()[0]
+
+        role.delete()
+
+        return 1
index 132eec4..973d971 100644 (file)
@@ -21,16 +21,26 @@ class DeleteRoleFromPerson(Method):
         PasswordAuth(),
         Mixed(Person.fields['person_id'],
               Person.fields['email']),
-        Parameter(int, 'Role ID')
+        Mixed(Parameter(int, "Role identifier"),
+              Parameter(str, "Role name"))
         ]
 
     returns = Parameter(int, '1 if successful')
 
-    def call(self, auth, person_id_or_email, role_id):
+    def call(self, auth, person_id_or_email, role_id_or_name):
         # Get all roles
-        roles = Roles(self.api)
-        if role_id not in roles:
-            raise PLCInvalidArgument, "Invalid role ID"
+        roles = {}
+        for role_id, role in Roles(self.api).iteritems():
+            roles[role_id] = role['name']
+            roles[role['name']] = role_id
+
+        if role_id_or_name not in roles:
+            raise PLCInvalidArgument, "Invalid role identifier or name"
+
+        if isinstance(role_id_or_name, int):
+            role_id = role_id_or_name
+        else:
+            role_id = roles[role_id_or_name]
 
         # Get account information
         persons = Persons(self.api, [person_id_or_email])
diff --git a/PLC/Methods/GetRoles.py b/PLC/Methods/GetRoles.py
new file mode 100644 (file)
index 0000000..1a2c196
--- /dev/null
@@ -0,0 +1,21 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Roles import Role, Roles
+from PLC.Auth import PasswordAuth
+
+class GetRoles(Method):
+    """
+    Get an array of structs containing details about all roles.
+    """
+
+    roles = ['admin', 'pi', 'user', 'tech']
+
+    accepts = [
+        PasswordAuth()
+        ]
+
+    returns = [Role.fields]
+
+    def call(self, auth):
+        return Roles(self.api).values()
index 003052e..0327c3d 100644 (file)
@@ -1 +1 @@
-methods = 'AddAddress AddAddressType AddAddressTypeToAddress AddAttribute AddKey AddNetworkMethod AddNetworkType AddNodeGroup AddNodeNetwork AddNode AddNodeToNodeGroup AddPerson AddPersonToSite AddPersonToSlice AddRoleToPerson AddSite AddSliceAttribute AddSlice AddSliceToNodes AdmAddNodeGroup AdmAddNodeNetwork AdmAddNode AdmAddNodeToNodeGroup AdmAddPerson AdmAddPersonToSite AdmAddSite AdmAuthCheck AdmDeleteNodeGroup AdmDeleteNodeNetwork AdmDeleteNode AdmDeletePerson AdmDeleteSite AdmGetAllNodeNetworkBandwidthLimits AdmGetAllNodeNetworks AdmGetAllRoles AdmGetNodeGroupNodes AdmGetNodeGroups AdmGetNodes AdmGetPersonRoles AdmGetPersonSites AdmGetPersons AdmGetSiteNodes AdmGetSitePersons AdmGetSites AdmGrantRoleToPerson AdmIsPersonInRole AdmRemoveNodeFromNodeGroup AdmRemovePersonFromSite AdmRevokeRoleFromPerson AdmSetPersonEnabled AdmSetPersonPrimarySite AdmUpdateNodeGroup AdmUpdateNodeNetwork AdmUpdateNode AdmUpdatePerson AdmUpdateSite AuthCheck DeleteAddress DeleteAddressTypeFromAddress DeleteAddressType DeleteAttribute DeleteKey DeleteNetworkMethod DeleteNetworkType DeleteNodeFromNodeGroup DeleteNodeGroup DeleteNodeNetwork DeleteNode DeletePersonFromSite DeletePersonFromSlice DeletePerson DeleteRoleFromPerson DeleteSite DeleteSliceAttribute DeleteSliceFromNodes DeleteSlice GetAddresses GetAddressTypes GetAttributes GetKeys GetNetworkMethods GetNetworkTypes GetNodeGroups GetNodeNetworks GetNodes GetPersons GetSites GetSliceAttributes GetSlices SetPersonPrimaryKey SetPersonPrimarySite UpdateAddress UpdateAddressType UpdateAttribute UpdateKey UpdateNodeGroup UpdateNodeNetwork UpdateNode UpdatePerson UpdateSite UpdateSliceAttribute UpdateSlice  system.listMethods  system.methodHelp  system.methodSignature  system.multicall'.split()
+methods = 'AddAddress AddAddressType AddAddressTypeToAddress AddAttribute AddBootState AddKey AddNetworkMethod AddNetworkType AddNodeGroup AddNodeNetwork AddNode AddNodeToNodeGroup AddPerson AddPersonToSite AddPersonToSlice AddRole AddRoleToPerson AddSite AddSliceAttribute AddSlice AddSliceToNodes AdmAddNodeGroup AdmAddNodeNetwork AdmAddNode AdmAddNodeToNodeGroup AdmAddPerson AdmAddPersonToSite AdmAddSite AdmAuthCheck AdmDeleteNodeGroup AdmDeleteNodeNetwork AdmDeleteNode AdmDeletePerson AdmDeleteSite AdmGetAllNodeNetworkBandwidthLimits AdmGetAllNodeNetworks AdmGetAllRoles AdmGetNodeGroupNodes AdmGetNodeGroups AdmGetNodes AdmGetPersonRoles AdmGetPersonSites AdmGetPersons AdmGetSiteNodes AdmGetSitePersons AdmGetSites AdmGrantRoleToPerson AdmIsPersonInRole AdmRemoveNodeFromNodeGroup AdmRemovePersonFromSite AdmRevokeRoleFromPerson AdmSetPersonEnabled AdmSetPersonPrimarySite AdmUpdateNodeGroup AdmUpdateNodeNetwork AdmUpdateNode AdmUpdatePerson AdmUpdateSite AuthCheck DeleteAddress DeleteAddressTypeFromAddress DeleteAddressType DeleteAttribute DeleteBootState DeleteKey DeleteNetworkMethod DeleteNetworkType DeleteNodeFromNodeGroup DeleteNodeGroup DeleteNodeNetwork DeleteNode DeletePersonFromSite DeletePersonFromSlice DeletePerson DeleteRoleFromPerson DeleteRole DeleteSite DeleteSliceAttribute DeleteSliceFromNodes DeleteSlice GetAddresses GetAddressTypes GetAttributes GetBootStates GetKeys GetNetworkMethods GetNetworkTypes GetNodeGroups GetNodeNetworks GetNodes GetPersons GetRoles GetSites GetSliceAttributes GetSlices SetPersonPrimaryKey SetPersonPrimarySite UpdateAddress UpdateAddressType UpdateAttribute UpdateKey UpdateNodeGroup UpdateNodeNetwork UpdateNode UpdatePerson UpdateSite UpdateSliceAttribute UpdateSlice  system.listMethods  system.methodHelp  system.methodSignature  system.multicall'.split()
index c035fe6..40c3277 100644 (file)
@@ -4,22 +4,92 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Roles.py,v 1.1 2006/09/06 15:36:07 mlhuang Exp $
+# $Id: Roles.py,v 1.2 2006/10/06 18:19:41 mlhuang Exp $
 #
 
-class Roles(dict):
+from types import StringTypes
+from PLC.Faults import *
+from PLC.Parameter import Parameter
+from PLC.Table import Row, Table
+
+class Role(Row):
+    """
+    Representation of a row in the roles table. To use,
+    instantiate with a dict of values.
+    """
+
+    table_name = 'roles'
+    primary_key = 'role_id'
+    fields = {
+        'role_id': Parameter(int, "Role identifier"),
+        'name': Parameter(str, "Role", max = 100),
+        }
+
+    def __init__(self, api, fields = {}):
+        Row.__init__(self, fields)
+        self.api = api
+
+    def validate_role_id(self, role_id):
+       # Make sure role does not already exist
+       conflicts = Roles(self.api, [role_id])
+        if conflicts:
+            raise PLCInvalidArgument, "Role ID already in use"
+
+        return role_id
+
+    def validate_name(self, name):
+       # Remove leading and trailing spaces
+       name = name.strip()
+
+       # Make sure name is not blank after we removed the spaces
+        if not name:
+            raise PLCInvalidArgument, "Role must be specified"
+       
+       # Make sure role does not already exist
+       conflicts = Roles(self.api, [name])
+        if conflicts:
+            raise PLCInvalidArgument, "Role name already in use"
+
+       return name
+
+    def delete(self, commit = True):
+        assert 'role_id' in self
+
+        # Clean up miscellaneous join tables
+        for table in ['person_role', 'roles']:
+            self.api.db.do("DELETE FROM %s" \
+                           " WHERE role_id = %d" % \
+                           (table, self['role_id']), self)
+
+        self.api.db.do("DELETE FROM attributes WHERE min_role_id = %d" % \
+                       self['role_id'])
+
+        if commit:
+            self.api.db.commit()
+        
+class Roles(Table):
     """
     Representation of the roles table in the database.
     """
 
-    # Role IDs equal to or lower than this number are for use by real
-    # accounts. Other role IDs are used internally.
-    role_max = 500
+    def __init__(self, api, role_id_or_name_list = None):
+        sql = "SELECT %s FROM roles" % \
+              ", ".join(Role.fields)
+        
+        if role_id_or_name_list:
+            # Separate the list into integers and strings
+            role_ids = filter(lambda role_id: isinstance(role_id, (int, long)),
+                                   role_id_or_name_list)
+            names = filter(lambda name: isinstance(name, StringTypes),
+                           role_id_or_name_list)
+            sql += " WHERE (False"
+            if role_ids:
+                sql += " OR role_id IN (%s)" % ", ".join(map(str, role_ids))
+            if names:
+                sql += " OR name IN (%s)" % ", ".join(api.db.quote(names))
+            sql += ")"
 
-    def __init__(self, api):
-        sql = "SELECT * FROM roles" \
-              " WHERE role_id <= %d" % self.role_max
+        rows = api.db.selectall(sql)
 
-        for row in api.db.selectall(sql):
-            self[row['role_id']] = row['name']
-            self[row['name']] = row['role_id']
+        for row in rows:
+            self[row['role_id']] = Role(api, row)