Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate all identity_zone.subdomain values in the DB to lowercase.
Some databases are case sensitives, but subdomains should not be. For existing databases that may have duplicates based on case, will have a breaking change. This is highly unlikely though. https://www.pivotaltracker.com/story/show/101469422 [#101469422]
- Loading branch information
Showing
10 changed files
with
410 additions
and
56 deletions.
There are no files selected for viewing
94 changes: 94 additions & 0 deletions
94
common/src/main/java/org/cloudfoundry/identity/uaa/db/StoreSubDomainAsLowerCase_V2_7_3.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* | ||
* ***************************************************************************** | ||
* Cloud Foundry | ||
* Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved. | ||
* This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this product except in compliance with the License. | ||
* | ||
* This product includes a number of subcomponents with | ||
* separate copyright notices and license terms. Your use of these | ||
* subcomponents is subject to the terms and conditions of the | ||
* subcomponent's license, as noted in the LICENSE file. | ||
* ***************************************************************************** | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.db; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.cloudfoundry.identity.uaa.zone.IdentityZone; | ||
import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; | ||
import org.cloudfoundry.identity.uaa.zone.JdbcIdentityZoneProvisioning; | ||
import org.flywaydb.core.api.migration.spring.SpringJdbcMigration; | ||
import org.flywaydb.core.internal.util.StringUtils; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; | ||
|
||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
public class StoreSubDomainAsLowerCase_V2_7_3 implements SpringJdbcMigration { | ||
|
||
Log logger = LogFactory.getLog(StoreSubDomainAsLowerCase_V2_7_3.class); | ||
|
||
@Override | ||
public synchronized void migrate(JdbcTemplate jdbcTemplate) throws Exception { | ||
RandomValueStringGenerator generator = new RandomValueStringGenerator(3); | ||
IdentityZoneProvisioning provisioning = new JdbcIdentityZoneProvisioning(jdbcTemplate); | ||
Map<String, List<IdentityZone>> zones = new HashMap<>(); | ||
Set<String> duplicates = new HashSet<>(); | ||
for (IdentityZone zone : provisioning.retrieveAll()) { | ||
addToMap(zone, zones, duplicates); | ||
} | ||
for (String s : duplicates) { | ||
logger.debug("Processing zone duplicates for subdomain:" + s); | ||
List<IdentityZone> dupZones = zones.get(s); | ||
for (int i=1; dupZones.size()>1 && i<dupZones.size(); i++) { | ||
IdentityZone dupZone = dupZones.get(i); | ||
String newsubdomain = null; | ||
while (newsubdomain==null) { | ||
String potentialsubdomain = (dupZone.getSubdomain() +"-"+ generator.generate()).toLowerCase(); | ||
if (zones.get(potentialsubdomain)==null) { | ||
newsubdomain = potentialsubdomain; | ||
} | ||
} | ||
logger.debug(String.format("Updating zone id:%s; old subdomain: %s; new subdomain: %s;", dupZone.getId(), dupZone.getSubdomain(), newsubdomain)); | ||
dupZone.setSubdomain(newsubdomain); | ||
dupZone = provisioning.update(dupZone); | ||
zones.put(newsubdomain, Arrays.asList(dupZone)); | ||
} | ||
} | ||
for (IdentityZone zone : provisioning.retrieveAll()) { | ||
String subdomain = zone.getSubdomain(); | ||
if (StringUtils.hasText(subdomain) && !(subdomain.toLowerCase().equals(subdomain))) { | ||
logger.debug(String.format("Lowercasing zone subdomain for id:%s; old subdomain: %s; new subdomain: %s;", zone.getId(), zone.getSubdomain(), zone.getSubdomain().toLowerCase())); | ||
zone.setSubdomain(subdomain.toLowerCase()); | ||
provisioning.update(zone); | ||
} | ||
|
||
} | ||
} | ||
|
||
private void addToMap(IdentityZone zone, Map<String, List<IdentityZone>> zones, Set<String> duplicates) { | ||
if (zone==null || zone.getSubdomain()==null) { | ||
return; | ||
} | ||
String subdomain = zone.getSubdomain().toLowerCase(); | ||
if (zones.get(subdomain)==null) { | ||
List<IdentityZone> list = new LinkedList<>(); | ||
list.add(zone); | ||
zones.put(subdomain, list); | ||
} else { | ||
logger.warn("Found duplicate zone for subdomain:"+subdomain); | ||
duplicates.add(subdomain); | ||
zones.get(subdomain).add(zone); | ||
} | ||
} | ||
|
||
|
||
} |
21 changes: 21 additions & 0 deletions
21
.../org/cloudfoundry/identity/uaa/db/hsqldb/V2_7_3__Migrate_Zone_Subdomain_To_Lowercase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* ****************************************************************************** | ||
* * Cloud Foundry | ||
* * Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved. | ||
* * | ||
* * This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* * You may not use this product except in compliance with the License. | ||
* * | ||
* * This product includes a number of subcomponents with | ||
* * separate copyright notices and license terms. Your use of these | ||
* * subcomponents is subject to the terms and conditions of the | ||
* * subcomponent's license, as noted in the LICENSE file. | ||
* ****************************************************************************** | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.db.hsqldb; | ||
|
||
import org.cloudfoundry.identity.uaa.db.StoreSubDomainAsLowerCase_V2_7_3; | ||
|
||
public class V2_7_3__Migrate_Zone_Subdomain_To_Lowercase extends StoreSubDomainAsLowerCase_V2_7_3 { | ||
} |
21 changes: 21 additions & 0 deletions
21
...a/org/cloudfoundry/identity/uaa/db/mysql/V2_7_3__Migrate_Zone_Subdomain_To_Lowercase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* ****************************************************************************** | ||
* * Cloud Foundry | ||
* * Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved. | ||
* * | ||
* * This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* * You may not use this product except in compliance with the License. | ||
* * | ||
* * This product includes a number of subcomponents with | ||
* * separate copyright notices and license terms. Your use of these | ||
* * subcomponents is subject to the terms and conditions of the | ||
* * subcomponent's license, as noted in the LICENSE file. | ||
* ****************************************************************************** | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.db.mysql; | ||
|
||
import org.cloudfoundry.identity.uaa.db.StoreSubDomainAsLowerCase_V2_7_3; | ||
|
||
public class V2_7_3__Migrate_Zone_Subdomain_To_Lowercase extends StoreSubDomainAsLowerCase_V2_7_3 { | ||
} |
21 changes: 21 additions & 0 deletions
21
.../cloudfoundry/identity/uaa/db/postgresql/V2_7_3__Migrate_Zone_Subdomain_To_Lowercase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* ****************************************************************************** | ||
* * Cloud Foundry | ||
* * Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved. | ||
* * | ||
* * This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* * You may not use this product except in compliance with the License. | ||
* * | ||
* * This product includes a number of subcomponents with | ||
* * separate copyright notices and license terms. Your use of these | ||
* * subcomponents is subject to the terms and conditions of the | ||
* * subcomponent's license, as noted in the LICENSE file. | ||
* ****************************************************************************** | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.db.postgresql; | ||
|
||
import org.cloudfoundry.identity.uaa.db.StoreSubDomainAsLowerCase_V2_7_3; | ||
|
||
public class V2_7_3__Migrate_Zone_Subdomain_To_Lowercase extends StoreSubDomainAsLowerCase_V2_7_3 { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
...rc/test/java/org/cloudfoundry/identity/uaa/db/StoreSubDomainAsLowerCase_V2_7_3_Tests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
* ***************************************************************************** | ||
* Cloud Foundry | ||
* Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved. | ||
* This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this product except in compliance with the License. | ||
* | ||
* This product includes a number of subcomponents with | ||
* separate copyright notices and license terms. Your use of these | ||
* subcomponents is subject to the terms and conditions of the | ||
* subcomponent's license, as noted in the LICENSE file. | ||
* ***************************************************************************** | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.db; | ||
|
||
import org.cloudfoundry.identity.uaa.test.JdbcTestBase; | ||
import org.cloudfoundry.identity.uaa.zone.IdentityZone; | ||
import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; | ||
import org.cloudfoundry.identity.uaa.zone.JdbcIdentityZoneProvisioning; | ||
import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.springframework.dao.DuplicateKeyException; | ||
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; | ||
|
||
import java.sql.SQLException; | ||
import java.sql.Timestamp; | ||
import java.util.Arrays; | ||
import java.util.Date; | ||
import java.util.List; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assume.assumeTrue; | ||
|
||
public class StoreSubDomainAsLowerCase_V2_7_3_Tests extends JdbcTestBase { | ||
|
||
private IdentityZoneProvisioning provisioning; | ||
private StoreSubDomainAsLowerCase_V2_7_3 migration; | ||
private RandomValueStringGenerator generator; | ||
|
||
@Before | ||
public void setUpDuplicateZones() { | ||
provisioning = new JdbcIdentityZoneProvisioning(jdbcTemplate); | ||
migration = new StoreSubDomainAsLowerCase_V2_7_3(); | ||
generator = new RandomValueStringGenerator(6); | ||
} | ||
|
||
@Test | ||
public void ensure_that_subdomains_get_lower_cased() throws Exception { | ||
List<String> subdomains = Arrays.asList( | ||
"Zone1" + generator.generate(), | ||
"Zone2" + generator.generate(), | ||
"Zone3" + generator.generate(), | ||
"Zone4+generator.generate()" | ||
); | ||
|
||
for (String subdomain : subdomains) { | ||
IdentityZone zone = MultitenancyFixture.identityZone(subdomain, subdomain); | ||
IdentityZone created = provisioning.create(zone); | ||
assertEquals(subdomain.toLowerCase(), created.getSubdomain()); | ||
jdbcTemplate.update("UPDATE identity_zone SET subdomain = ? WHERE id = ?", subdomain, subdomain); | ||
assertEquals(subdomain, jdbcTemplate.queryForObject("SELECT subdomain FROM identity_zone where id = ?", String.class, subdomain)); | ||
} | ||
|
||
migration.migrate(jdbcTemplate); | ||
for (String subdomain : subdomains) { | ||
for (IdentityZone zone : | ||
Arrays.asList( | ||
provisioning.retrieve(subdomain), | ||
provisioning.retrieveBySubdomain(subdomain.toLowerCase()), | ||
provisioning.retrieveBySubdomain(subdomain) | ||
) | ||
) { | ||
assertNotNull(zone); | ||
assertEquals(subdomain, zone.getId()); | ||
assertEquals(subdomain.toLowerCase(), zone.getSubdomain()); | ||
} | ||
} | ||
} | ||
|
||
@Test | ||
public void test_duplicate_subdomains() throws Exception { | ||
check_db_is_case_sensitive(); | ||
List<String> ids = Arrays.asList( | ||
"id1"+generator.generate().toLowerCase(), | ||
"id2"+generator.generate().toLowerCase(), | ||
"id3"+generator.generate().toLowerCase(), | ||
"id4"+generator.generate().toLowerCase(), | ||
"id5"+generator.generate().toLowerCase() | ||
); | ||
List<String> subdomains = Arrays.asList( | ||
"domain1", | ||
"Domain1", | ||
"doMain1", | ||
"domain4"+generator.generate().toLowerCase(), | ||
"domain5"+generator.generate().toLowerCase() | ||
); | ||
for (int i=0; i<ids.size(); i++) { | ||
IdentityZone zone = MultitenancyFixture.identityZone(ids.get(i), subdomains.get(i)); | ||
zone.setSubdomain(subdomains.get(i)); //mixed case | ||
createIdentityZoneThroughSQL(zone); | ||
} | ||
IdentityZone lowercase = provisioning.retrieveBySubdomain("domain1"); | ||
IdentityZone mixedcase = provisioning.retrieveBySubdomain("Domain1"); | ||
assertEquals(lowercase.getId(), mixedcase.getId()); | ||
|
||
migration.migrate(jdbcTemplate); | ||
|
||
for (IdentityZone zone : provisioning.retrieveAll()) { | ||
//ensure we converted to lower case | ||
assertEquals(zone.getSubdomain().toLowerCase(), zone.getSubdomain()); | ||
} | ||
} | ||
|
||
|
||
public void check_db_is_case_sensitive() throws Exception { | ||
String usubdomain = "TEST_UPPER_" + generator.generate(); | ||
String lsubdomain = usubdomain.toLowerCase(); | ||
|
||
//check if the DB is case sensitive | ||
for (String subdomain : Arrays.asList(usubdomain, lsubdomain)) { | ||
try { | ||
IdentityZone identityZone = MultitenancyFixture.identityZone(subdomain+generator.generate(), subdomain); | ||
identityZone.setSubdomain(subdomain); | ||
createIdentityZoneThroughSQL(identityZone); | ||
} catch (SQLException x) { | ||
assumeTrue("DB is not case sensitive. No need for this test", false); | ||
} catch (DuplicateKeyException x) { | ||
assumeTrue("DB is not case sensitive. No need for this test", false); | ||
} | ||
} | ||
} | ||
|
||
protected void createIdentityZoneThroughSQL(IdentityZone identityZone) throws SQLException { | ||
jdbcTemplate.update(JdbcIdentityZoneProvisioning.CREATE_IDENTITY_ZONE_SQL, ps -> { | ||
ps.setString(1, identityZone.getId().trim()); | ||
ps.setInt(2, identityZone.getVersion()); | ||
ps.setTimestamp(3, new Timestamp(new Date().getTime())); | ||
ps.setTimestamp(4, new Timestamp(new Date().getTime())); | ||
ps.setString(5, identityZone.getName()); | ||
ps.setString(6, identityZone.getSubdomain()); | ||
ps.setString(7, identityZone.getDescription()); | ||
}); | ||
} | ||
} |
Oops, something went wrong.