Migrate all password hashes to the new location if needed

Migrate password.password to password.password_hash if
password.password_hash is not populated.

This stages keystone to eliminate the no longer required "password"
column in favor of the "password_hash" column in the Stein release.

Change-Id: I96cd75808e9915e614ea2289d7dd56c3d1873d50
This commit is contained in:
Morgan Fainberg 2018-06-19 15:26:53 -07:00
parent 7636755307
commit 6663ea9a47
4 changed files with 116 additions and 0 deletions

View File

@ -0,0 +1,15 @@
# 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

View File

@ -0,0 +1,26 @@
# 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
import sqlalchemy.sql.expression as expression
def upgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
password_table = sql.Table('password', meta, autoload=True)
with migrate_engine.begin() as conn:
stmt = password_table.update().where(
password_table.c.password_hash == expression.null()).values(
{'password_hash': password_table.c.password})
conn.execute(stmt)

View File

@ -0,0 +1,15 @@
# 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

View File

@ -3015,6 +3015,66 @@ class FullMigration(SqlMigrateBase, unit.TestCase):
}
limit_table.insert().values(limit).execute()
def test_migration_046_copies_data_from_password_to_password_hash(self):
self.expand(46)
self.migrate(45)
self.contract(45)
# Create User and Local User
project_table = sqlalchemy.Table('project', self.metadata,
autoload=True)
domain_data = {'id': '_domain', 'domain_id': '_domain',
'enabled': True, 'name': '_domain', 'is_domain': True}
project_table.insert().values(domain_data).execute()
user_table = sqlalchemy.Table('user', self.metadata, autoload=True)
user_id = uuid.uuid4().hex
user = {'id': user_id, 'enabled': True, 'domain_id': domain_data['id']}
user_table.insert().values(user).execute()
local_user_table = sqlalchemy.Table('local_user', self.metadata,
autoload=True)
local_user = {
'id': 1, 'user_id': user_id, 'domain_id': user['domain_id'],
'name': 'name'}
local_user_table.insert().values(local_user).execute()
password_table = sqlalchemy.Table('password',
self.metadata, autoload=True)
password_data = {
'local_user_id': local_user['id'],
'created_at': datetime.datetime.utcnow(),
'expires_at': datetime.datetime.utcnow(),
'password': uuid.uuid4().hex}
password_data1 = {
'local_user_id': local_user['id'],
'created_at': datetime.datetime.utcnow(),
'expires_at': datetime.datetime.utcnow(),
'password_hash': uuid.uuid4().hex}
password_data2 = {
'local_user_id': local_user['id'],
'created_at': datetime.datetime.utcnow(),
'expires_at': datetime.datetime.utcnow(),
'password': uuid.uuid4().hex,
'password_hash': uuid.uuid4().hex}
password_table.insert().values(password_data).execute()
password_table.insert().values(password_data1).execute()
password_table.insert().values(password_data2).execute()
self.migrate(46)
passwords = list(password_table.select().execute())
for p in passwords:
if p.password == password_data['password']:
self.assertEqual(p.password_hash, p.password)
self.assertIsNotNone(p.password)
self.assertIsNotNone(p.password_hash)
elif p.password_hash == password_data1['password_hash']:
self.assertIsNone(p.password)
self.assertIsNotNone(p.password_hash)
elif p.password_hash == password_data2['password_hash']:
self.assertIsNotNone(p.password)
self.assertIsNotNone(p.password_hash)
self.assertNotEqual(p.password, p.password_hash)
else:
raise ValueError('Too Many Passwords Found')
class MySQLOpportunisticFullMigration(FullMigration):
FIXTURE = db_fixtures.MySQLOpportunisticFixture