Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Removed Meta mechanism from SQL key_manager

Replaced Meta mechanism with Session/Query one.
Updated unit tests.
  • Loading branch information...
commit dee20e10f7eaa8e2392b85b5376b53e654018cd3 1 parent 1454e31
Roman Pystin authored skraynev committed
View
2  etc/proxy-server.conf-encryption-sample
@@ -339,7 +339,7 @@ use = egg:swift#key_manager
# crypto_keystore_driver = swift.common.key_manager.drivers.dummy.DummyDriver
## To enable FakeDriver which use only one hardcoded key, uncomment next line:
# crypto_keystore_driver = swift.common.key_manager.drivers.fake.FakeDriver
-## To ebale SQLDriver which generate keys and store it into SQL database,
+## To enable SQLDriver which generate keys and store it into SQL database,
## uncomment next line:
# crypto_keystore_driver = swift.common.key_manager.drivers.sql.SQLDriver
## SQLDriver support connection URI for SQLAlchemy, it's required if used
View
17 swift/common/key_manager/drivers/base.py
@@ -54,3 +54,20 @@ def sync(self):
a structure of data storage.
"""
raise NotImplementedError()
+
+ @staticmethod
+ def validate_key_id(key_id):
+ """
+ Checks key_id to be a number
+
+ :raise ValueError: if key_id isn't converted to positive number
+ """
+ if key_id:
+ try:
+ k_id = long(key_id)
+ except ValueError:
+ raise ValueError("Incorrect value %r. String must include "
+ "only digits chars." % (key_id,))
+ if k_id < 0:
+ raise ValueError("Incorrect value %r. Only positive "
+ "numbers are allowed." % (key_id,))
View
111 swift/common/key_manager/drivers/sql/driver.py
@@ -24,9 +24,11 @@
import base64
import os
-from sqlalchemy import create_engine, exc
-from sqlalchemy.schema import MetaData, Table, Column
+from sqlalchemy import create_engine, exc, orm
+from sqlalchemy.schema import Column
from sqlalchemy.types import String, Integer
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
from migrate.versioning import api as versioning_api
from migrate import exceptions as versioning_exceptions
@@ -34,12 +36,27 @@
from swift.common.key_manager.drivers.sql import migrate_repo
-meta = MetaData()
-key_info_table = Table("key_info", meta,
- Column('account', String(42)),
- Column('key_id', Integer, primary_key=True,
- autoincrement=True),
- Column('encryption_key', String(42)))
+Base = declarative_base()
+Session = sessionmaker(expire_on_commit=False)
+
+
+class Key(Base):
+ __tablename__ = "key_info"
+
+ account = Column(String(42))
+ key_id = Column(Integer, primary_key=True, autoincrement=True)
+ encryption_key = Column(String(42))
+
+ def __init__(self, account, encryption_key):
+ self.account = account
+ self.encryption_key = encryption_key
+
+ def __eq__(self, other):
+ return self.account == other.account and self.key_id == other.key_id\
+ and self.encryption_key == other.encryption_key
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
class SQLDriver(base.KeyDriver):
@@ -61,7 +78,7 @@ def __init__(self, conf):
self.default_connection_attempts)
self.connection_url = conf.get('crypto_keystore_sql_url',
self.default_connection_url)
- self.engine = meta.bind = create_engine(self.connection_url)
+ self.engine = create_engine(self.connection_url)
self.reconnect_to_db()
def reconnect_to_db(self):
@@ -82,19 +99,23 @@ def reconnect_to_db(self):
break
def get_key_id(self, acc_name):
- """
- Give key_id is associated by account
+ """Give key_id is associated by account.
+
+ If no key_id to be found, it's created.
:param acc_name: string is name of account
- :return key_id: number of key in database
+ :returns: number of key in database
"""
- re = self.find_value("account", acc_name)
- if not re:
- self.reconnect_to_db()
- key_info_table.insert().execute(account=acc_name)
- re = self.find_value("account", acc_name)
- key_id = re[0][1]
- return key_id
+ session = Session(bind=self.engine)
+ try:
+ key = session.query(Key).filter(Key.account == acc_name).first()
+ if key is None:
+ key = Key(acc_name, generate_key())
+ session.add(key)
+ session.commit()
+ finally:
+ session.close()
+ return key.key_id
def get_key(self, key_id):
"""
@@ -103,56 +124,18 @@ def get_key(self, key_id):
:param key_id: number of key in database
:return key: string is used for encryption process
- :raise TypeError: if key_id isn't string or int
- :raise ValueError: if string include not only digits chars
:raise StandardError: if DB don't have row with current key_id
"""
- if not isinstance(key_id, long):
- try:
- key_id = long(key_id)
- except TypeError:
- raise TypeError("Incorrect type of value %r. Must be string or"
- " int." % (key_id,))
- except ValueError:
- raise ValueError("Incorrect value %r. String must include "
- "only digits chars." % (key_id,))
-
- re = self.find_value("key_id", str(key_id))
+ self.reconnect_to_db()
+ session = Session(bind=self.engine)
+ re = session.query(Key).filter(Key.key_id == key_id).all()
if not re:
raise StandardError("In DataBase no row with current key_id")
- if re[0][2] is None:
- # For openssl standart key consist of 16 hex chars
- key = os.urandom(16)
- # change format for store key in database
- enc_key = base64.b16encode(key)
- # check connection to database
- self.reconnect_to_db()
- # select key_id column
- column = key_info_table.c.key_id
- # create update method for table with current key_id
- update_method = key_info_table.update(column == str(key_id))
- # update encryption_key column using update method
- update_method.execute(encryption_key=enc_key)
- return key
-
- key = base64.b16decode(re[0][2])
+ key = base64.b16decode(re[0].encryption_key)
+ session.close()
return key
- def find_value(self, col_name, val):
- """Find row where param col_name == val
- :param conn: connection object for executing SQL commands
- :param col_name: string - name of using collumn
- :param val: string - value to search
-
- :return res: tuple with values account, key_id, key
- """
- self.reconnect_to_db()
- # select column
- column = key_info_table.c[col_name]
- # use select method
- return key_info_table.select(column == val).execute().fetchall()
-
def sync(self):
"""
Migrate database schemas.
@@ -163,3 +146,7 @@ def sync(self):
except versioning_exceptions.DatabaseNotControlledError:
versioning_api.version_control(self.connection_url, repo_path)
versioning_api.upgrade(self.connection_url, repo_path)
+
+
+def generate_key():
+ return base64.b16encode(os.urandom(16))
View
3  swift/obj/server.py
@@ -662,14 +662,15 @@ def PUT(self, request):
"""Handle HTTP PUT requests for the Swift Object Server."""
start_time = time.time()
key_id = request.headers.get('x-object-meta-key-id')
- encryption_context = self.crypto_driver.encryption_context(key_id)
try:
device, partition, account, container, obj = \
split_path(unquote(request.path), 5, 5, True)
validate_device_partition(device, partition)
+ self.key_manager.validate_key_id(key_id)
except ValueError, err:
return HTTPBadRequest(body=str(err), request=request,
content_type='text/plain')
+ encryption_context = self.crypto_driver.encryption_context(key_id)
if self.mount_check and not check_mount(self.devices, device):
return HTTPInsufficientStorage(drive=device, request=request)
if 'x-timestamp' not in request.headers or \
View
58 test/unit/common/test_key_manager.py
@@ -24,7 +24,8 @@
from sqlalchemy.types import String, Integer
from swift.common.key_manager.drivers.sql import SQLDriver
-from swift.common.key_manager.drivers.sql.driver import key_info_table
+from swift.common.key_manager.drivers.sql.driver import Key, Session,\
+ generate_key
meta_test = MetaData()
@@ -48,6 +49,7 @@ def setUp(self):
engine = create_engine(self.url)
meta_test.bind = engine
+ Session.configure(bind=engine)
table_template.create(engine, checkfirst=True)
def tearDown(self):
@@ -57,40 +59,6 @@ def tearDown(self):
"""
os.remove(self.db_path)
- def test_find_value(self):
- """
- Find row in table according serching pattern.
- """
- first_acc_info = ["test_account", "test_key_string"]
- second_acc_info = ["test_account2", "test_key_string2"]
-
- answer1 = [('test_account', 1L, 'test_key_string')]
- answer2 = [('test_account2', 2L, 'test_key_string2')]
-
- for data_acc in [first_acc_info, second_acc_info]:
- key_info_table.insert().execute(account=data_acc[0],
- encryption_key=data_acc[1])
-
- # check, that result empty , if such information not exist in DataBase
- self.assertFalse(self.key_driver.find_value("account", "my_account"))
-
- # check, results for currect find requests
- # First answer
- res = self.key_driver.find_value("account", first_acc_info[0])
- self.assertEquals(res, answer1)
- res = self.key_driver.find_value("encryption_key", first_acc_info[1])
- self.assertEquals(res, answer1)
- res = self.key_driver.find_value("key_id", "1")
- self.assertEquals(res, answer1)
-
- # Second answer
- res = self.key_driver.find_value("account", second_acc_info[0])
- self.assertEquals(res, answer2)
- res = self.key_driver.find_value("encryption_key", second_acc_info[1])
- self.assertEquals(res, answer2)
- res = self.key_driver.find_value("key_id", "2")
- self.assertEquals(res, answer2)
-
def test_get_key_id(self):
"""
Check key_id value for different account values.
@@ -111,8 +79,10 @@ def test_get_key(self):
"""
# create 2 account with key_id
acc_info = ["acc1", "acc2"]
+ session = Session()
for acc in acc_info:
- key_info_table.insert().execute(account=acc)
+ session.add(Key(acc, generate_key()))
+ session.commit()
# check key for first account
key1 = self.key_driver.get_key(1)
key2 = self.key_driver.get_key(1)
@@ -123,12 +93,7 @@ def test_get_key(self):
self.assertNotEqual(key2, key3)
# check raise, if incorrect id (no in table)
self.assertRaises(StandardError, self.key_driver.get_key, 100)
- # check raise, if incorrect id (string and have not only digits)
- self.assertRaises(ValueError, self.key_driver.get_key, "id100")
- # check raise, if incorrect id (not string or int)
- test = [[2], {"test": "test"}, (222, 2)]
- for val in test:
- self.assertRaises(TypeError, self.key_driver.get_key, val)
+ session.close()
@mock.patch('migrate.versioning.api.upgrade')
def test_sync_success(self, mock_upgrade):
@@ -149,6 +114,15 @@ def test_sync_failed(self, mock_upgrade, mock_version_control):
mock_upgrade.assert_has_calls(2 * [mock.call(self.url, mock.ANY)])
mock_version_control.assert_called_once_with(self.url, mock.ANY)
+ def test_invalid_key_id(self):
+ self.assertRaises(ValueError,
+ self.key_driver.validate_key_id, "n0tnumb3r")
+ self.assertRaises(ValueError,
+ self.key_driver.validate_key_id, "-13")
+
+ def test_valid_key_id(self):
+ self.assertEqual(None, self.key_driver.validate_key_id("42"))
+
class TestSQLDriverReconnection(unittest.TestCase):
def setUp(self):
Please sign in to comment.
Something went wrong with that request. Please try again.