sql: Squash ocata migrations
Make the following changes to the new "initial" migrations. - Add four additional indexes to 'revocation_event' table (010) - Add unique constraint to 'user_id' column of 'nonlocal_user' table (011) - Add 'domain_id' column to 'identity_provider' table, and a foreign key constraint on the 'id' column of the 'project' table (012) - Add 'ondelete=CASCADE' to foreignkey constraint on 'protocol_id' and 'idp_id' columns of 'federated_user' table (013) - Add 'domain_id' column to the 'user' table (014) - Replace foreign key constraints covering 'user_id' columns of 'local_user' and 'nonlocal_user' tables with constraints covering 'user_id' and 'domain_id' columns (014) - Add unique constraint covering 'id' and 'domain_id' columns of 'user' table (014) - Alter 'domain_id' column of 'user' table to be non-nullable (015) - Add 'user_option' table (016) Change-Id: I96cab42cfcfd3e86b53f25abf4cf4043af3b5667 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
pass
|
||||
@@ -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,23 +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 migrate
|
||||
import sqlalchemy as sql
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
nonlocal_user = sql.Table('nonlocal_user', meta, autoload=True)
|
||||
migrate.UniqueConstraint(nonlocal_user.c.user_id,
|
||||
name='ixu_nonlocal_user_user_id').create()
|
||||
@@ -1,38 +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.sql import upgrades
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
idp_table = sql.Table('identity_provider', meta, autoload=True)
|
||||
idp_table.c.domain_id.alter(nullable=False, unique=True)
|
||||
|
||||
if upgrades.USE_TRIGGERS:
|
||||
if migrate_engine.name == 'postgresql':
|
||||
drop_idp_insert_trigger = (
|
||||
'DROP TRIGGER idp_insert_read_only on identity_provider;'
|
||||
)
|
||||
elif migrate_engine.name == 'mysql':
|
||||
drop_idp_insert_trigger = (
|
||||
'DROP TRIGGER idp_insert_read_only;'
|
||||
)
|
||||
else:
|
||||
drop_idp_insert_trigger = (
|
||||
'DROP TRIGGER IF EXISTS idp_insert_read_only;'
|
||||
)
|
||||
migrate_engine.execute(drop_idp_insert_trigger)
|
||||
@@ -1,31 +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 migrate
|
||||
import sqlalchemy as sql
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
federated_table = sql.Table('federated_user', meta, autoload=True)
|
||||
protocol_table = sql.Table('federation_protocol', meta, autoload=True)
|
||||
|
||||
migrate.ForeignKeyConstraint(
|
||||
columns=[federated_table.c.protocol_id, federated_table.c.idp_id],
|
||||
refcolumns=[protocol_table.c.id, protocol_table.c.idp_id]).drop()
|
||||
|
||||
migrate.ForeignKeyConstraint(
|
||||
columns=[federated_table.c.protocol_id, federated_table.c.idp_id],
|
||||
refcolumns=[protocol_table.c.id, protocol_table.c.idp_id],
|
||||
ondelete='CASCADE').create()
|
||||
@@ -1,94 +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 migrate
|
||||
import sqlalchemy as sql
|
||||
|
||||
from keystone.common.sql import upgrades
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
inspector = sql.inspect(migrate_engine)
|
||||
|
||||
user = sql.Table('user', meta, autoload=True)
|
||||
local_user = sql.Table('local_user', meta, autoload=True)
|
||||
nonlocal_user = sql.Table('nonlocal_user', meta, autoload=True)
|
||||
|
||||
# drop previous fk constraints
|
||||
fk_name = _get_fk_name(inspector, 'local_user', 'user_id')
|
||||
if fk_name:
|
||||
migrate.ForeignKeyConstraint(columns=[local_user.c.user_id],
|
||||
refcolumns=[user.c.id],
|
||||
name=fk_name).drop()
|
||||
|
||||
fk_name = _get_fk_name(inspector, 'nonlocal_user', 'user_id')
|
||||
if fk_name:
|
||||
migrate.ForeignKeyConstraint(columns=[nonlocal_user.c.user_id],
|
||||
refcolumns=[user.c.id],
|
||||
name=fk_name).drop()
|
||||
|
||||
# create user unique constraint needed for the new composite fk constraint
|
||||
migrate.UniqueConstraint(user.c.id, user.c.domain_id,
|
||||
name='ixu_user_id_domain_id').create()
|
||||
# create new composite fk constraints
|
||||
migrate.ForeignKeyConstraint(
|
||||
columns=[local_user.c.user_id, local_user.c.domain_id],
|
||||
refcolumns=[user.c.id, user.c.domain_id],
|
||||
onupdate='CASCADE', ondelete='CASCADE').create()
|
||||
migrate.ForeignKeyConstraint(
|
||||
columns=[nonlocal_user.c.user_id, nonlocal_user.c.domain_id],
|
||||
refcolumns=[user.c.id, user.c.domain_id],
|
||||
onupdate='CASCADE', ondelete='CASCADE').create()
|
||||
|
||||
# drop triggers
|
||||
if upgrades.USE_TRIGGERS:
|
||||
if migrate_engine.name == 'postgresql':
|
||||
drop_local_user_insert_trigger = (
|
||||
'DROP TRIGGER local_user_after_insert_trigger on local_user;')
|
||||
drop_local_user_update_trigger = (
|
||||
'DROP TRIGGER local_user_after_update_trigger on local_user;')
|
||||
drop_nonlocal_user_insert_trigger = (
|
||||
'DROP TRIGGER nonlocal_user_after_insert_trigger '
|
||||
'on nonlocal_user;')
|
||||
drop_nonlocal_user_update_trigger = (
|
||||
'DROP TRIGGER nonlocal_user_after_update_trigger '
|
||||
'on nonlocal_user;')
|
||||
elif migrate_engine.name == 'mysql':
|
||||
drop_local_user_insert_trigger = (
|
||||
'DROP TRIGGER local_user_after_insert_trigger;')
|
||||
drop_local_user_update_trigger = (
|
||||
'DROP TRIGGER local_user_after_update_trigger;')
|
||||
drop_nonlocal_user_insert_trigger = (
|
||||
'DROP TRIGGER nonlocal_user_after_insert_trigger;')
|
||||
drop_nonlocal_user_update_trigger = (
|
||||
'DROP TRIGGER nonlocal_user_after_update_trigger;')
|
||||
else:
|
||||
drop_local_user_insert_trigger = (
|
||||
'DROP TRIGGER IF EXISTS local_user_after_insert_trigger;')
|
||||
drop_local_user_update_trigger = (
|
||||
'DROP TRIGGER IF EXISTS local_user_after_update_trigger;')
|
||||
drop_nonlocal_user_insert_trigger = (
|
||||
'DROP TRIGGER IF EXISTS nonlocal_user_after_insert_trigger;')
|
||||
drop_nonlocal_user_update_trigger = (
|
||||
'DROP TRIGGER IF EXISTS nonlocal_user_after_update_trigger;')
|
||||
migrate_engine.execute(drop_local_user_insert_trigger)
|
||||
migrate_engine.execute(drop_local_user_update_trigger)
|
||||
migrate_engine.execute(drop_nonlocal_user_insert_trigger)
|
||||
migrate_engine.execute(drop_nonlocal_user_update_trigger)
|
||||
|
||||
|
||||
def _get_fk_name(inspector, table, fk_column):
|
||||
for fk in inspector.get_foreign_keys(table):
|
||||
if fk_column in fk['constrained_columns']:
|
||||
return fk['name']
|
||||
@@ -1,34 +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.sql import upgrades
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
user_table = sql.Table('user', meta, autoload=True)
|
||||
user_table.c.domain_id.alter(nullable=False)
|
||||
|
||||
if upgrades.USE_TRIGGERS:
|
||||
if migrate_engine.name == 'postgresql':
|
||||
drop_trigger_stmt = 'DROP TRIGGER federated_user_insert_trigger '
|
||||
drop_trigger_stmt += 'on federated_user;'
|
||||
elif migrate_engine.name == 'mysql':
|
||||
drop_trigger_stmt = 'DROP TRIGGER federated_user_insert_trigger;'
|
||||
else:
|
||||
drop_trigger_stmt = (
|
||||
'DROP TRIGGER IF EXISTS federated_user_insert_trigger;')
|
||||
migrate_engine.execute(drop_trigger_stmt)
|
||||
@@ -1,16 +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):
|
||||
# NOTE(notmorgan): This is a no-op, no data-migration needed.
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
pass
|
||||
@@ -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,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,55 +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 uuid
|
||||
|
||||
import sqlalchemy as sql
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from keystone.resource.backends import base
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
maker = sessionmaker(bind=migrate_engine)
|
||||
session = maker()
|
||||
|
||||
idp_table = sql.Table('identity_provider', meta, autoload=True)
|
||||
|
||||
for idp_row in idp_table.select().execute():
|
||||
domain_id = _create_federated_domain(meta, session, idp_row['id'])
|
||||
# update idp with the new federated domain_id
|
||||
values = {'domain_id': domain_id}
|
||||
stmt = idp_table.update().where(
|
||||
idp_table.c.id == idp_row['id']).values(values)
|
||||
stmt.execute()
|
||||
|
||||
|
||||
def _create_federated_domain(meta, session, idp_id):
|
||||
domain_id = uuid.uuid4().hex
|
||||
desc = 'Auto generated federated domain for Identity Provider: ' + idp_id
|
||||
federated_domain = {
|
||||
'id': domain_id,
|
||||
'name': domain_id,
|
||||
'enabled': True,
|
||||
'description': desc,
|
||||
'domain_id': base.NULL_DOMAIN_ID,
|
||||
'is_domain': True,
|
||||
'parent_id': None,
|
||||
'extra': '{}'
|
||||
}
|
||||
project_table = sql.Table('project', meta, autoload=True)
|
||||
new_row = project_table.insert().values(**federated_domain)
|
||||
session.execute(new_row)
|
||||
session.commit()
|
||||
return domain_id
|
||||
@@ -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,45 +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
|
||||
import sqlalchemy.sql.expression as expression
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
user_table = sql.Table('user', meta, autoload=True)
|
||||
|
||||
# update user domain_id from local_user
|
||||
local_table = sql.Table('local_user', meta, autoload=True)
|
||||
_update_user_domain_id(migrate_engine, user_table, local_table)
|
||||
|
||||
# update user domain_id from nonlocal_user
|
||||
nonlocal_table = sql.Table('nonlocal_user', meta, autoload=True)
|
||||
_update_user_domain_id(migrate_engine, user_table, nonlocal_table)
|
||||
|
||||
|
||||
def _update_user_domain_id(migrate_engine, user_table, child_user_table):
|
||||
join = sql.join(user_table, child_user_table,
|
||||
user_table.c.id == child_user_table.c.user_id)
|
||||
where = user_table.c.domain_id == expression.null()
|
||||
sel = (
|
||||
sql.select([user_table.c.id, child_user_table.c.domain_id])
|
||||
.select_from(join).where(where)
|
||||
)
|
||||
with migrate_engine.begin() as conn:
|
||||
for user in conn.execute(sel):
|
||||
values = {'domain_id': user['domain_id']}
|
||||
stmt = user_table.update().where(
|
||||
user_table.c.id == user['id']).values(values)
|
||||
conn.execute(stmt)
|
||||
@@ -1,36 +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
|
||||
import sqlalchemy.sql.expression as expression
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
user_table = sql.Table('user', meta, autoload=True)
|
||||
federated_table = sql.Table('federated_user', meta, autoload=True)
|
||||
idp_table = sql.Table('identity_provider', meta, autoload=True)
|
||||
|
||||
join = sql.join(federated_table, idp_table,
|
||||
federated_table.c.idp_id == idp_table.c.id)
|
||||
sel = sql.select(
|
||||
[federated_table.c.user_id, idp_table.c.domain_id]).select_from(join)
|
||||
with migrate_engine.begin() as conn:
|
||||
for user in conn.execute(sel):
|
||||
values = {'domain_id': user['domain_id']}
|
||||
stmt = user_table.update().where(
|
||||
sql.and_(
|
||||
user_table.c.domain_id == expression.null(),
|
||||
user_table.c.id == user['user_id'])).values(values)
|
||||
conn.execute(stmt)
|
||||
@@ -1,16 +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):
|
||||
# NOTE(notmorgan): This is a no-op, no data-migration needed.
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
# This is a placeholder for Newton backports. Do not use this number for new
|
||||
# Ocata work. New Ocata work starts after all the placeholders.
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
pass
|
||||
@@ -1,31 +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
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
revocation_event = sql.Table('revocation_event', meta, autoload=True)
|
||||
sql.Index('ix_revocation_event_issued_before',
|
||||
revocation_event.c.issued_before).create()
|
||||
sql.Index('ix_revocation_event_project_id_issued_before',
|
||||
revocation_event.c.project_id,
|
||||
revocation_event.c.issued_before).create()
|
||||
sql.Index('ix_revocation_event_user_id_issued_before',
|
||||
revocation_event.c.user_id,
|
||||
revocation_event.c.issued_before).create()
|
||||
sql.Index('ix_revocation_event_audit_id_issued_before',
|
||||
revocation_event.c.audit_id,
|
||||
revocation_event.c.issued_before).create()
|
||||
@@ -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,73 +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.sql import upgrades
|
||||
|
||||
|
||||
MYSQL_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER idp_insert_read_only BEFORE INSERT ON identity_provider
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '%s';
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER idp_insert_read_only BEFORE INSERT ON identity_provider
|
||||
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 idp_insert_read_only BEFORE INSERT ON identity_provider
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE keystone_read_only_insert();
|
||||
"""
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
idp = sql.Table('identity_provider', meta, autoload=True)
|
||||
project = sql.Table('project', meta, autoload=True)
|
||||
domain_id = sql.Column('domain_id', sql.String(64),
|
||||
sql.ForeignKey(project.c.id), nullable=True)
|
||||
idp.create_column(domain_id)
|
||||
|
||||
if upgrades.USE_TRIGGERS:
|
||||
# Setting idp to be read-only to prevent old code from creating an idp
|
||||
# without a domain_id during an upgrade. This should be okay as it is
|
||||
# highly unlikely that an idp would be created during the migration and
|
||||
# the impact from preventing creations is minor.
|
||||
error_message = ('Identity provider migration in progress. Cannot '
|
||||
'insert new rows into the identity_provider table at '
|
||||
'this time.')
|
||||
if migrate_engine.name == 'postgresql':
|
||||
idp_insert_trigger = POSTGRESQL_INSERT_TRIGGER % error_message
|
||||
elif migrate_engine.name == 'sqlite':
|
||||
idp_insert_trigger = SQLITE_INSERT_TRIGGER % error_message
|
||||
else:
|
||||
idp_insert_trigger = MYSQL_INSERT_TRIGGER % error_message
|
||||
migrate_engine.execute(idp_insert_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
|
||||
@@ -1,165 +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.sql import upgrades
|
||||
|
||||
# define the local_user triggers for insert and update
|
||||
MYSQL_LOCAL_USER_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER local_user_after_insert_trigger
|
||||
AFTER INSERT
|
||||
ON local_user FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
END;
|
||||
"""
|
||||
|
||||
MYSQL_LOCAL_USER_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER local_user_after_update_trigger
|
||||
AFTER UPDATE
|
||||
ON local_user FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id <> NEW.domain_id;
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_LOCAL_USER_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER local_user_after_insert_trigger
|
||||
AFTER INSERT
|
||||
ON local_user
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_LOCAL_USER_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER local_user_after_update_trigger
|
||||
AFTER UPDATE
|
||||
ON local_user
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id <> NEW.domain_id;
|
||||
END;
|
||||
"""
|
||||
|
||||
POSTGRESQL_LOCAL_USER_INSERT_TRIGGER = """
|
||||
CREATE OR REPLACE FUNCTION update_user_domain_id()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
UPDATE "user" SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id;
|
||||
RETURN NULL;
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER local_user_after_insert_trigger AFTER INSERT ON local_user
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE update_user_domain_id();
|
||||
"""
|
||||
|
||||
POSTGRESQL_LOCAL_USER_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER local_user_after_update_trigger AFTER UPDATE ON local_user
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE update_user_domain_id();
|
||||
"""
|
||||
|
||||
MYSQL_NONLOCAL_USER_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER nonlocal_user_after_insert_trigger
|
||||
AFTER INSERT
|
||||
ON nonlocal_user FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
END;
|
||||
"""
|
||||
|
||||
# define the nonlocal_user triggers for insert and update
|
||||
MYSQL_NONLOCAL_USER_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER nonlocal_user_after_update_trigger
|
||||
AFTER UPDATE
|
||||
ON nonlocal_user FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id <> NEW.domain_id;
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_NONLOCAL_USER_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER nonlocal_user_after_insert_trigger
|
||||
AFTER INSERT
|
||||
ON nonlocal_user
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_NONLOCAL_USER_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER nonlocal_user_after_update_trigger
|
||||
AFTER UPDATE
|
||||
ON nonlocal_user
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id and domain_id <> NEW.domain_id;
|
||||
END;
|
||||
"""
|
||||
|
||||
POSTGRESQL_NONLOCAL_USER_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER nonlocal_user_after_insert_trigger AFTER INSERT ON nonlocal_user
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE update_user_domain_id();
|
||||
"""
|
||||
|
||||
POSTGRESQL_NONLOCAL_USER_UPDATE_TRIGGER = """
|
||||
CREATE TRIGGER nonlocal_user_after_update_trigger AFTER UPDATE ON nonlocal_user
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE update_user_domain_id();
|
||||
"""
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
user = sql.Table('user', meta, autoload=True)
|
||||
project = sql.Table('project', meta, autoload=True)
|
||||
domain_id = sql.Column('domain_id', sql.String(64),
|
||||
sql.ForeignKey(project.c.id), nullable=True)
|
||||
user.create_column(domain_id)
|
||||
|
||||
if upgrades.USE_TRIGGERS:
|
||||
if migrate_engine.name == 'postgresql':
|
||||
local_user_insert_trigger = POSTGRESQL_LOCAL_USER_INSERT_TRIGGER
|
||||
local_user_update_trigger = POSTGRESQL_LOCAL_USER_UPDATE_TRIGGER
|
||||
nonlocal_user_insert_trigger = (
|
||||
POSTGRESQL_NONLOCAL_USER_INSERT_TRIGGER)
|
||||
nonlocal_user_update_trigger = (
|
||||
POSTGRESQL_NONLOCAL_USER_UPDATE_TRIGGER)
|
||||
elif migrate_engine.name == 'sqlite':
|
||||
local_user_insert_trigger = SQLITE_LOCAL_USER_INSERT_TRIGGER
|
||||
local_user_update_trigger = SQLITE_LOCAL_USER_UPDATE_TRIGGER
|
||||
nonlocal_user_insert_trigger = SQLITE_NONLOCAL_USER_INSERT_TRIGGER
|
||||
nonlocal_user_update_trigger = SQLITE_NONLOCAL_USER_UPDATE_TRIGGER
|
||||
else:
|
||||
local_user_insert_trigger = MYSQL_LOCAL_USER_INSERT_TRIGGER
|
||||
local_user_update_trigger = MYSQL_LOCAL_USER_UPDATE_TRIGGER
|
||||
nonlocal_user_insert_trigger = MYSQL_NONLOCAL_USER_INSERT_TRIGGER
|
||||
nonlocal_user_update_trigger = MYSQL_NONLOCAL_USER_UPDATE_TRIGGER
|
||||
migrate_engine.execute(local_user_insert_trigger)
|
||||
migrate_engine.execute(local_user_update_trigger)
|
||||
migrate_engine.execute(nonlocal_user_insert_trigger)
|
||||
migrate_engine.execute(nonlocal_user_update_trigger)
|
||||
@@ -1,69 +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.sql import upgrades
|
||||
|
||||
|
||||
MYSQL_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER federated_user_insert_trigger
|
||||
AFTER INSERT
|
||||
ON federated_user FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = (
|
||||
SELECT domain_id FROM identity_provider WHERE id = NEW.idp_id)
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
END;
|
||||
"""
|
||||
|
||||
SQLITE_INSERT_TRIGGER = """
|
||||
CREATE TRIGGER federated_user_insert_trigger
|
||||
AFTER INSERT
|
||||
ON federated_user
|
||||
BEGIN
|
||||
UPDATE user SET domain_id = (
|
||||
SELECT domain_id FROM identity_provider WHERE id = NEW.idp_id)
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
END;
|
||||
"""
|
||||
|
||||
POSTGRESQL_INSERT_TRIGGER = """
|
||||
CREATE OR REPLACE FUNCTION update_federated_user_domain_id()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
UPDATE "user" SET domain_id = (
|
||||
SELECT domain_id FROM identity_provider WHERE id = NEW.idp_id)
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
RETURN NULL;
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER federated_user_insert_trigger AFTER INSERT ON federated_user
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE update_federated_user_domain_id();
|
||||
"""
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
if upgrades.USE_TRIGGERS:
|
||||
if migrate_engine.name == 'postgresql':
|
||||
insert_trigger = POSTGRESQL_INSERT_TRIGGER
|
||||
elif migrate_engine.name == 'sqlite':
|
||||
insert_trigger = SQLITE_INSERT_TRIGGER
|
||||
else:
|
||||
insert_trigger = MYSQL_INSERT_TRIGGER
|
||||
migrate_engine.execute(insert_trigger)
|
||||
@@ -1,34 +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
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
user_table = sql.Table('user', meta, autoload=True)
|
||||
|
||||
user_option = sql.Table(
|
||||
'user_option',
|
||||
meta,
|
||||
sql.Column('user_id', sql.String(64), sql.ForeignKey(user_table.c.id,
|
||||
ondelete='CASCADE'), nullable=False, primary_key=True),
|
||||
sql.Column('option_id', sql.String(4), nullable=False,
|
||||
primary_key=True),
|
||||
sql.Column('option_value', ks_sql.JsonBlob, nullable=True),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8')
|
||||
|
||||
user_option.create(migrate_engine, checkfirst=True)
|
||||
@@ -192,6 +192,7 @@ def upgrade(migrate_engine):
|
||||
sql.Column('id', sql.String(64), primary_key=True),
|
||||
sql.Column('enabled', sql.Boolean, nullable=False),
|
||||
sql.Column('description', sql.Text(), nullable=True),
|
||||
sql.Column('domain_id', sql.String(64), nullable=False),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8',
|
||||
)
|
||||
@@ -225,7 +226,6 @@ def upgrade(migrate_engine):
|
||||
sql.Column(
|
||||
'user_id',
|
||||
sql.String(64),
|
||||
sql.ForeignKey('user.id', ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
unique=True,
|
||||
),
|
||||
@@ -401,6 +401,22 @@ def upgrade(migrate_engine):
|
||||
# was 'revocation_event_new' so the index got that name. We may wish to
|
||||
# rename this eventually.
|
||||
sql.Index('ix_revocation_event_new_revoked_at', 'revoked_at'),
|
||||
sql.Index('ix_revocation_event_issued_before', 'issued_before'),
|
||||
sql.Index(
|
||||
'ix_revocation_event_project_id_issued_before',
|
||||
'project_id',
|
||||
'issued_before',
|
||||
),
|
||||
sql.Index(
|
||||
'ix_revocation_event_user_id_issued_before',
|
||||
'user_id',
|
||||
'issued_before',
|
||||
),
|
||||
sql.Index(
|
||||
'ix_revocation_event_audit_id_issued_before',
|
||||
'audit_id',
|
||||
'issued_before',
|
||||
),
|
||||
)
|
||||
|
||||
role = sql.Table(
|
||||
@@ -469,9 +485,7 @@ def upgrade(migrate_engine):
|
||||
sql.Column('trust_id', sql.String(length=64)),
|
||||
sql.Column('user_id', sql.String(length=64)),
|
||||
sql.Index('ix_token_expires', 'expires'),
|
||||
sql.Index(
|
||||
'ix_token_expires_valid', 'expires', 'valid'
|
||||
),
|
||||
sql.Index('ix_token_expires_valid', 'expires', 'valid'),
|
||||
sql.Index('ix_token_user_id', 'user_id'),
|
||||
sql.Index('ix_token_trust_id', 'trust_id'),
|
||||
mysql_engine='InnoDB',
|
||||
@@ -524,6 +538,31 @@ def upgrade(migrate_engine):
|
||||
sql.Column('default_project_id', sql.String(length=64)),
|
||||
sql.Column('created_at', sql.DateTime(), nullable=True),
|
||||
sql.Column('last_active_at', sql.Date(), nullable=True),
|
||||
sql.Column(
|
||||
'domain_id',
|
||||
sql.String(64),
|
||||
sql.ForeignKey(project.c.id),
|
||||
nullable=False,
|
||||
),
|
||||
sql.UniqueConstraint('id', 'domain_id', name='ixu_user_id_domain_id'),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8',
|
||||
)
|
||||
|
||||
user_option = sql.Table(
|
||||
'user_option',
|
||||
meta,
|
||||
sql.Column(
|
||||
'user_id',
|
||||
sql.String(64),
|
||||
sql.ForeignKey(user.c.id, ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sql.Column(
|
||||
'option_id', sql.String(4), nullable=False, primary_key=True
|
||||
),
|
||||
sql.Column('option_value', ks_sql.JsonBlob, nullable=True),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8',
|
||||
)
|
||||
@@ -536,9 +575,9 @@ def upgrade(migrate_engine):
|
||||
sql.Column(
|
||||
'user_id',
|
||||
sql.String(64),
|
||||
sql.ForeignKey(user.c.id, ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
),
|
||||
sql.UniqueConstraint('user_id', name='ixu_nonlocal_user_user_id'),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8',
|
||||
)
|
||||
@@ -656,6 +695,7 @@ def upgrade(migrate_engine):
|
||||
trust,
|
||||
trust_role,
|
||||
user,
|
||||
user_option,
|
||||
user_group_membership,
|
||||
region,
|
||||
assignment,
|
||||
@@ -738,6 +778,24 @@ def upgrade(migrate_engine):
|
||||
federation_protocol.c.id,
|
||||
federation_protocol.c.idp_id,
|
||||
],
|
||||
'ondelete': 'CASCADE',
|
||||
},
|
||||
{
|
||||
'columns': [identity_provider.c.domain_id],
|
||||
'references': [project.c.id],
|
||||
'name': 'domain_id',
|
||||
},
|
||||
{
|
||||
'columns': [local_user.c.user_id, local_user.c.domain_id],
|
||||
'references': [user.c.id, user.c.domain_id],
|
||||
'onupdate': 'CASCADE',
|
||||
'ondelete': 'CASCADE',
|
||||
},
|
||||
{
|
||||
'columns': [nonlocal_user.c.user_id, nonlocal_user.c.domain_id],
|
||||
'references': [user.c.id, user.c.domain_id],
|
||||
'onupdate': 'CASCADE',
|
||||
'ondelete': 'CASCADE',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -758,6 +816,7 @@ def upgrade(migrate_engine):
|
||||
refcolumns=fkey['references'],
|
||||
name=fkey.get('name'),
|
||||
ondelete=fkey.get('ondelete'),
|
||||
onupdate=fkey.get('onupdate'),
|
||||
).create()
|
||||
|
||||
# TODO(stephenfin): Remove these procedures in a future contract migration
|
||||
@@ -783,3 +842,46 @@ def upgrade(migrate_engine):
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
""")
|
||||
migrate_engine.execute(credential_update_trigger)
|
||||
|
||||
error_message = (
|
||||
'Identity provider migration in progress. Cannot '
|
||||
'insert new rows into the identity_provider table at '
|
||||
'this time.'
|
||||
)
|
||||
identity_provider_insert_trigger = textwrap.dedent(f"""
|
||||
CREATE OR REPLACE FUNCTION keystone_read_only_insert()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
RAISE EXCEPTION '{error_message}';
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
""")
|
||||
migrate_engine.execute(identity_provider_insert_trigger)
|
||||
|
||||
federated_user_insert_trigger = textwrap.dedent("""
|
||||
CREATE OR REPLACE FUNCTION update_federated_user_domain_id()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
UPDATE "user" SET domain_id = (
|
||||
SELECT domain_id FROM identity_provider WHERE id = NEW.idp_id)
|
||||
WHERE id = NEW.user_id and domain_id IS NULL;
|
||||
RETURN NULL;
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
""")
|
||||
migrate_engine.execute(federated_user_insert_trigger)
|
||||
|
||||
local_user_insert_trigger = textwrap.dedent("""
|
||||
CREATE OR REPLACE FUNCTION update_user_domain_id()
|
||||
RETURNS trigger AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
UPDATE "user" SET domain_id = NEW.domain_id
|
||||
WHERE id = NEW.user_id;
|
||||
RETURN NULL;
|
||||
END
|
||||
$BODY$ LANGUAGE plpgsql;
|
||||
""")
|
||||
migrate_engine.execute(local_user_insert_trigger)
|
||||
@@ -29,7 +29,7 @@ from keystone.i18n import _
|
||||
|
||||
USE_TRIGGERS = True
|
||||
|
||||
INITIAL_VERSION = 3
|
||||
INITIAL_VERSION = 15
|
||||
EXPAND_REPO = 'expand_repo'
|
||||
DATA_MIGRATION_REPO = 'data_migration_repo'
|
||||
CONTRACT_REPO = 'contract_repo'
|
||||
|
||||
@@ -41,7 +41,6 @@ For further information, see `oslo.db documentation
|
||||
|
||||
import datetime
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
@@ -112,7 +111,10 @@ INITIAL_TABLE_STRUCTURE = {
|
||||
],
|
||||
'user': [
|
||||
'id', 'extra', 'enabled', 'default_project_id', 'created_at',
|
||||
'last_active_at',
|
||||
'last_active_at', 'domain_id',
|
||||
],
|
||||
'user_option': [
|
||||
'user_id', 'option_id', 'option_value',
|
||||
],
|
||||
'user_group_membership': [
|
||||
'user_id', 'group_id',
|
||||
@@ -136,7 +138,7 @@ INITIAL_TABLE_STRUCTURE = {
|
||||
'id', 'policy_id', 'endpoint_id', 'service_id', 'region_id',
|
||||
],
|
||||
'identity_provider': [
|
||||
'id', 'enabled', 'description',
|
||||
'id', 'enabled', 'description', 'domain_id',
|
||||
],
|
||||
'federation_protocol': [
|
||||
'id', 'idp_id', 'mapping_id',
|
||||
@@ -603,431 +605,6 @@ class FullMigration(MigrateBase, unit.TestCase):
|
||||
upgrades.INITIAL_VERSION + 2,
|
||||
)
|
||||
|
||||
def test_migration_010_add_revocation_event_indexes(self):
|
||||
self.expand(9)
|
||||
self.migrate(9)
|
||||
self.contract(9)
|
||||
self.assertFalse(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_issued_before'))
|
||||
self.assertFalse(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_project_id_issued_before'))
|
||||
self.assertFalse(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_user_id_issued_before'))
|
||||
self.assertFalse(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_audit_id_issued_before'))
|
||||
self.expand(10)
|
||||
self.migrate(10)
|
||||
self.contract(10)
|
||||
self.assertTrue(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_issued_before'))
|
||||
self.assertTrue(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_project_id_issued_before'))
|
||||
self.assertTrue(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_user_id_issued_before'))
|
||||
self.assertTrue(self.does_index_exist(
|
||||
'revocation_event',
|
||||
'ix_revocation_event_audit_id_issued_before'))
|
||||
|
||||
def test_migration_011_user_id_unique_for_nonlocal_user(self):
|
||||
table_name = 'nonlocal_user'
|
||||
column = 'user_id'
|
||||
self.expand(10)
|
||||
self.migrate(10)
|
||||
self.contract(10)
|
||||
self.assertFalse(self.does_unique_constraint_exist(table_name, column))
|
||||
self.expand(11)
|
||||
self.migrate(11)
|
||||
self.contract(11)
|
||||
self.assertTrue(self.does_unique_constraint_exist(table_name, column))
|
||||
|
||||
def test_migration_012_add_domain_id_to_idp(self):
|
||||
def _create_domain():
|
||||
domain_id = uuid.uuid4().hex
|
||||
domain = {
|
||||
'id': domain_id,
|
||||
'name': domain_id,
|
||||
'enabled': True,
|
||||
'description': uuid.uuid4().hex,
|
||||
'domain_id': resource_base.NULL_DOMAIN_ID,
|
||||
'is_domain': True,
|
||||
'parent_id': None,
|
||||
'extra': '{}'
|
||||
}
|
||||
self.insert_dict(session, 'project', domain)
|
||||
return domain_id
|
||||
|
||||
def _get_new_idp(domain_id):
|
||||
new_idp = {'id': uuid.uuid4().hex,
|
||||
'domain_id': domain_id,
|
||||
'enabled': True,
|
||||
'description': uuid.uuid4().hex}
|
||||
return new_idp
|
||||
|
||||
session = self.sessionmaker()
|
||||
idp_name = 'identity_provider'
|
||||
self.expand(11)
|
||||
self.migrate(11)
|
||||
self.contract(11)
|
||||
self.assertTableColumns(idp_name,
|
||||
['id',
|
||||
'enabled',
|
||||
'description'])
|
||||
# add some data
|
||||
for i in range(5):
|
||||
idp = {'id': uuid.uuid4().hex,
|
||||
'enabled': True,
|
||||
'description': uuid.uuid4().hex}
|
||||
self.insert_dict(session, idp_name, idp)
|
||||
|
||||
# upgrade
|
||||
self.expand(12)
|
||||
self.assertTableColumns(idp_name,
|
||||
['id',
|
||||
'domain_id',
|
||||
'enabled',
|
||||
'description'])
|
||||
|
||||
# confirm we cannot insert an idp during expand
|
||||
domain_id = _create_domain()
|
||||
new_idp = _get_new_idp(domain_id)
|
||||
self.assertRaises(db_exception.DBError, self.insert_dict, session,
|
||||
idp_name, new_idp)
|
||||
|
||||
# confirm we cannot insert an idp during migrate
|
||||
self.migrate(12)
|
||||
self.assertRaises(db_exception.DBError, self.insert_dict, session,
|
||||
idp_name, new_idp)
|
||||
|
||||
# confirm we can insert a new idp after contract
|
||||
self.contract(12)
|
||||
self.insert_dict(session, idp_name, new_idp)
|
||||
|
||||
# confirm domain_id column is not null
|
||||
idp_table = sqlalchemy.Table(idp_name, self.metadata, autoload=True)
|
||||
self.assertFalse(idp_table.c.domain_id.nullable)
|
||||
|
||||
def test_migration_013_protocol_cascade_delete_for_federated_user(self):
|
||||
if self.engine.name == 'sqlite':
|
||||
self.skipTest('sqlite backend does not support foreign keys')
|
||||
|
||||
self.expand(12)
|
||||
self.migrate(12)
|
||||
self.contract(12)
|
||||
|
||||
# This test requires a bit of setup to properly work, first we create
|
||||
# an identity provider, mapping and a protocol. Then, we create a
|
||||
# federated user and delete the protocol. We expect the federated user
|
||||
# to be deleted as well.
|
||||
|
||||
session = self.sessionmaker()
|
||||
|
||||
def _create_protocol():
|
||||
domain = {
|
||||
'id': uuid.uuid4().hex,
|
||||
'name': uuid.uuid4().hex,
|
||||
'domain_id': resource_base.NULL_DOMAIN_ID,
|
||||
'is_domain': True,
|
||||
'parent_id': None
|
||||
}
|
||||
self.insert_dict(session, 'project', domain)
|
||||
|
||||
idp = {'id': uuid.uuid4().hex, 'enabled': True,
|
||||
'domain_id': domain['id']}
|
||||
self.insert_dict(session, 'identity_provider', idp)
|
||||
|
||||
mapping = {'id': uuid.uuid4().hex, 'rules': json.dumps([])}
|
||||
self.insert_dict(session, 'mapping', mapping)
|
||||
|
||||
protocol = {'id': uuid.uuid4().hex, 'idp_id': idp['id'],
|
||||
'mapping_id': mapping['id']}
|
||||
protocol_table = sqlalchemy.Table(
|
||||
'federation_protocol', self.metadata, autoload=True)
|
||||
self.insert_dict(session, 'federation_protocol', protocol,
|
||||
table=protocol_table)
|
||||
|
||||
return protocol, protocol_table
|
||||
|
||||
def _create_federated_user(idp_id, protocol_id):
|
||||
user = {'id': uuid.uuid4().hex}
|
||||
self.insert_dict(session, 'user', user)
|
||||
|
||||
# NOTE(rodrigods): do not set the ID, the engine will do that
|
||||
# for us and we won't need it later.
|
||||
federated_user = {
|
||||
'user_id': user['id'], 'idp_id': idp_id,
|
||||
'protocol_id': protocol_id, 'unique_id': uuid.uuid4().hex}
|
||||
federated_table = sqlalchemy.Table(
|
||||
'federated_user', self.metadata, autoload=True)
|
||||
self.insert_dict(session, 'federated_user', federated_user,
|
||||
table=federated_table)
|
||||
|
||||
return federated_user, federated_table
|
||||
|
||||
protocol, protocol_table = _create_protocol()
|
||||
federated_user, federated_table = _create_federated_user(
|
||||
protocol['idp_id'], protocol['id'])
|
||||
|
||||
# before updating the foreign key, we won't be able to delete the
|
||||
# protocol
|
||||
self.assertRaises(db_exception.DBError,
|
||||
session.execute,
|
||||
protocol_table.delete().where(
|
||||
protocol_table.c.id == protocol['id']))
|
||||
|
||||
self.expand(13)
|
||||
self.migrate(13)
|
||||
self.contract(13)
|
||||
|
||||
# now we are able to delete the protocol
|
||||
session.execute(
|
||||
protocol_table.delete().where(
|
||||
protocol_table.c.id == protocol['id']))
|
||||
|
||||
# assert the cascade deletion worked
|
||||
federated_users = session.query(federated_table).filter_by(
|
||||
protocol_id=federated_user['protocol_id']).all()
|
||||
self.assertThat(federated_users, matchers.HasLength(0))
|
||||
|
||||
def test_migration_014_add_domain_id_to_user_table(self):
|
||||
def create_domain():
|
||||
table = sqlalchemy.Table('project', self.metadata, autoload=True)
|
||||
domain_id = uuid.uuid4().hex
|
||||
domain = {
|
||||
'id': domain_id,
|
||||
'name': domain_id,
|
||||
'enabled': True,
|
||||
'description': uuid.uuid4().hex,
|
||||
'domain_id': resource_base.NULL_DOMAIN_ID,
|
||||
'is_domain': True,
|
||||
'parent_id': None,
|
||||
'extra': '{}'
|
||||
}
|
||||
table.insert().values(domain).execute()
|
||||
return domain_id
|
||||
|
||||
def create_user(table):
|
||||
user_id = uuid.uuid4().hex
|
||||
user = {'id': user_id, 'enabled': True}
|
||||
table.insert().values(user).execute()
|
||||
return user_id
|
||||
|
||||
# insert local_user or nonlocal_user
|
||||
def create_child_user(table, user_id, domain_id):
|
||||
child_user = {
|
||||
'user_id': user_id,
|
||||
'domain_id': domain_id,
|
||||
'name': uuid.uuid4().hex
|
||||
}
|
||||
table.insert().values(child_user).execute()
|
||||
|
||||
# update local_user or nonlocal_user
|
||||
def update_child_user(table, user_id, new_domain_id):
|
||||
table.update().where(table.c.user_id == user_id).values(
|
||||
domain_id=new_domain_id).execute()
|
||||
|
||||
def assertUserDomain(user_id, domain_id):
|
||||
user = sqlalchemy.Table('user', self.metadata, autoload=True)
|
||||
cols = [user.c.domain_id]
|
||||
filter = user.c.id == user_id
|
||||
sel = sqlalchemy.select(cols).where(filter)
|
||||
domains = sel.execute().fetchone()
|
||||
self.assertEqual(domain_id, domains[0])
|
||||
|
||||
user_table_name = 'user'
|
||||
self.expand(13)
|
||||
self.migrate(13)
|
||||
self.contract(13)
|
||||
self.assertTableColumns(
|
||||
user_table_name, ['id', 'extra', 'enabled', 'default_project_id',
|
||||
'created_at', 'last_active_at'])
|
||||
self.expand(14)
|
||||
self.assertTableColumns(
|
||||
user_table_name, ['id', 'extra', 'enabled', 'default_project_id',
|
||||
'created_at', 'last_active_at', 'domain_id'])
|
||||
user_table = sqlalchemy.Table(user_table_name, self.metadata,
|
||||
autoload=True)
|
||||
local_user_table = sqlalchemy.Table('local_user', self.metadata,
|
||||
autoload=True)
|
||||
nonlocal_user_table = sqlalchemy.Table('nonlocal_user', self.metadata,
|
||||
autoload=True)
|
||||
|
||||
# add users before migrate to test that the user.domain_id gets updated
|
||||
# after migrate
|
||||
user_ids = []
|
||||
expected_domain_id = create_domain()
|
||||
user_id = create_user(user_table)
|
||||
create_child_user(local_user_table, user_id, expected_domain_id)
|
||||
user_ids.append(user_id)
|
||||
user_id = create_user(user_table)
|
||||
create_child_user(nonlocal_user_table, user_id, expected_domain_id)
|
||||
user_ids.append(user_id)
|
||||
|
||||
self.migrate(14)
|
||||
# test local_user insert trigger updates user.domain_id
|
||||
user_id = create_user(user_table)
|
||||
domain_id = create_domain()
|
||||
create_child_user(local_user_table, user_id, domain_id)
|
||||
assertUserDomain(user_id, domain_id)
|
||||
|
||||
# test local_user update trigger updates user.domain_id
|
||||
new_domain_id = create_domain()
|
||||
update_child_user(local_user_table, user_id, new_domain_id)
|
||||
assertUserDomain(user_id, new_domain_id)
|
||||
|
||||
# test nonlocal_user insert trigger updates user.domain_id
|
||||
user_id = create_user(user_table)
|
||||
create_child_user(nonlocal_user_table, user_id, domain_id)
|
||||
assertUserDomain(user_id, domain_id)
|
||||
|
||||
# test nonlocal_user update trigger updates user.domain_id
|
||||
update_child_user(nonlocal_user_table, user_id, new_domain_id)
|
||||
assertUserDomain(user_id, new_domain_id)
|
||||
|
||||
self.contract(14)
|
||||
# test migrate updated the user.domain_id
|
||||
for user_id in user_ids:
|
||||
assertUserDomain(user_id, expected_domain_id)
|
||||
|
||||
# test unique and fk constraints
|
||||
if self.engine.name == 'mysql':
|
||||
self.assertTrue(
|
||||
self.does_index_exist('user', 'ixu_user_id_domain_id'))
|
||||
else:
|
||||
self.assertTrue(
|
||||
self.does_constraint_exist('user', 'ixu_user_id_domain_id'))
|
||||
self.assertTrue(self.does_fk_exist('local_user', 'user_id'))
|
||||
self.assertTrue(self.does_fk_exist('local_user', 'domain_id'))
|
||||
self.assertTrue(self.does_fk_exist('nonlocal_user', 'user_id'))
|
||||
self.assertTrue(self.does_fk_exist('nonlocal_user', 'domain_id'))
|
||||
|
||||
def test_migration_015_update_federated_user_domain(self):
|
||||
def create_domain():
|
||||
table = sqlalchemy.Table('project', self.metadata, autoload=True)
|
||||
domain_id = uuid.uuid4().hex
|
||||
domain = {
|
||||
'id': domain_id,
|
||||
'name': domain_id,
|
||||
'enabled': True,
|
||||
'description': uuid.uuid4().hex,
|
||||
'domain_id': resource_base.NULL_DOMAIN_ID,
|
||||
'is_domain': True,
|
||||
'parent_id': None,
|
||||
'extra': '{}'
|
||||
}
|
||||
table.insert().values(domain).execute()
|
||||
return domain_id
|
||||
|
||||
def create_idp(domain_id):
|
||||
table = sqlalchemy.Table('identity_provider', self.metadata,
|
||||
autoload=True)
|
||||
idp_id = uuid.uuid4().hex
|
||||
idp = {
|
||||
'id': idp_id,
|
||||
'domain_id': domain_id,
|
||||
'enabled': True,
|
||||
'description': uuid.uuid4().hex
|
||||
}
|
||||
table.insert().values(idp).execute()
|
||||
return idp_id
|
||||
|
||||
def create_protocol(idp_id):
|
||||
table = sqlalchemy.Table('federation_protocol', self.metadata,
|
||||
autoload=True)
|
||||
protocol_id = uuid.uuid4().hex
|
||||
protocol = {
|
||||
'id': protocol_id,
|
||||
'idp_id': idp_id,
|
||||
'mapping_id': uuid.uuid4().hex
|
||||
}
|
||||
table.insert().values(protocol).execute()
|
||||
return protocol_id
|
||||
|
||||
def create_user():
|
||||
table = sqlalchemy.Table('user', self.metadata, autoload=True)
|
||||
user_id = uuid.uuid4().hex
|
||||
user = {'id': user_id, 'enabled': True}
|
||||
table.insert().values(user).execute()
|
||||
return user_id
|
||||
|
||||
def create_federated_user(user_id, idp_id, protocol_id):
|
||||
table = sqlalchemy.Table('federated_user', self.metadata,
|
||||
autoload=True)
|
||||
federated_user = {
|
||||
'user_id': user_id,
|
||||
'idp_id': idp_id,
|
||||
'protocol_id': protocol_id,
|
||||
'unique_id': uuid.uuid4().hex,
|
||||
'display_name': uuid.uuid4().hex
|
||||
}
|
||||
table.insert().values(federated_user).execute()
|
||||
|
||||
def assertUserDomain(user_id, domain_id):
|
||||
table = sqlalchemy.Table('user', self.metadata, autoload=True)
|
||||
where = table.c.id == user_id
|
||||
stmt = sqlalchemy.select([table.c.domain_id]).where(where)
|
||||
domains = stmt.execute().fetchone()
|
||||
self.assertEqual(domain_id, domains[0])
|
||||
|
||||
def assertUserDomainIsNone(user_id):
|
||||
table = sqlalchemy.Table('user', self.metadata, autoload=True)
|
||||
where = table.c.id == user_id
|
||||
stmt = sqlalchemy.select([table.c.domain_id]).where(where)
|
||||
domains = stmt.execute().fetchone()
|
||||
self.assertIsNone(domains[0])
|
||||
|
||||
self.expand(14)
|
||||
self.migrate(14)
|
||||
self.contract(14)
|
||||
|
||||
domain_id = create_domain()
|
||||
idp_id = create_idp(domain_id)
|
||||
protocol_id = create_protocol(idp_id)
|
||||
|
||||
# create user before expand to test data migration
|
||||
user_id_before_expand = create_user()
|
||||
create_federated_user(user_id_before_expand, idp_id, protocol_id)
|
||||
assertUserDomainIsNone(user_id_before_expand)
|
||||
|
||||
self.expand(15)
|
||||
# create user before migrate to test insert trigger
|
||||
user_id_before_migrate = create_user()
|
||||
create_federated_user(user_id_before_migrate, idp_id, protocol_id)
|
||||
assertUserDomain(user_id_before_migrate, domain_id)
|
||||
|
||||
self.migrate(15)
|
||||
# test insert trigger after migrate
|
||||
user_id = create_user()
|
||||
create_federated_user(user_id, idp_id, protocol_id)
|
||||
assertUserDomain(user_id, domain_id)
|
||||
|
||||
self.contract(15)
|
||||
# test migrate updated the user.domain_id
|
||||
assertUserDomain(user_id_before_expand, domain_id)
|
||||
|
||||
# verify that the user.domain_id is now not nullable
|
||||
user_table = sqlalchemy.Table('user', self.metadata, autoload=True)
|
||||
self.assertFalse(user_table.c.domain_id.nullable)
|
||||
|
||||
def test_migration_016_add_user_options(self):
|
||||
self.expand(15)
|
||||
self.migrate(15)
|
||||
self.contract(15)
|
||||
|
||||
user_option = 'user_option'
|
||||
self.assertTableDoesNotExist(user_option)
|
||||
self.expand(16)
|
||||
self.migrate(16)
|
||||
self.contract(16)
|
||||
self.assertTableColumns(user_option,
|
||||
['user_id', 'option_id', 'option_value'])
|
||||
|
||||
def test_migration_024_add_created_expires_at_int_columns_password(self):
|
||||
|
||||
self.expand(23)
|
||||
@@ -2181,10 +1758,6 @@ class FullMigration(MigrateBase, unit.TestCase):
|
||||
class MySQLOpportunisticFullMigration(FullMigration):
|
||||
FIXTURE = db_fixtures.MySQLOpportunisticFixture
|
||||
|
||||
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')
|
||||
|
||||
|
||||
class PostgreSQLOpportunisticFullMigration(FullMigration):
|
||||
FIXTURE = db_fixtures.PostgresqlOpportunisticFixture
|
||||
|
||||
Reference in New Issue
Block a user