diff --git a/keystone/common/sql/migrate_repo/versions/095_add_integer_pkey_to_revocation_event_table.py b/keystone/common/sql/migrate_repo/versions/095_add_integer_pkey_to_revocation_event_table.py new file mode 100644 index 0000000000..7a75f7b193 --- /dev/null +++ b/keystone/common/sql/migrate_repo/versions/095_add_integer_pkey_to_revocation_event_table.py @@ -0,0 +1,62 @@ +# 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 + + # You can specify primary keys when creating tables, however adding + # auto-increment integer primary keys for existing tables is not + # cross-engine compatibility supported. Thus, the approach is to: + # (1) create a new revocation_event table with an int pkey, + # (2) migrate data from the old table to the new table, + # (3) delete the old revocation_event table + # (4) rename the new revocation_event table + revocation_table = sql.Table('revocation_event', meta, autoload=True) + + revocation_table_new = sql.Table( + 'revocation_event_new', + meta, + sql.Column('id', sql.Integer, primary_key=True), + sql.Column('domain_id', sql.String(64)), + sql.Column('project_id', sql.String(64)), + sql.Column('user_id', sql.String(64)), + sql.Column('role_id', sql.String(64)), + sql.Column('trust_id', sql.String(64)), + sql.Column('consumer_id', sql.String(64)), + sql.Column('access_token_id', sql.String(64)), + sql.Column('issued_before', sql.DateTime(), nullable=False), + sql.Column('expires_at', sql.DateTime()), + sql.Column('revoked_at', sql.DateTime(), index=True, nullable=False), + sql.Column('audit_id', sql.String(32), nullable=True), + sql.Column('audit_chain_id', sql.String(32), nullable=True)) + revocation_table_new.create(migrate_engine, checkfirst=True) + + revocation_table_new.insert().from_select(['domain_id', + 'project_id', + 'user_id', + 'role_id', + 'trust_id', + 'consumer_id', + 'access_token_id', + 'issued_before', + 'expires_at', + 'revoked_at', + 'audit_id', + 'audit_chain_id'], + revocation_table.select()) + + revocation_table.drop() + revocation_table_new.rename('revocation_event') diff --git a/keystone/revoke/backends/sql.py b/keystone/revoke/backends/sql.py index fa62e34c03..9f8a82db02 100644 --- a/keystone/revoke/backends/sql.py +++ b/keystone/revoke/backends/sql.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import uuid - from keystone.common import sql from keystone.models import revoke_model from keystone import revoke @@ -23,7 +21,7 @@ class RevocationEvent(sql.ModelBase, sql.ModelDictMixin): # The id field is not going to be exposed to the outside world. # It is, however, necessary for SQLAlchemy. - id = sql.Column(sql.String(64), primary_key=True) + id = sql.Column(sql.Integer, primary_key=True, nullable=False) domain_id = sql.Column(sql.String(64)) project_id = sql.Column(sql.String(64)) user_id = sql.Column(sql.String(64)) @@ -96,7 +94,6 @@ class Revoke(revoke.RevokeDriverV8): kwargs = dict() for attr in revoke_model.REVOKE_KEYS: kwargs[attr] = getattr(event, attr) - kwargs['id'] = uuid.uuid4().hex record = RevocationEvent(**kwargs) with sql.session_for_write() as session: session.add(record) diff --git a/keystone/tests/unit/test_backend_sql.py b/keystone/tests/unit/test_backend_sql.py index 2f0dc74aac..2e703fffed 100644 --- a/keystone/tests/unit/test_backend_sql.py +++ b/keystone/tests/unit/test_backend_sql.py @@ -196,6 +196,22 @@ class SqlModels(SqlTests): ('user_id', sql.String, 64)) self.assertExpectedSchema('user_group_membership', cols) + def test_revocation_event_model(self): + cols = (('id', sql.Integer, None), + ('domain_id', sql.String, 64), + ('project_id', sql.String, 64), + ('user_id', sql.String, 64), + ('role_id', sql.String, 64), + ('trust_id', sql.String, 64), + ('consumer_id', sql.String, 64), + ('access_token_id', sql.String, 64), + ('issued_before', sql.DateTime, None), + ('expires_at', sql.DateTime, None), + ('revoked_at', sql.DateTime, None), + ('audit_id', sql.String, 32), + ('audit_chain_id', sql.String, 32)) + self.assertExpectedSchema('revocation_event', cols) + class SqlIdentity(SqlTests, identity_tests.IdentityTests, assignment_tests.AssignmentTests, diff --git a/keystone/tests/unit/test_sql_upgrade.py b/keystone/tests/unit/test_sql_upgrade.py index 0f524d5cf0..207122ef64 100644 --- a/keystone/tests/unit/test_sql_upgrade.py +++ b/keystone/tests/unit/test_sql_upgrade.py @@ -1043,6 +1043,22 @@ class SqlUpgradeTests(SqlMigrateBase): 'unique_id', 'display_name']) + def test_add_int_pkey_to_revocation_event_table(self): + meta = sqlalchemy.MetaData() + meta.bind = self.engine + REVOCATION_EVENT_TABLE_NAME = 'revocation_event' + self.upgrade(94) + revocation_event_table = sqlalchemy.Table(REVOCATION_EVENT_TABLE_NAME, + meta, autoload=True) + # assert id column is a string (before) + self.assertEqual('VARCHAR(64)', str(revocation_event_table.c.id.type)) + self.upgrade(95) + meta.clear() + revocation_event_table = sqlalchemy.Table(REVOCATION_EVENT_TABLE_NAME, + meta, autoload=True) + # assert id column is an integer (after) + self.assertEqual('INTEGER', str(revocation_event_table.c.id.type)) + class VersionTests(SqlMigrateBase):