diff --git a/keystone/identity/mapping_backends/sql.py b/keystone/identity/mapping_backends/sql.py index 7e2fc9aeb3..7da264dd9e 100644 --- a/keystone/identity/mapping_backends/sql.py +++ b/keystone/identity/mapping_backends/sql.py @@ -65,12 +65,17 @@ class Mapping(base.MappingDriverV8): def create_id_mapping(self, local_entity, public_id=None): entity = local_entity.copy() - with sql.session_for_write() as session: - if public_id is None: - public_id = self.id_generator_api.generate_public_ID(entity) - entity['public_id'] = public_id - mapping_ref = IDMapping.from_dict(entity) - session.add(mapping_ref) + try: + with sql.session_for_write() as session: + if public_id is None: + public_id = self.id_generator_api.generate_public_ID( + entity) + entity['public_id'] = public_id + mapping_ref = IDMapping.from_dict(entity) + session.add(mapping_ref) + except sql.DBDuplicateEntry: + # something else created the mapping already. We can use it. + public_id = self.get_public_id(local_entity) return public_id def delete_id_mapping(self, public_id): diff --git a/keystone/tests/unit/test_backend_id_mapping_sql.py b/keystone/tests/unit/test_backend_id_mapping_sql.py index e6635e1875..1edc56d67e 100644 --- a/keystone/tests/unit/test_backend_id_mapping_sql.py +++ b/keystone/tests/unit/test_backend_id_mapping_sql.py @@ -196,3 +196,21 @@ class SqlIDMapping(test_backend_sql.SqlTests): self.id_mapping_api.purge_mappings({}) self.assertThat(mapping_sql.list_id_mappings(), matchers.HasLength(initial_mappings)) + + def test_create_duplicate_mapping(self): + local_entity = { + 'domain_id': self.domainA['id'], + 'local_id': uuid.uuid4().hex, + 'entity_type': mapping.EntityType.USER} + public_id1 = self.id_mapping_api.create_id_mapping(local_entity) + + # second call should be successful and return the same + # public_id as above + public_id2 = self.id_mapping_api.create_id_mapping(local_entity) + self.assertEqual(public_id1, public_id2) + + # even if public_id was specified, it should not be used, + # and still the same public_id should be returned + public_id3 = self.id_mapping_api.create_id_mapping( + local_entity, public_id=uuid.uuid4().hex) + self.assertEqual(public_id1, public_id3)