Merge "sql: Squash newton migrations (part 2)"
This commit is contained in:
commit
af960f8c7f
|
@ -1,39 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
import sqlalchemy as sql
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
password = sql.Table('password', meta, autoload=True)
|
||||
# Because it's difficult to get a timestamp server default working among
|
||||
# all of the supported databases and versions, I'm choosing to drop and
|
||||
# then recreate the column as I think this is a more cleaner option. This
|
||||
# will only impact operators that have already deployed the 105 migration;
|
||||
# resetting the password created_at for security compliance features, if
|
||||
# enabled.
|
||||
password.c.created_at.drop()
|
||||
# sqlite doesn't support server_default=sql.func.now(), so skipping.
|
||||
if migrate_engine.name == 'sqlite':
|
||||
created_at = sql.Column('created_at', sql.TIMESTAMP, nullable=True)
|
||||
else:
|
||||
# Changing type to timestamp as mysql 5.5 and older doesn't support
|
||||
# datetime defaults.
|
||||
created_at = sql.Column('created_at', sql.TIMESTAMP, nullable=False,
|
||||
default=datetime.datetime.utcnow,
|
||||
server_default=sql.func.now())
|
||||
password.create_column(created_at)
|
|
@ -1,60 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone.common.sql import upgrades
|
||||
|
||||
import sqlalchemy as sql
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
credential_table = sql.Table('credential', meta, autoload=True)
|
||||
credential_table.c.blob.drop()
|
||||
|
||||
if upgrades.USE_TRIGGERS:
|
||||
if migrate_engine.name == 'postgresql':
|
||||
drop_credential_update_trigger = (
|
||||
'DROP TRIGGER credential_update_read_only on credential;'
|
||||
)
|
||||
drop_credential_insert_trigger = (
|
||||
'DROP TRIGGER credential_insert_read_only on credential;'
|
||||
)
|
||||
elif migrate_engine.name == 'mysql':
|
||||
drop_credential_update_trigger = (
|
||||
'DROP TRIGGER credential_update_read_only;'
|
||||
)
|
||||
drop_credential_insert_trigger = (
|
||||
'DROP TRIGGER credential_insert_read_only;'
|
||||
)
|
||||
else:
|
||||
# NOTE(lbragstad, henry-nash): Apparently sqlalchemy and sqlite
|
||||
# behave weird when using triggers, which is why we use the `IF
|
||||
# EXISTS` conditional here. I think what is happening is that the
|
||||
# credential_table.c.blob.drop() causes sqlalchemy to create a new
|
||||
# credential table - but it doesn't copy the triggers over, which
|
||||
# causes the DROP TRIGGER statement to fail without `IF EXISTS`
|
||||
# because the trigger doesn't exist in the new table(?!).
|
||||
drop_credential_update_trigger = (
|
||||
'DROP TRIGGER IF EXISTS credential_update_read_only;'
|
||||
)
|
||||
drop_credential_insert_trigger = (
|
||||
'DROP TRIGGER IF EXISTS credential_insert_read_only;'
|
||||
)
|
||||
migrate_engine.execute(drop_credential_update_trigger)
|
||||
migrate_engine.execute(drop_credential_insert_trigger)
|
||||
|
||||
# NOTE(lbragstad): We close these so that they are not nullable because
|
||||
# Newton code (and anything after) would always populate these values.
|
||||
credential_table.c.encrypted_blob.alter(nullable=False)
|
||||
credential_table.c.key_hash.alter(nullable=False)
|
|
@ -1,37 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
import sqlalchemy as sql
|
||||
import sqlalchemy.sql.expression as expression
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
password = sql.Table('password', meta, autoload=True)
|
||||
# reset created_at column
|
||||
password.c.created_at.drop()
|
||||
created_at = sql.Column('created_at', sql.DateTime(),
|
||||
nullable=True,
|
||||
default=datetime.datetime.utcnow)
|
||||
password.create_column(created_at)
|
||||
# update created_at value
|
||||
now = datetime.datetime.utcnow()
|
||||
values = {'created_at': now}
|
||||
stmt = password.update().where(
|
||||
password.c.created_at == expression.null()).values(values)
|
||||
stmt.execute()
|
||||
# set not nullable
|
||||
password.c.created_at.alter(nullable=False)
|
|
@ -1,15 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
pass
|
|
@ -1,39 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sqlalchemy as sql
|
||||
|
||||
from keystone.credential.providers import fernet as credential_fernet
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
session = sql.orm.sessionmaker(bind=migrate_engine)()
|
||||
|
||||
credential_table = sql.Table('credential', meta, autoload=True)
|
||||
credentials = list(credential_table.select().execute())
|
||||
|
||||
for credential in credentials:
|
||||
crypto, keys = credential_fernet.get_multi_fernet_keys()
|
||||
primary_key_hash = credential_fernet.primary_key_hash(keys)
|
||||
encrypted_blob = crypto.encrypt(credential['blob'].encode('utf-8'))
|
||||
values = {
|
||||
'encrypted_blob': encrypted_blob,
|
||||
'key_hash': primary_key_hash
|
||||
}
|
||||
update = credential_table.update().where(
|
||||
credential_table.c.id == credential.id
|
||||
).values(values)
|
||||
session.execute(update)
|
||||
session.commit()
|
||||
session.close()
|
|
@ -1,15 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
pass
|
|
@ -1,18 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# A null initial migration to open this repo. Do not re-use replace this with
|
||||
# a real migration, add additional ones in subsequent version scripts.
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
pass
|
|
@ -1,129 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sqlalchemy as sql
|
||||
|
||||
from keystone.common import sql as ks_sql
|
||||
from keystone.common.sql import upgrades
|
||||
|
||||
|
||||
# NOTE(lbragstad): MySQL error state of 45000 is a generic unhandled exception.
|
||||
# Keystone will return a 500 in this case.
|
||||
MYSQL_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER credential_insert_read_only BEFORE INSERT ON credential
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '%s';
|
||||
END;
|
||||
"""
|
||||
|
||||
MYSQL_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER credential_update_read_only BEFORE UPDATE ON credential
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.encrypted_blob IS NULL THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '%s';
|
||||
END IF;
|
||||
IF NEW.encrypted_blob IS NOT NULL AND OLD.blob IS NULL THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '%s';
|
||||
END IF;
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER credential_insert_read_only BEFORE INSERT ON credential
|
||||
BEGIN
|
||||
SELECT RAISE (ABORT, '%s');
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER credential_update_read_only BEFORE UPDATE ON credential
|
||||
WHEN NEW.encrypted_blob IS NULL
|
||||
BEGIN
|
||||
SELECT RAISE (ABORT, '%s');
|
||||
END;
|
||||
"""
|
||||
|
||||
POSTGRESQL_INSERT_TRIGGER = """
|
||||
CREATE OR REPLACE FUNCTION keystone_read_only_insert()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
RAISE EXCEPTION '%s';
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER credential_insert_read_only BEFORE INSERT ON credential
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE keystone_read_only_insert();
|
||||
"""
|
||||
|
||||
POSTGRESQL_UPDATE_TRIGGER = """
|
||||
CREATE OR REPLACE FUNCTION keystone_read_only_update()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
IF NEW.encrypted_blob IS NULL THEN
|
||||
RAISE EXCEPTION '%s';
|
||||
END IF;
|
||||
IF NEW.encrypted_blob IS NOT NULL AND OLD.blob IS NULL THEN
|
||||
RAISE EXCEPTION '%s';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER credential_update_read_only BEFORE UPDATE ON credential
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE keystone_read_only_update();
|
||||
"""
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
key_hash = sql.Column('key_hash', sql.String(64), nullable=True)
|
||||
encrypted_blob = sql.Column(
|
||||
'encrypted_blob',
|
||||
ks_sql.Text,
|
||||
nullable=True
|
||||
)
|
||||
credential_table = sql.Table('credential', meta, autoload=True)
|
||||
credential_table.create_column(key_hash)
|
||||
credential_table.create_column(encrypted_blob)
|
||||
credential_table.c.blob.alter(nullable=True)
|
||||
|
||||
if not upgrades.USE_TRIGGERS:
|
||||
# Skip managing triggers if we're doing an offline upgrade.
|
||||
return
|
||||
|
||||
error_message = ('Credential migration in progress. Cannot perform '
|
||||
'writes to credential table.')
|
||||
if migrate_engine.name == 'postgresql':
|
||||
credential_insert_trigger = POSTGRESQL_INSERT_TRIGGER % error_message
|
||||
credential_update_trigger = POSTGRESQL_UPDATE_TRIGGER % (
|
||||
error_message, error_message
|
||||
)
|
||||
elif migrate_engine.name == 'sqlite':
|
||||
credential_insert_trigger = SQLITE_INSERT_TRIGGER % error_message
|
||||
credential_update_trigger = SQLITE_UPDATE_TRIGGER % error_message
|
||||
else:
|
||||
credential_insert_trigger = MYSQL_INSERT_TRIGGER % error_message
|
||||
credential_update_trigger = MYSQL_UPDATE_TRIGGER % (
|
||||
error_message, error_message
|
||||
)
|
||||
|
||||
migrate_engine.execute(credential_insert_trigger)
|
||||
migrate_engine.execute(credential_update_trigger)
|
|
@ -10,6 +10,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import textwrap
|
||||
|
||||
import migrate
|
||||
from oslo_log import log
|
||||
|
@ -83,9 +85,14 @@ def upgrade(migrate_engine):
|
|||
sql.Column('id', sql.String(length=64), primary_key=True),
|
||||
sql.Column('user_id', sql.String(length=64), nullable=False),
|
||||
sql.Column('project_id', sql.String(length=64)),
|
||||
sql.Column('blob', ks_sql.JsonBlob, nullable=False),
|
||||
sql.Column('type', sql.String(length=255), nullable=False),
|
||||
sql.Column('extra', ks_sql.JsonBlob.impl),
|
||||
sql.Column('key_hash', sql.String(64), nullable=False),
|
||||
sql.Column(
|
||||
'encrypted_blob',
|
||||
ks_sql.Text,
|
||||
nullable=False,
|
||||
),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8',
|
||||
)
|
||||
|
@ -249,7 +256,6 @@ def upgrade(migrate_engine):
|
|||
nullable=False,
|
||||
),
|
||||
sql.Column('password', sql.String(128), nullable=True),
|
||||
sql.Column('created_at', sql.DateTime(), nullable=True),
|
||||
sql.Column('expires_at', sql.DateTime(), nullable=True),
|
||||
sql.Column(
|
||||
'self_service',
|
||||
|
@ -258,6 +264,12 @@ def upgrade(migrate_engine):
|
|||
server_default='0',
|
||||
default=False,
|
||||
),
|
||||
sql.Column(
|
||||
'created_at',
|
||||
sql.DateTime(),
|
||||
nullable=False,
|
||||
default=datetime.datetime.utcnow,
|
||||
),
|
||||
)
|
||||
|
||||
policy = sql.Table(
|
||||
|
@ -747,3 +759,27 @@ def upgrade(migrate_engine):
|
|||
name=fkey.get('name'),
|
||||
ondelete=fkey.get('ondelete'),
|
||||
).create()
|
||||
|
||||
# TODO(stephenfin): Remove these procedures in a future contract migration
|
||||
|
||||
if migrate_engine.name == 'postgresql':
|
||||
error_message = (
|
||||
'Credential migration in progress. Cannot perform '
|
||||
'writes to credential table.'
|
||||
)
|
||||
credential_update_trigger = textwrap.dedent(f"""
|
||||
CREATE OR REPLACE FUNCTION keystone_read_only_update()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
IF NEW.encrypted_blob IS NULL THEN
|
||||
RAISE EXCEPTION '{error_message}';
|
||||
END IF;
|
||||
IF NEW.encrypted_blob IS NOT NULL AND OLD.blob IS NULL THEN
|
||||
RAISE EXCEPTION '{error_message}';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
""")
|
||||
migrate_engine.execute(credential_update_trigger)
|
|
@ -1,15 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
pass
|
|
@ -29,7 +29,7 @@ from keystone.i18n import _
|
|||
|
||||
USE_TRIGGERS = True
|
||||
|
||||
INITIAL_VERSION = 0
|
||||
INITIAL_VERSION = 3
|
||||
EXPAND_REPO = 'expand_repo'
|
||||
DATA_MIGRATION_REPO = 'data_migration_repo'
|
||||
CONTRACT_REPO = 'contract_repo'
|
||||
|
|
|
@ -77,7 +77,8 @@ INITIAL_TABLE_STRUCTURE = {
|
|||
'type', 'domain_id',
|
||||
],
|
||||
'credential': [
|
||||
'id', 'user_id', 'project_id', 'blob', 'type', 'extra',
|
||||
'id', 'user_id', 'project_id', 'type', 'extra', 'key_hash',
|
||||
'encrypted_blob',
|
||||
],
|
||||
'endpoint': [
|
||||
'id', 'legacy_endpoint_id', 'interface', 'region_id', 'service_id',
|
||||
|
@ -602,151 +603,6 @@ class FullMigration(MigrateBase, unit.TestCase):
|
|||
upgrades.INITIAL_VERSION + 2,
|
||||
)
|
||||
|
||||
def test_migration_002_password_created_at_not_nullable(self):
|
||||
# upgrade each repository to 001
|
||||
self.expand(1)
|
||||
self.migrate(1)
|
||||
self.contract(1)
|
||||
|
||||
password = sqlalchemy.Table('password', self.metadata, autoload=True)
|
||||
self.assertTrue(password.c.created_at.nullable)
|
||||
# upgrade each repository to 002
|
||||
self.expand(2)
|
||||
self.migrate(2)
|
||||
self.contract(2)
|
||||
password = sqlalchemy.Table('password', self.metadata, autoload=True)
|
||||
if self.engine.name != 'sqlite':
|
||||
self.assertFalse(password.c.created_at.nullable)
|
||||
|
||||
def test_migration_003_migrate_unencrypted_credentials(self):
|
||||
self.useFixture(
|
||||
ksfixtures.KeyRepository(
|
||||
self.config_fixture,
|
||||
'credential',
|
||||
credential_fernet.MAX_ACTIVE_KEYS
|
||||
)
|
||||
)
|
||||
|
||||
session = self.sessionmaker()
|
||||
credential_table_name = 'credential'
|
||||
|
||||
# upgrade each repository to 002
|
||||
self.expand(2)
|
||||
self.migrate(2)
|
||||
self.contract(2)
|
||||
|
||||
# populate the credential table with some sample credentials
|
||||
credentials = list()
|
||||
for i in range(5):
|
||||
credential = {'id': uuid.uuid4().hex,
|
||||
'blob': uuid.uuid4().hex,
|
||||
'user_id': uuid.uuid4().hex,
|
||||
'type': 'cert'}
|
||||
credentials.append(credential)
|
||||
self.insert_dict(session, credential_table_name, credential)
|
||||
|
||||
# verify the current schema
|
||||
self.assertTableColumns(
|
||||
credential_table_name,
|
||||
['id', 'user_id', 'project_id', 'type', 'blob', 'extra']
|
||||
)
|
||||
|
||||
# upgrade expand repo to 003 to add new columns
|
||||
self.expand(3)
|
||||
|
||||
# verify encrypted_blob and key_hash columns have been added and verify
|
||||
# the original blob column is still there
|
||||
self.assertTableColumns(
|
||||
credential_table_name,
|
||||
['id', 'user_id', 'project_id', 'type', 'blob', 'extra',
|
||||
'key_hash', 'encrypted_blob']
|
||||
)
|
||||
|
||||
# verify triggers by making sure we can't write to the credential table
|
||||
credential = {'id': uuid.uuid4().hex,
|
||||
'blob': uuid.uuid4().hex,
|
||||
'user_id': uuid.uuid4().hex,
|
||||
'type': 'cert'}
|
||||
self.assertRaises(db_exception.DBError,
|
||||
self.insert_dict,
|
||||
session,
|
||||
credential_table_name,
|
||||
credential)
|
||||
|
||||
# upgrade migrate repo to 003 to migrate existing credentials
|
||||
self.migrate(3)
|
||||
|
||||
# make sure we've actually updated the credential with the
|
||||
# encrypted blob and the corresponding key hash
|
||||
credential_table = sqlalchemy.Table(
|
||||
credential_table_name,
|
||||
self.metadata,
|
||||
autoload=True
|
||||
)
|
||||
for credential in credentials:
|
||||
filter = credential_table.c.id == credential['id']
|
||||
cols = [credential_table.c.key_hash, credential_table.c.blob,
|
||||
credential_table.c.encrypted_blob]
|
||||
q = sqlalchemy.select(cols).where(filter)
|
||||
result = session.execute(q).fetchone()
|
||||
|
||||
self.assertIsNotNone(result.encrypted_blob)
|
||||
self.assertIsNotNone(result.key_hash)
|
||||
# verify the original blob column is still populated
|
||||
self.assertEqual(result.blob, credential['blob'])
|
||||
|
||||
# verify we can't make any writes to the credential table
|
||||
credential = {'id': uuid.uuid4().hex,
|
||||
'blob': uuid.uuid4().hex,
|
||||
'user_id': uuid.uuid4().hex,
|
||||
'key_hash': uuid.uuid4().hex,
|
||||
'type': 'cert'}
|
||||
self.assertRaises(db_exception.DBError,
|
||||
self.insert_dict,
|
||||
session,
|
||||
credential_table_name,
|
||||
credential)
|
||||
|
||||
# upgrade contract repo to 003 to remove triggers and blob column
|
||||
self.contract(3)
|
||||
|
||||
# verify the new schema doesn't have a blob column anymore
|
||||
self.assertTableColumns(
|
||||
credential_table_name,
|
||||
['id', 'user_id', 'project_id', 'type', 'extra', 'key_hash',
|
||||
'encrypted_blob']
|
||||
)
|
||||
|
||||
# verify that the triggers are gone by writing to the database
|
||||
credential = {'id': uuid.uuid4().hex,
|
||||
'encrypted_blob': uuid.uuid4().hex,
|
||||
'key_hash': uuid.uuid4().hex,
|
||||
'user_id': uuid.uuid4().hex,
|
||||
'type': 'cert'}
|
||||
self.insert_dict(session, credential_table_name, credential)
|
||||
|
||||
def test_migration_004_reset_password_created_at(self):
|
||||
# upgrade each repository to 003 and test
|
||||
self.expand(3)
|
||||
self.migrate(3)
|
||||
self.contract(3)
|
||||
password = sqlalchemy.Table('password', self.metadata, autoload=True)
|
||||
# postgresql returns 'TIMESTAMP WITHOUT TIME ZONE'
|
||||
self.assertTrue(
|
||||
str(password.c.created_at.type).startswith('TIMESTAMP'))
|
||||
# upgrade each repository to 004 and test
|
||||
self.expand(4)
|
||||
self.migrate(4)
|
||||
self.contract(4)
|
||||
password = sqlalchemy.Table('password', self.metadata, autoload=True)
|
||||
# type would still be TIMESTAMP with postgresql
|
||||
if self.engine.name == 'postgresql':
|
||||
self.assertTrue(
|
||||
str(password.c.created_at.type).startswith('TIMESTAMP'))
|
||||
else:
|
||||
self.assertEqual('DATETIME', str(password.c.created_at.type))
|
||||
self.assertFalse(password.c.created_at.nullable)
|
||||
|
||||
def test_migration_010_add_revocation_event_indexes(self):
|
||||
self.expand(9)
|
||||
self.migrate(9)
|
||||
|
@ -2325,10 +2181,6 @@ class FullMigration(MigrateBase, unit.TestCase):
|
|||
class MySQLOpportunisticFullMigration(FullMigration):
|
||||
FIXTURE = db_fixtures.MySQLOpportunisticFixture
|
||||
|
||||
def test_migration_003_migrate_unencrypted_credentials(self):
|
||||
self.skip_test_overrides('skipped to update u-c for PyMySql version'
|
||||
'to 0.10.0')
|
||||
|
||||
def test_migration_012_add_domain_id_to_idp(self):
|
||||
self.skip_test_overrides('skipped to update u-c for PyMySql version'
|
||||
'to 0.10.0')
|
||||
|
|
Loading…
Reference in New Issue