com.google.gerrit.server.RefLogIdentityProvider Maven / Gradle / Ivy
// Copyright (C) 2023 The Android Open Source Project
//
// 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.
package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkState;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.entities.Account;
import java.time.Instant;
import java.time.ZoneId;
import org.eclipse.jgit.lib.PersonIdent;
/**
 * Extension point that allows to control which identity should be recorded in the reflog for ref
 * updates done by a user or done on behalf of a user.
 */
public interface RefLogIdentityProvider {
  /**
   * Creates a {@link PersonIdent} for the given user that should be used as the user identity in
   * the reflog for ref updates done by this user or done on behalf of this user.
   *
   * The returned {@link PersonIdent} is created with the current timestamp and the system
   * default timezone.
   *
   * @param user the user for which a reflog identity should be created
   */
  default PersonIdent newRefLogIdent(IdentifiedUser user) {
    return newRefLogIdent(user, Instant.now(), ZoneId.systemDefault());
  }
  /**
   * Creates a {@link PersonIdent} for the given user that should be used as the user identity in
   * the reflog for ref updates done by this user or done on behalf of this user.
   *
   * @param user the user for which a reflog identity should be created
   * @param when the timestamp that should be used to create the {@link PersonIdent}
   * @param zoneId the zone ID identifying the timezone that should be used to create the {@link
   *     PersonIdent}
   */
  PersonIdent newRefLogIdent(IdentifiedUser user, Instant when, ZoneId zoneId);
  /**
   * Creates a {@link PersonIdent} for the given users that should be used as the user identity in
   * the reflog for ref updates done by these users or done on behalf of these users.
   *
   * 
Usually ref updates are done by a single user or on behalf of a single user, but with {@link
   * com.google.gerrit.server.update.BatchUpdate} it's possible that updates of different users are
   * batched together into a single ref update.
   *
   * 
If a single user is provided or all provided users reference the same account a reflog
   * identity for that user/account is created and returned.
   *
   * 
If multiple users (that reference different accounts) are provided a shared reflog identity
   * is created and returned. The shared reflog identity lists all involved accounts. How the shared
   * reflog identity looks like doesn't matter much, as long as it's not the reflog identity of a
   * real user (e.g. if impersonated updates of multiple users are batched together it must not be
   * the reflog identity of the real user).
   *
   * @param users the users for which a reflog identity should be created
   * @param when the timestamp that should be used to create the {@link PersonIdent}
   * @param zoneId the zone ID identifying the timezone that should be used to create the {@link
   *     PersonIdent}
   */
  default PersonIdent newRefLogIdent(
      ImmutableList users, Instant when, ZoneId zoneId) {
    checkState(!users.isEmpty(), "expected at least one user");
    // If it's a single user create a reflog ident for that user.
    // Use IdentifiedUser.newReflogIdent(Instant, ZoneId) rather than invoking
    // #newRefLogIdent(IdentifiedUser, Instant ZoneId) directly, so that we can benefit from the
    // reflog ident caching in IdentifiedUser.
    if (users.size() == 1 || users.stream().allMatch(user -> user.hasSameAccountId(users.get(0)))) {
      return users.get(0).newRefLogIdent(when, zoneId);
    }
    // Multiple users (for different accounts) have been provided. Create a shared relog identity
    // that lists all involved accounts.
    String accounts =
        users.stream()
            .map(IdentifiedUser::getAccountId)
            .map(Account.Id::get)
            .distinct()
            .sorted()
            .map(id -> "account-" + id)
            .collect(joining("|"));
    return new PersonIdent(
        accounts, String.format("%s@%s", accounts, getDefaultDomain()), when, zoneId);
  }
  /**
   * Returns the default domain for constructing email addresses if guessing the correct host is not
   * possible.
   */
  default String getDefaultDomain() {
    return "unknown";
  }
}