From 0e514ee3c14478bd852f71bf0211335c5ec848f0 Mon Sep 17 00:00:00 2001 From: Tony Mack Date: Sun, 10 Mar 2013 22:14:41 -0400 Subject: [PATCH] Fix KeyTypes, Keys --- PLC/KeyTypes.py | 42 +++++++------ PLC/Keys.py | 110 +++++++++++++++++++++++------------ PLC/Methods/AddKeyType.py | 2 +- PLC/Methods/AddPersonKey.py | 9 +-- PLC/Storage/AlchemyObject.py | 3 + 5 files changed, 104 insertions(+), 62 deletions(-) diff --git a/PLC/KeyTypes.py b/PLC/KeyTypes.py index df15643b..a3b048b1 100644 --- a/PLC/KeyTypes.py +++ b/PLC/KeyTypes.py @@ -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) diff --git a/PLC/Keys.py b/PLC/Keys.py index c1744703..e633a431 100644 --- a/PLC/Keys.py +++ b/PLC/Keys.py @@ -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) diff --git a/PLC/Methods/AddKeyType.py b/PLC/Methods/AddKeyType.py index b3690a86..04802b4c 100644 --- a/PLC/Methods/AddKeyType.py +++ b/PLC/Methods/AddKeyType.py @@ -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 diff --git a/PLC/Methods/AddPersonKey.py b/PLC/Methods/AddPersonKey.py index 6dd587dc..2c3fb9d1 100644 --- a/PLC/Methods/AddPersonKey.py +++ b/PLC/Methods/AddPersonKey.py @@ -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'] diff --git a/PLC/Storage/AlchemyObject.py b/PLC/Storage/AlchemyObject.py index 88b505e4..c0c3974a 100644 --- a/PLC/Storage/AlchemyObject.py +++ b/PLC/Storage/AlchemyObject.py @@ -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, -- 2.47.0