blob: f8fcaddb61c5eec78ba3dbe89496209e485f237f [file] [log] [blame]
// Copyright (C) 2017 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.pgm.init;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.entities.GroupReference;
import com.google.gerrit.entities.InternalGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerIdProvider;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.db.AuditLogFormatter;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupDelta;
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.util.FS;
/**
* A database accessor for calls related to groups.
*
* <p>All calls which read or write group related details to the NoteDb <strong>during init</strong>
* are gathered here. For non-init cases, use {@code Groups} or {@code GroupsUpdate} instead.
*
* <p>All methods of this class refer to <em>internal</em> groups.
*/
public class GroupsOnInit {
private final InitFlags flags;
private final SitePaths site;
private final AllUsersName allUsers;
@Inject
public GroupsOnInit(InitFlags flags, SitePaths site, AllUsersNameOnInitProvider allUsers) {
this.flags = flags;
this.site = site;
this.allUsers = new AllUsersName(allUsers.get());
}
/**
* Returns the {@code AccountGroup} for the specified {@code GroupReference}.
*
* @param groupReference the {@code GroupReference} of the group
* @return the {@code InternalGroup} represented by the {@code GroupReference}
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the data in NoteDb is in an incorrect format
* @throws NoSuchGroupException if a group with such a name doesn't exist
*/
public InternalGroup getExistingGroup(GroupReference groupReference)
throws NoSuchGroupException, IOException, ConfigInvalidException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) {
AccountGroup.UUID groupUuid = groupReference.getUUID();
GroupConfig groupConfig = GroupConfig.loadForGroup(allUsers, allUsersRepo, groupUuid);
return groupConfig
.getLoadedGroup()
.orElseThrow(() -> new NoSuchGroupException(groupReference.getUUID()));
}
}
throw new NoSuchGroupException(groupReference.getUUID());
}
/**
* Returns {@code GroupReference}s for all internal groups.
*
* @return a stream of the {@code GroupReference}s of all internal groups
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the data in NoteDb is in an incorrect format
*/
public Stream<GroupReference> getAllGroupReferences() throws IOException, ConfigInvalidException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) {
return GroupNameNotes.loadAllGroups(allUsersRepo).stream();
}
}
return Stream.empty();
}
/**
* Adds an account as member to a group. The account is only added as a new member if it isn't
* already a member of the group.
*
* <p><strong>Note</strong>: This method doesn't check whether the account exists! It also doesn't
* update the account index!
*
* @param groupUuid the UUID of the group
* @param account the account to add
* @throws NoSuchGroupException if the specified group doesn't exist
*/
public void addGroupMember(AccountGroup.UUID groupUuid, Account account)
throws NoSuchGroupException, IOException, ConfigInvalidException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository repository = new FileRepository(allUsersRepoPath)) {
addGroupMemberInNoteDb(repository, groupUuid, account);
}
}
}
private void addGroupMemberInNoteDb(
Repository repository, AccountGroup.UUID groupUuid, Account account)
throws IOException, ConfigInvalidException, NoSuchGroupException {
GroupConfig groupConfig = GroupConfig.loadForGroup(allUsers, repository, groupUuid);
InternalGroup group =
groupConfig.getLoadedGroup().orElseThrow(() -> new NoSuchGroupException(groupUuid));
GroupDelta groupDelta = getMemberAdditionDelta(account);
AuditLogFormatter auditLogFormatter = getAuditLogFormatter(account);
groupConfig.setGroupDelta(groupDelta, auditLogFormatter);
commit(repository, groupConfig, group.getCreatedOn());
}
@Nullable
private File getPathToAllUsersRepository() {
Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath"));
checkArgument(basePath != null, "gerrit.basePath must be configured");
return RepositoryCache.FileKey.resolve(basePath.resolve(allUsers.get()).toFile(), FS.DETECTED);
}
private static GroupDelta getMemberAdditionDelta(Account account) {
return GroupDelta.builder()
.setMemberModification(members -> Sets.union(members, ImmutableSet.of(account.id())))
.build();
}
private AuditLogFormatter getAuditLogFormatter(Account account)
throws IOException, ConfigInvalidException {
String serverId = new GerritServerIdProvider(flags.cfg, site).get();
return AuditLogFormatter.createBackedBy(ImmutableSet.of(account), ImmutableSet.of(), serverId);
}
private void commit(Repository repository, GroupConfig groupConfig, Instant groupCreatedOn)
throws IOException {
PersonIdent personIdent =
new PersonIdent(new GerritPersonIdentProvider(flags.cfg).get(), groupCreatedOn);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(repository, personIdent)) {
groupConfig.commit(metaDataUpdate);
}
}
private MetaDataUpdate createMetaDataUpdate(Repository repository, PersonIdent personIdent) {
MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsers, repository);
metaDataUpdate.getCommitBuilder().setAuthor(personIdent);
metaDataUpdate.getCommitBuilder().setCommitter(personIdent);
return metaDataUpdate;
}
}