Fix KeyTypes, Keys
authorTony Mack <tmack@paris.CS.Princeton.EDU>
Mon, 11 Mar 2013 02:14:41 +0000 (22:14 -0400)
committerTony Mack <tmack@paris.CS.Princeton.EDU>
Mon, 11 Mar 2013 02:14:41 +0000 (22:14 -0400)
PLC/KeyTypes.py
PLC/Keys.py
PLC/Methods/AddKeyType.py
PLC/Methods/AddPersonKey.py
PLC/Storage/AlchemyObject.py

index df15643..a3b048b 100644 (file)
@@ -7,19 +7,17 @@
 
 from PLC.Faults import *
 from PLC.Parameter import Parameter
-from PLC.Table import Row, Table
+from PLC.Storage.AlchemyObject import AlchemyObj
 
-class KeyType(Row):
+class KeyType(AlchemyObj):
     """
     Representation of a row in the key_types table. To use,
     instantiate with a dict of values.
     """
 
-    table_name = 'key_types'
-    primary_key = 'key_type'
-    join_tables = ['keys']
+    tablename = 'key_types'
     fields = {
-        'key_type': Parameter(str, "Key type", max = 20),
+        'key_type': Parameter(str, "Key type", max = 20, primary_key=True),
         }
 
     def validate_key_type(self, name):
@@ -28,24 +26,34 @@ class KeyType(Row):
             raise PLCInvalidArgument, "Key type must be specified"
 
         # Make sure key type does not alredy exist
-        conflicts = KeyTypes(self.api, [name])
+        conflicts = KeyType().select(filter={'key_type': name})
         if conflicts:
             raise PLCInvalidArgument, "Key type name already in use"
 
         return name
 
-class KeyTypes(Table):
+    def sync(self, commit = True, validate=True):
+        AlchemyObj.sync(self, commit=commit, validate=validate)
+        AlchemyObj.insert(self, dict(self))
+
+    def delete(self, commit=True):
+        """
+        Delete existing key type
+        """
+        assert 'key_type' in self
+        AlchemyObj.delete(self, filter={'key_type': self['key_type']})  
+
+class KeyTypes(list):
     """
     Representation of the key_types table in the database.
     """
 
     def __init__(self, api, key_types = None):
-        Table.__init__(self, api, KeyType)
-
-        sql = "SELECT %s FROM key_types" % \
-              ", ".join(KeyType.fields)
-
-        if key_types:
-            sql += " WHERE key_type IN (%s)" % ", ".join( [ api.db.quote (t) for t in key_types ] )
-
-        self.selectall(sql)
+       if key_types:
+            key_types = KeyType().select(filter={'key_type': key_types})
+        else:
+            key_types = KeyType().select()
+
+        for key_type in key_types:
+            key_type = KeyType(api, object=key_type)
+            self.append(key_type) 
index c174470..e633a43 100644 (file)
@@ -1,4 +1,7 @@
 import re
+import random
+import string
+from types import StringTypes
 
 from PLC.Faults import *
 from PLC.Parameter import Parameter
@@ -20,44 +23,71 @@ class Key(AlchemyObj):
         'key_id': Parameter(str, "Key identifier", primary_key=True),
         'key_type': Parameter(str, "Key type"),
         'key': Parameter(str, "Key string", max = 4096), # backwards compatibility
+        'person_id': Parameter(int, "User to which this key belongs", nullok = True),
+        'is_blacklisted': Parameter(bool, "Is the key blacklisted", default=False),
         'name': Parameter(str, "Key name"),
-        'uuid': Parameter(str, "Key name", ro=True)
+        'peer_id': Parameter(int, "Peer to which this key belongs", nullok = True),
+        'peer_key_id': Parameter(int, "Foreign key identifier at peer", nullok = True),
         }
 
     def sync(self, commit = True, validate = True):
+        from PLC.Persons import Person
         # sync the nova record and the plc record
         assert 'key' in self
-        assert 'name' in self
         AlchemyObj.sync(self, commit=commit, validate=validate)
-        nova_key = {
-            'public_key': self['key'],
-            'name': self['name'] 
-        }
-        self.object = self.api.client_shell.nova.keypairs.create(**nova_key)
-        self['uuid'] = self.object.uuid
-        AlchemyObj.insert(self, dict(self))
-
+        persons = Person().select(filter={'person_id': self['person_id']})
+        if not persons:
+            raise PLCInvalidArgument, "Invalid person id" 
+        person = persons[0]
+        if 'key_id' not in self:
+            rand = ''.join(random.choice(string.ascii_letters) for x in range(5))
+            self['name'] = person.email.split('@')[0] + rand
+            nova_key = {
+                'public_key': self['key'],
+                'name': self['name']
+            }
+            self.object = self.api.client_shell.nova.keypairs.create(**nova_key)
+            AlchemyObj.insert(self, dict(self))
+            key = Key().select(filter={'name': self['name']})[0]
+            self['key_id'] = key.key_id 
+        else:
+            # nova has no way to update existing keys. Just remove the old
+            # key and add a new one 
+            nova_key = {
+                'public_key': self['key'],
+                'name': self['name']
+            }    
+            self.api.client_shell.nova.keypairs.delete(self['name'])
+            self.object = self.api.client_shell.nova.keypairs.create(**nova_key)      
+            AlchemyObj.update(self, {'key_id': self['key_id']}, dict(self))
+            
     def delete(self):
         assert 'key_id' in self
         assert 'name' in self
-        self.api.client_shell.nova.keypairs.delete(self.id)
+        self.api.client_shell.nova.keypairs.delete(self['name'])
+        AlchemyObj.delete(self, filter={'key_id': self['key_id']}) 
+        
 
-    def validate_public_key(self, key):
-        # Key must not be blacklisted
-        pass
-        return key
+    def validate_key_type(self, key_type):
+        exists = KeyTypes(self.api, key_type)
+        if not exists:
+            raise PLCInvalidArgument, "Invalid key type"
+        return key_type
 
-    def validate_name(self, name):
-        keys = Keys(self.api, name)
+    def validate_key(self, key):
+        # Key must not be blacklisted
+        keys = Keys(self.api, {'key': key, 'is_blacklisted': True})
         if keys:
-            raise PLCInvalidArgument, "Key name alredy in use"
+            raise PLCInvalidArgument, "Key is blacklisted and cannot be used"
+
+        return key
 
     def validate(self):
         # Basic validation
-        NovaObject.validate(self)
+        AlchemyObj.validate(self)
 
-        assert 'public_key' in self
-        key = self['public_key']
+        assert 'key' in self
+        key = self['key']
 
         if self['key_type'] == 'ssh':
             # Accept only SSH version 2 keys without options. From
@@ -85,7 +115,8 @@ class Key(AlchemyObj):
         assert 'key_id' in self
         assert 'key' in self
 
-        pass
+        AlchemyObj.updatedb(self, filter={'key': self['key']}, values={'is_blacklisted': True}) 
+
 
 class Keys(list):
     """
@@ -95,19 +126,24 @@ class Keys(list):
 
     def __init__(self, api, key_filter = None, columns = None):
         self.api = api
-        keysManager = self.api.client_shell.nova.keypairs
-        keyPairs = []
-
-        if key_filter is not None:
-            if isinstance(key_filter, (list, tuple, set, int, long)):
-                keyPairs = filter(lambda kp: kp.uuid in key_filter,
-                                  keysManager.findall())
-            elif isinstance(key_filter, dict):
-                keyPairs = keysManager.findall(**key_filter)
-            elif isinstnace(key_filter, StringTypes):
-                keyPairs = keyManagers.findall(uuid = key_filter)
-            else:
-                raise PLCInvalidArgument, "Wrong key filter %r"%key_filter
-
-        self.extend(keyPairs)
+
+        if not key_filter:
+            keys = Key().select()
+        elif isinstance(key_filter, dict):
+            keys = Key().select(filter=key_filter) 
+        elif isinstance(key_filter, StringTypes):
+            keys = Key().select(filter={'name': key_filter})
+        elif isinstance(key_filter, int):
+            keys = Key().select(filter={'key_id': key_filter})
+        elif isinstance(key_filter, (list, tuple, set)):
+            ints = filter(lambda x: isinstance(x, (int, long)), key_filter)
+            strs = filter(lambda x: isinstance(x, StringTypes), key_filter)
+            key_filter = {'key_id': ints, 'name': strs}
+            keys = Key().select(filter=key_filter)  
+        else:
+            raise PLCInvalidArgument, "Wrong key filter %s" % key_filter  
+            
+        for key in keys:
+            key = Key(api, object=key, columns=columns)
+            self.append(key)   
 
index b3690a8..04802b4 100644 (file)
@@ -24,6 +24,6 @@ class AddKeyType(Method):
     def call(self, auth, name):
         key_type = KeyType(self.api)
         key_type['key_type'] = name
-        key_type.sync(insert = True)
+        key_type.sync()
 
         return 1
index 6dd587d..2c3fb9d 100644 (file)
@@ -46,14 +46,9 @@ class AddPersonKey(Method):
             if person['person_id'] != self.caller['person_id']:
                 raise PLCPermissionDenied, "You may only modify your own keys"
 
+        key_fields['person_id'] = person['person_id']
         key = Key(self.api, key_fields)
-        key.sync(insert = True)
+        key.sync()
         #person.add_key(key, commit = True)
 
-        # Logging variables
-        self.event_objects = {'Person': [person['person_id']],
-                              'Key': [key['key_id']]}
-        self.message = 'Key %d added to person %d' % \
-                        (key['key_id'], person['person_id'])
-
         return key['key_id']
index 88b505e..c0c3974 100644 (file)
@@ -3,6 +3,7 @@ from datetime import datetime
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy import Table, Column, MetaData, join, ForeignKey
 from sqlalchemy import Column, Integer, String, TIMESTAMP
+from sqlalchemy.types import Boolean
 from sqlalchemy.orm import relationship, backref
 from sqlalchemy.orm import column_property
 from sqlalchemy.orm import mapper, object_mapper
@@ -42,6 +43,8 @@ class AlchemyObj(Record):
                 type = Integer
             elif param.type == datetime:
                 type = TIMESTAMP
+            elif param.type == bool:
+                type = Boolean
 
             column = Column(field, type, 
                             nullable = param.nullok,