blob: 613b2bb7688116abcd803cad26be597c9c092eaf [file] [log] [blame]
// 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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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<IdentifiedUser> 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";
}
}