Line data Source code
1 : // Copyright (C) 2011 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.pgm; 16 : 17 : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT; 18 : 19 : import com.google.gerrit.exceptions.DuplicateKeyException; 20 : import com.google.gerrit.extensions.config.FactoryModule; 21 : import com.google.gerrit.lifecycle.LifecycleManager; 22 : import com.google.gerrit.pgm.util.SiteProgram; 23 : import com.google.gerrit.server.account.externalids.DisabledExternalIdCache; 24 : import com.google.gerrit.server.account.externalids.ExternalId; 25 : import com.google.gerrit.server.account.externalids.ExternalIdFactory; 26 : import com.google.gerrit.server.account.externalids.ExternalIdNotes; 27 : import com.google.gerrit.server.account.externalids.ExternalIds; 28 : import com.google.gerrit.server.config.AllUsersName; 29 : import com.google.gerrit.server.extensions.events.GitReferenceUpdated; 30 : import com.google.gerrit.server.git.GitRepositoryManager; 31 : import com.google.gerrit.server.git.meta.MetaDataUpdate; 32 : import com.google.gerrit.server.index.account.AccountSchemaDefinitions; 33 : import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck; 34 : import com.google.inject.Inject; 35 : import com.google.inject.Injector; 36 : import com.google.inject.Provider; 37 : import java.io.IOException; 38 : import java.util.Collection; 39 : import java.util.Locale; 40 : import java.util.Optional; 41 : import org.apache.commons.lang3.StringUtils; 42 : import org.eclipse.jgit.errors.ConfigInvalidException; 43 : import org.eclipse.jgit.lib.ProgressMonitor; 44 : import org.eclipse.jgit.lib.Repository; 45 : import org.eclipse.jgit.lib.TextProgressMonitor; 46 : 47 : /** Converts the local username for all accounts to lower case */ 48 0 : public class LocalUsernamesToLowerCase extends SiteProgram { 49 0 : private final LifecycleManager manager = new LifecycleManager(); 50 0 : private final TextProgressMonitor monitor = new TextProgressMonitor(); 51 : 52 : @Inject private GitRepositoryManager repoManager; 53 : @Inject private AllUsersName allUsersName; 54 : @Inject private Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory; 55 : @Inject private ExternalIdNotes.FactoryNoReindex externalIdNotesFactory; 56 : @Inject private ExternalIdFactory externalIdFactory; 57 : @Inject private ExternalIds externalIds; 58 : 59 : @Override 60 : public int run() throws Exception { 61 0 : Injector dbInjector = createDbInjector(); 62 0 : manager.add(dbInjector, dbInjector.createChildInjector(NoteDbSchemaVersionCheck.module())); 63 0 : manager.start(); 64 0 : dbInjector 65 0 : .createChildInjector( 66 0 : new FactoryModule() { 67 : @Override 68 : protected void configure() { 69 0 : bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED); 70 0 : factory(MetaDataUpdate.InternalFactory.class); 71 : 72 : // The LocalUsernamesToLowerCase program needs to access all external IDs only 73 : // once to update them. After the update they are not accessed again. Hence the 74 : // LocalUsernamesToLowerCase program doesn't benefit from caching external IDs and 75 : // the external ID cache can be disabled. 76 0 : install(DisabledExternalIdCache.module()); 77 0 : } 78 : }) 79 0 : .injectMembers(this); 80 : 81 0 : Collection<ExternalId> todo = externalIds.all(); 82 0 : monitor.beginTask("Converting local usernames", todo.size()); 83 : 84 0 : try (Repository repo = repoManager.openRepository(allUsersName)) { 85 0 : ExternalIdNotes extIdNotes = externalIdNotesFactory.load(repo); 86 0 : for (ExternalId extId : todo) { 87 0 : convertLocalUserToLowerCase(extIdNotes, extId); 88 0 : monitor.update(1); 89 0 : } 90 0 : try (MetaDataUpdate metaDataUpdate = metaDataUpdateServerFactory.get().create(allUsersName)) { 91 0 : metaDataUpdate.setMessage("Convert local usernames to lower case"); 92 0 : extIdNotes.commit(metaDataUpdate); 93 : } 94 : } 95 : 96 0 : monitor.endTask(); 97 : 98 0 : int exitCode = reindexAccounts(); 99 0 : manager.stop(); 100 0 : return exitCode; 101 : } 102 : 103 : private void convertLocalUserToLowerCase(ExternalIdNotes extIdNotes, ExternalId extId) 104 : throws DuplicateKeyException, IOException { 105 0 : if (extId.isScheme(SCHEME_GERRIT)) { 106 0 : String localUser = extId.key().id(); 107 0 : String localUserLowerCase = localUser.toLowerCase(Locale.US); 108 0 : if (!localUser.equals(localUserLowerCase)) { 109 0 : ExternalId extIdLowerCase = 110 0 : externalIdFactory.create( 111 : SCHEME_GERRIT, 112 : localUserLowerCase, 113 0 : extId.accountId(), 114 0 : extId.email(), 115 0 : extId.password()); 116 0 : replaceIfNotExists(extIdNotes, extId, extIdLowerCase); 117 : } 118 : } 119 0 : } 120 : 121 : private void replaceIfNotExists( 122 : ExternalIdNotes extIdNotes, ExternalId extId, ExternalId extIdLowerCase) throws IOException { 123 : try { 124 0 : Optional<ExternalId> existingExternalId = 125 : extIdNotes 126 0 : .get(extIdLowerCase.key()) 127 0 : .filter(eid -> eid.accountId().equals(extIdLowerCase.accountId())) 128 0 : .filter(eid -> StringUtils.equalsIgnoreCase(eid.email(), extId.email())) 129 0 : .filter(eid -> StringUtils.equalsIgnoreCase(eid.password(), extId.password())); 130 0 : if (existingExternalId.isPresent()) { 131 0 : System.err.println( 132 : "WARNING: external-id " 133 : + extIdLowerCase 134 : + " already exists with the same account-id " 135 0 : + extId.accountId() 136 : + " :" 137 : + "removing the duplicate external-id " 138 0 : + extId.key()); 139 0 : extIdNotes.delete(extId); 140 : } else { 141 0 : extIdNotes.replace(extId, extIdLowerCase); 142 : } 143 0 : } catch (ConfigInvalidException e) { 144 0 : throw new IOException( 145 : "Unable to parse external id definition when looking for current external-id " 146 : + extIdLowerCase, 147 : e); 148 0 : } 149 0 : } 150 : 151 : private int reindexAccounts() throws Exception { 152 0 : monitor.beginTask("Reindex accounts", ProgressMonitor.UNKNOWN); 153 0 : String[] reindexArgs = { 154 0 : "--site-path", getSitePath().toString(), "--index", AccountSchemaDefinitions.NAME 155 : }; 156 0 : System.out.println("Migration complete, reindexing accounts with:"); 157 0 : System.out.println(" reindex " + String.join(" ", reindexArgs)); 158 0 : Reindex reindexPgm = new Reindex(); 159 0 : int exitCode = reindexPgm.main(reindexArgs); 160 0 : monitor.endTask(); 161 0 : return exitCode; 162 : } 163 : }