diff --git a/api-ref/source/v3-ext/trust.inc b/api-ref/source/v3-ext/trust.inc index 1a296c0784..c840b664b2 100644 --- a/api-ref/source/v3-ext/trust.inc +++ b/api-ref/source/v3-ext/trust.inc @@ -116,7 +116,6 @@ Parameters - allow_redelegation: allow_redelegation - expires_at: trust_expires_at - project_id: trust_project_id - - redelegated_trust_id: redelegated_trust_id - redelegation_count: redelegation_count - remaining_uses: remaining_uses - roles: trust_roles diff --git a/keystone/common/sql/contract_repo/versions/062_contract_extract_redelegation_data_from_extras.py b/keystone/common/sql/contract_repo/versions/062_contract_extract_redelegation_data_from_extras.py new file mode 100644 index 0000000000..9cb40b4541 --- /dev/null +++ b/keystone/common/sql/contract_repo/versions/062_contract_extract_redelegation_data_from_extras.py @@ -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 diff --git a/keystone/common/sql/data_migration_repo/versions/062_migrate_extract_redelegation_data_from_extras.py b/keystone/common/sql/data_migration_repo/versions/062_migrate_extract_redelegation_data_from_extras.py new file mode 100644 index 0000000000..ddb30368d2 --- /dev/null +++ b/keystone/common/sql/data_migration_repo/versions/062_migrate_extract_redelegation_data_from_extras.py @@ -0,0 +1,43 @@ +# 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 oslo_serialization import jsonutils +import sqlalchemy as sql + + +def upgrade(migrate_engine): + meta = sql.MetaData() + meta.bind = migrate_engine + + trust_table = sql.Table('trust', meta, autoload=True) + trust_list = list(trust_table.select().execute()) + + # Loop through all the trusts and move the redelegeated trust id out of + # extras. + for trust in trust_list: + if trust.extra is not None: + extra_dict = jsonutils.loads(trust.extra) + else: + extra_dict = {} + + new_values = {} + + new_values['redelegated_trust_id'] = extra_dict.pop( + 'redelegated_trust_id', None) + new_values['redelegation_count'] = extra_dict.pop( + 'redelegation_count', None) + + new_values['extra'] = jsonutils.dumps(extra_dict) + + clause = trust_table.c.id == trust.id + update = trust_table.update().where(clause).values(new_values) + migrate_engine.execute(update) diff --git a/keystone/common/sql/expand_repo/versions/062_expand_extract_redelegation_data_from_extras.py b/keystone/common/sql/expand_repo/versions/062_expand_extract_redelegation_data_from_extras.py new file mode 100644 index 0000000000..7e3019eb55 --- /dev/null +++ b/keystone/common/sql/expand_repo/versions/062_expand_extract_redelegation_data_from_extras.py @@ -0,0 +1,31 @@ +# 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 + + +def upgrade(migrate_engine): + meta = sql.MetaData() + meta.bind = migrate_engine + + trust_table = sql.Table('trust', meta, autoload=True) + trust_id_column = sql.Column( + 'redelegated_trust_id', + sql.String(64), + nullable=True) + count_column = sql.Column( + 'redelegation_count', + sql.Integer, + nullable=True) + + trust_table.create_column(trust_id_column) + trust_table.create_column(count_column) diff --git a/keystone/tests/unit/test_sql_upgrade.py b/keystone/tests/unit/test_sql_upgrade.py index 64155c7e98..6d0c531211 100644 --- a/keystone/tests/unit/test_sql_upgrade.py +++ b/keystone/tests/unit/test_sql_upgrade.py @@ -52,6 +52,7 @@ from oslo_db import exception as db_exception from oslo_db.sqlalchemy import enginefacade from oslo_db.sqlalchemy import test_fixtures as db_fixtures from oslo_log import log +from oslo_serialization import jsonutils from oslotest import base as test_base import pytz from sqlalchemy.engine import reflection @@ -3283,6 +3284,85 @@ class FullMigration(SqlMigrateBase, unit.TestCase): app_cred_access_rule_table.insert().values( app_cred_access_rule_rel).execute() + def test_migration_062_add_trust_redelegation(self): + # ensure initial schema + self.expand(61) + self.migrate(61) + self.contract(61) + self.assertTableColumns('trust', ['id', + 'trustor_user_id', + 'trustee_user_id', + 'project_id', + 'impersonation', + 'expires_at', + 'expires_at_int', + 'remaining_uses', + 'deleted_at', + 'extra']) + + # fixture + trust = { + 'id': uuid.uuid4().hex, + 'trustor_user_id': uuid.uuid4().hex, + 'trustee_user_id': uuid.uuid4().hex, + 'project_id': uuid.uuid4().hex, + 'impersonation': True, + 'expires_at': datetime.datetime.now(), + 'remaining_uses': 10, + 'deleted_at': datetime.datetime.now(), + 'redelegated_trust_id': uuid.uuid4().hex, + 'redelegation_count': 3, + 'other': uuid.uuid4().hex + } + old_trust = trust.copy() + old_extra = { + 'redelegated_trust_id': old_trust.pop('redelegated_trust_id'), + 'redelegation_count': old_trust.pop('redelegation_count'), + 'other': old_trust.pop('other') + } + old_trust['extra'] = jsonutils.dumps(old_extra) + # load fixture + session = self.sessionmaker() + self.insert_dict(session, 'trust', old_trust) + + # ensure redelegation data is in extra + stored_trust = list(self.select_table('trust').execute())[0] + self.assertDictEqual({ + 'redelegated_trust_id': trust['redelegated_trust_id'], + 'redelegation_count': trust['redelegation_count'], + 'other': trust['other']}, + jsonutils.loads(stored_trust.extra)) + + # upgrade and ensure expected schema + self.expand(62) + self.migrate(62) + self.contract(62) + self.assertTableColumns('trust', ['id', + 'trustor_user_id', + 'trustee_user_id', + 'project_id', + 'impersonation', + 'expires_at', + 'expires_at_int', + 'remaining_uses', + 'deleted_at', + 'redelegated_trust_id', + 'redelegation_count', + 'extra']) + + trust_table = sqlalchemy.Table('trust', self.metadata, autoload=True) + self.assertTrue(trust_table.c.redelegated_trust_id.nullable) + self.assertTrue(trust_table.c.redelegation_count.nullable) + + # test target data layout + upgraded_trust = list(self.select_table('trust').execute())[0] + self.assertDictEqual({'other': trust['other']}, + jsonutils.loads(upgraded_trust.extra)) + self.assertEqual(trust['redelegated_trust_id'], + upgraded_trust.redelegated_trust_id) + self.assertEqual(trust['redelegation_count'], + upgraded_trust.redelegation_count) + class MySQLOpportunisticFullMigration(FullMigration): FIXTURE = db_fixtures.MySQLOpportunisticFixture diff --git a/keystone/trust/backends/sql.py b/keystone/trust/backends/sql.py index 76f2c32d68..0dcd3d1b2e 100644 --- a/keystone/trust/backends/sql.py +++ b/keystone/trust/backends/sql.py @@ -32,7 +32,8 @@ class TrustModel(sql.ModelBase, sql.ModelDictMixinWithExtras): __tablename__ = 'trust' attributes = ['id', 'trustor_user_id', 'trustee_user_id', 'project_id', 'impersonation', 'expires_at', - 'remaining_uses', 'deleted_at'] + 'remaining_uses', 'deleted_at', 'redelegated_trust_id', + 'redelegation_count'] id = sql.Column(sql.String(64), primary_key=True) # user id of owner trustor_user_id = sql.Column(sql.String(64), nullable=False,) @@ -44,6 +45,8 @@ class TrustModel(sql.ModelBase, sql.ModelDictMixinWithExtras): _expires_at = sql.Column('expires_at', sql.DateTime) expires_at_int = sql.Column(sql.DateTimeInt(), nullable=True) remaining_uses = sql.Column(sql.Integer, nullable=True) + redelegated_trust_id = sql.Column(sql.String(64), nullable=True) + redelegation_count = sql.Column(sql.Integer, nullable=True) extra = sql.Column(sql.JsonBlob()) __table_args__ = (sql.UniqueConstraint( 'trustor_user_id', 'trustee_user_id', 'project_id',