LCOV - code coverage report
Current view: top level - server/account/externalids - ExternalIdCaseSensitivityMigrator.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 53 55 96.4 %
Date: 2022-11-19 15:00:39 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2021 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.server.account.externalids;
      16             : 
      17             : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
      18             : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
      19             : 
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.exceptions.DuplicateKeyException;
      22             : import com.google.gerrit.server.config.AllUsersName;
      23             : import com.google.gerrit.server.git.GitRepositoryManager;
      24             : import com.google.gerrit.server.git.meta.MetaDataUpdate;
      25             : import com.google.inject.AbstractModule;
      26             : import com.google.inject.Inject;
      27             : import com.google.inject.Provider;
      28             : import com.google.inject.assistedinject.Assisted;
      29             : import com.google.inject.assistedinject.FactoryModuleBuilder;
      30             : import java.io.IOException;
      31             : import java.util.Collection;
      32             : import java.util.Collections;
      33             : import org.eclipse.jgit.errors.ConfigInvalidException;
      34             : import org.eclipse.jgit.errors.RepositoryNotFoundException;
      35             : import org.eclipse.jgit.lib.Repository;
      36             : 
      37             : public class ExternalIdCaseSensitivityMigrator {
      38             : 
      39         138 :   public static class ExternalIdCaseSensitivityMigratorModule extends AbstractModule {
      40             :     @Override
      41             :     public void configure() {
      42         138 :       install(new FactoryModuleBuilder().build(ExternalIdCaseSensitivityMigrator.Factory.class));
      43         138 :     }
      44             :   }
      45             : 
      46           2 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      47             : 
      48             :   public interface Factory {
      49             :     ExternalIdCaseSensitivityMigrator create(
      50             :         @Assisted("isUserNameCaseInsensitive") Boolean isUserNameCaseInsensitive,
      51             :         @Assisted("dryRun") Boolean dryRun);
      52             :   }
      53             : 
      54             :   private GitRepositoryManager repoManager;
      55             :   private AllUsersName allUsersName;
      56             :   private Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory;
      57             :   private ExternalIdNotes.FactoryNoReindex externalIdNotesFactory;
      58             : 
      59             :   private ExternalIdFactory externalIdFactory;
      60             :   private Boolean isUserNameCaseInsensitive;
      61             :   private Boolean dryRun;
      62             : 
      63             :   @Inject
      64             :   public ExternalIdCaseSensitivityMigrator(
      65             :       GitRepositoryManager repoManager,
      66             :       AllUsersName allUsersName,
      67             :       Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory,
      68             :       ExternalIdNotes.FactoryNoReindex externalIdNotesFactory,
      69             :       ExternalIdFactory externalIdFactory,
      70             :       @Assisted("isUserNameCaseInsensitive") Boolean isUserNameCaseInsensitive,
      71           2 :       @Assisted("dryRun") Boolean dryRun) {
      72           2 :     this.repoManager = repoManager;
      73           2 :     this.allUsersName = allUsersName;
      74           2 :     this.metaDataUpdateServerFactory = metaDataUpdateServerFactory;
      75           2 :     this.externalIdNotesFactory = externalIdNotesFactory;
      76           2 :     this.externalIdFactory = externalIdFactory;
      77             : 
      78           2 :     this.isUserNameCaseInsensitive = isUserNameCaseInsensitive;
      79           2 :     this.dryRun = dryRun;
      80           2 :   }
      81             : 
      82             :   private void recomputeExternalIdNoteId(ExternalIdNotes extIdNotes, ExternalId extId)
      83             :       throws DuplicateKeyException, IOException {
      84           2 :     if (extId.isScheme(SCHEME_GERRIT) || extId.isScheme(SCHEME_USERNAME)) {
      85           2 :       ExternalIdKeyFactory keyFactory =
      86             :           new ExternalIdKeyFactory(
      87           2 :               new ExternalIdKeyFactory.Config() {
      88             :                 @Override
      89             :                 public boolean isUserNameCaseInsensitive() {
      90           2 :                   return isUserNameCaseInsensitive;
      91             :                 }
      92             :               });
      93           2 :       ExternalId.Key updatedKey = keyFactory.create(extId.key().scheme(), extId.key().id());
      94           2 :       ExternalId.Key oldKey =
      95           2 :           keyFactory.create(extId.key().scheme(), extId.key().id(), !isUserNameCaseInsensitive);
      96           2 :       if (!oldKey.sha1().getName().equals(updatedKey.sha1().getName())
      97           2 :           && !extId.key().sha1().getName().equals(updatedKey.sha1().getName())) {
      98           2 :         logger.atInfo().log("Converting note name of external ID: %s", oldKey);
      99           2 :         ExternalId updatedExtId =
     100           2 :             externalIdFactory.create(
     101           2 :                 updatedKey, extId.accountId(), extId.email(), extId.password(), extId.blobId());
     102           2 :         ExternalId oldExtId =
     103           2 :             externalIdFactory.create(
     104           2 :                 oldKey, extId.accountId(), extId.email(), extId.password(), extId.blobId());
     105           2 :         extIdNotes.replace(
     106           2 :             Collections.singleton(oldExtId),
     107           2 :             Collections.singleton(updatedExtId),
     108           2 :             (externalId) -> externalId.key().sha1());
     109             :       }
     110             :     }
     111           2 :   }
     112             : 
     113             :   public void migrate(Collection<ExternalId> todo, Runnable monitor)
     114             :       throws RepositoryNotFoundException, IOException, ConfigInvalidException {
     115           2 :     try (Repository repo = repoManager.openRepository(allUsersName)) {
     116           2 :       ExternalIdNotes extIdNotes = externalIdNotesFactory.load(repo);
     117           2 :       for (ExternalId extId : todo) {
     118           2 :         recomputeExternalIdNoteId(extIdNotes, extId);
     119           2 :         monitor.run();
     120           2 :       }
     121           2 :       if (!dryRun) {
     122           2 :         try (MetaDataUpdate metaDataUpdate =
     123           2 :             metaDataUpdateServerFactory.get().create(allUsersName)) {
     124           2 :           metaDataUpdate.setMessage(
     125           2 :               String.format(
     126             :                   "Migration to case %ssensitive usernames",
     127           2 :                   isUserNameCaseInsensitive ? "" : "in"));
     128           2 :           extIdNotes.commit(metaDataUpdate);
     129           0 :         } catch (Exception e) {
     130           0 :           logger.atSevere().withCause(e).log("%s", e.getMessage());
     131           2 :         }
     132             :       }
     133           2 :     } catch (DuplicateExternalIdKeyException e) {
     134           2 :       logger.atSevere().withCause(e).log("%s", e.getMessage());
     135           2 :       throw e;
     136           2 :     }
     137           2 :   }
     138             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750