assert primary key exists in record before attempting to delete it
[plcapi.git] / PLC / Keys.py
index 175a16b..07a05e4 100644 (file)
-from types import StringTypes
+import re
 
 from PLC.Faults import *
 from PLC.Parameter import Parameter
+from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
+from PLC.KeyTypes import KeyType, KeyTypes
+from PLC.NovaTable import NovaObject, NovaTable
 
-class Key(Row):
+class Key(NovaObject):
     """
-    Representation of a row in the keys table. To use, instantiate
-    with a dict of values.
+    Representation of a row in the keys table. To use, instantiate with a
+    dict of values. Update as you would a dict. Commit to the database
+    with sync().
     """
 
+    tablename = 'keys'
+    join_tables = ['person_key', 'peer_key']
     fields = {
-        'key_id': Parameter(int, "Key type"),
-        'key_type': Parameter(str, "Key type"),
-        'key': Parameter(str, "Key value"),
-        'last_updated': Parameter(str, "Date and time of last update"),
-        'is_blacklisted': Parameter(str, "Key has been blacklisted and is forever unusable"),
+        'id': Parameter(str, "Key identifier", primary_key=True),
+        #'key_type': Parameter(str, "Key type"),
+        'public_key': Parameter(str, "Key string", max = 4096),
+        'name': Parameter(str, "Key name",)
         }
 
-    def __init__(self, api, fields):
-        self.api = api
-        dict.__init__(fields)
-
-    def commit(self):
-        # XXX
-        pass
+    def sync(self, insert = False, validate = True):
+        NovaObject.sync(self, insert, validate)
+        if insert == True or 'id' not in self:
+            self.object = self.api.client_shell.nova.keypairs.create(self.id,
+                                                                     self.key)
+        else:
+            self.object = self.api.client_shell.nova.keypairs.update(self.id, dict(self))
 
     def delete(self):
-        # XXX
+        assert 'id' in self
+        self.api.client_shell.nova.keypairs.delete(self.id)
+
+    def validate_public_key(self, key):
+        # Key must not be blacklisted
         pass
+        return key
+
+    def validate_name(self, name)
+        keys = Keys(self.api, name)
+        if keys:
+            raise PLCInvalidArgument, "Key name alredy in use"
+
+    def validate(self):
+        # Basic validation
+        NovaObject.validate(self)
+
+        assert 'public_key' in self
+        key = self['public_key']
+
+        if self['key_type'] == 'ssh':
+            # Accept only SSH version 2 keys without options. From
+            # sshd(8):
+            #
+            # Each protocol version 2 public key consists of: options,
+            # keytype, base64 encoded key, comment.  The options field
+            # is optional...The comment field is not used for anything
+            # (but may be convenient for the user to identify the
+            # key). For protocol version 2 the keytype is ``ssh-dss''
+            # or ``ssh-rsa''.
+
+            good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
+            if not re.match(good_ssh_key, key, re.IGNORECASE):
+                raise PLCInvalidArgument, "Invalid SSH version 2 public key"
+
+    def blacklist(self, commit = True):
+        """
+        Permanently blacklist key (and all other identical keys),
+        preventing it from ever being added again. Because this could
+        affect multiple keys associated with multiple accounts, it
+        should be admin only.
+        """
 
-class Keys(Table):
+        assert 'key_id' in self
+        assert 'key' in self
+
+        # Get all matching keys
+        rows = self.api.db.selectall("SELECT key_id FROM keys WHERE key = %(key)s",
+                                     self)
+        key_ids = [row['key_id'] for row in rows]
+        assert key_ids
+        assert self['key_id'] in key_ids
+
+        # Keep the keys in the table
+        self.api.db.do("UPDATE keys SET is_blacklisted = True" \
+                       " WHERE key_id IN (%s)" % ", ".join(map(str, key_ids)))
+
+        # But disassociate them from all join tables
+        for table in self.join_tables:
+            self.api.db.do("DELETE FROM %s WHERE key_id IN (%s)" % \
+                           (table, ", ".join(map(str, key_ids))))
+
+        if commit:
+            self.api.db.commit()
+
+class Keys(NovaTable):
     """
     Representation of row(s) from the keys table in the
     database.
     """
 
-    def __init__(self, api, key_id_list = None):
-        # XXX
-        pass
+    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)
+