+ def validate_key(self, key):
+ # Key must not be blacklisted
+ rows = self.api.db.selectall("SELECT 1 from keys" \
+ " WHERE key = %(key)s" \
+ " AND is_blacklisted IS True",
+ locals())
+ if rows:
+ raise PLCInvalidArgument, "Key is blacklisted and cannot be used"
+
+ return key
+
+ def validate(self):
+ # Basic validation
+ Row.validate(self)
+
+ assert 'key' in self
+ key = self['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.
+ """
+
+ 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()