Line data Source code
1 : // Copyright (C) 2017 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.init; 16 : 17 : import static com.google.common.base.Preconditions.checkArgument; 18 : 19 : import com.google.common.collect.ImmutableSet; 20 : import com.google.common.collect.Sets; 21 : import com.google.gerrit.common.Nullable; 22 : import com.google.gerrit.entities.Account; 23 : import com.google.gerrit.entities.AccountGroup; 24 : import com.google.gerrit.entities.GroupReference; 25 : import com.google.gerrit.entities.InternalGroup; 26 : import com.google.gerrit.exceptions.NoSuchGroupException; 27 : import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider; 28 : import com.google.gerrit.pgm.init.api.InitFlags; 29 : import com.google.gerrit.server.GerritPersonIdentProvider; 30 : import com.google.gerrit.server.config.AllUsersName; 31 : import com.google.gerrit.server.config.GerritServerIdProvider; 32 : import com.google.gerrit.server.config.SitePaths; 33 : import com.google.gerrit.server.extensions.events.GitReferenceUpdated; 34 : import com.google.gerrit.server.git.meta.MetaDataUpdate; 35 : import com.google.gerrit.server.group.db.AuditLogFormatter; 36 : import com.google.gerrit.server.group.db.GroupConfig; 37 : import com.google.gerrit.server.group.db.GroupDelta; 38 : import com.google.gerrit.server.group.db.GroupNameNotes; 39 : import com.google.inject.Inject; 40 : import java.io.File; 41 : import java.io.IOException; 42 : import java.nio.file.Path; 43 : import java.time.Instant; 44 : import java.util.stream.Stream; 45 : import org.eclipse.jgit.errors.ConfigInvalidException; 46 : import org.eclipse.jgit.internal.storage.file.FileRepository; 47 : import org.eclipse.jgit.lib.PersonIdent; 48 : import org.eclipse.jgit.lib.Repository; 49 : import org.eclipse.jgit.lib.RepositoryCache; 50 : import org.eclipse.jgit.util.FS; 51 : 52 : /** 53 : * A database accessor for calls related to groups. 54 : * 55 : * <p>All calls which read or write group related details to the NoteDb <strong>during init</strong> 56 : * are gathered here. For non-init cases, use {@code Groups} or {@code GroupsUpdate} instead. 57 : * 58 : * <p>All methods of this class refer to <em>internal</em> groups. 59 : */ 60 : public class GroupsOnInit { 61 : 62 : private final InitFlags flags; 63 : private final SitePaths site; 64 : private final AllUsersName allUsers; 65 : 66 : @Inject 67 15 : public GroupsOnInit(InitFlags flags, SitePaths site, AllUsersNameOnInitProvider allUsers) { 68 15 : this.flags = flags; 69 15 : this.site = site; 70 15 : this.allUsers = new AllUsersName(allUsers.get()); 71 15 : } 72 : 73 : /** 74 : * Returns the {@code AccountGroup} for the specified {@code GroupReference}. 75 : * 76 : * @param groupReference the {@code GroupReference} of the group 77 : * @return the {@code InternalGroup} represented by the {@code GroupReference} 78 : * @throws IOException if an error occurs while reading from NoteDb 79 : * @throws ConfigInvalidException if the data in NoteDb is in an incorrect format 80 : * @throws NoSuchGroupException if a group with such a name doesn't exist 81 : */ 82 : public InternalGroup getExistingGroup(GroupReference groupReference) 83 : throws NoSuchGroupException, IOException, ConfigInvalidException { 84 0 : File allUsersRepoPath = getPathToAllUsersRepository(); 85 0 : if (allUsersRepoPath != null) { 86 0 : try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) { 87 0 : AccountGroup.UUID groupUuid = groupReference.getUUID(); 88 0 : GroupConfig groupConfig = GroupConfig.loadForGroup(allUsers, allUsersRepo, groupUuid); 89 0 : return groupConfig 90 0 : .getLoadedGroup() 91 0 : .orElseThrow(() -> new NoSuchGroupException(groupReference.getUUID())); 92 : } 93 : } 94 0 : throw new NoSuchGroupException(groupReference.getUUID()); 95 : } 96 : 97 : /** 98 : * Returns {@code GroupReference}s for all internal groups. 99 : * 100 : * @return a stream of the {@code GroupReference}s of all internal groups 101 : * @throws IOException if an error occurs while reading from NoteDb 102 : * @throws ConfigInvalidException if the data in NoteDb is in an incorrect format 103 : */ 104 : public Stream<GroupReference> getAllGroupReferences() throws IOException, ConfigInvalidException { 105 0 : File allUsersRepoPath = getPathToAllUsersRepository(); 106 0 : if (allUsersRepoPath != null) { 107 0 : try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) { 108 0 : return GroupNameNotes.loadAllGroups(allUsersRepo).stream(); 109 : } 110 : } 111 0 : return Stream.empty(); 112 : } 113 : 114 : /** 115 : * Adds an account as member to a group. The account is only added as a new member if it isn't 116 : * already a member of the group. 117 : * 118 : * <p><strong>Note</strong>: This method doesn't check whether the account exists! It also doesn't 119 : * update the account index! 120 : * 121 : * @param groupUuid the UUID of the group 122 : * @param account the account to add 123 : * @throws NoSuchGroupException if the specified group doesn't exist 124 : */ 125 : public void addGroupMember(AccountGroup.UUID groupUuid, Account account) 126 : throws NoSuchGroupException, IOException, ConfigInvalidException { 127 0 : File allUsersRepoPath = getPathToAllUsersRepository(); 128 0 : if (allUsersRepoPath != null) { 129 0 : try (Repository repository = new FileRepository(allUsersRepoPath)) { 130 0 : addGroupMemberInNoteDb(repository, groupUuid, account); 131 : } 132 : } 133 0 : } 134 : 135 : private void addGroupMemberInNoteDb( 136 : Repository repository, AccountGroup.UUID groupUuid, Account account) 137 : throws IOException, ConfigInvalidException, NoSuchGroupException { 138 0 : GroupConfig groupConfig = GroupConfig.loadForGroup(allUsers, repository, groupUuid); 139 0 : InternalGroup group = 140 0 : groupConfig.getLoadedGroup().orElseThrow(() -> new NoSuchGroupException(groupUuid)); 141 : 142 0 : GroupDelta groupDelta = getMemberAdditionDelta(account); 143 0 : AuditLogFormatter auditLogFormatter = getAuditLogFormatter(account); 144 0 : groupConfig.setGroupDelta(groupDelta, auditLogFormatter); 145 : 146 0 : commit(repository, groupConfig, group.getCreatedOn()); 147 0 : } 148 : 149 : @Nullable 150 : private File getPathToAllUsersRepository() { 151 0 : Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath")); 152 0 : checkArgument(basePath != null, "gerrit.basePath must be configured"); 153 0 : return RepositoryCache.FileKey.resolve(basePath.resolve(allUsers.get()).toFile(), FS.DETECTED); 154 : } 155 : 156 : private static GroupDelta getMemberAdditionDelta(Account account) { 157 0 : return GroupDelta.builder() 158 0 : .setMemberModification(members -> Sets.union(members, ImmutableSet.of(account.id()))) 159 0 : .build(); 160 : } 161 : 162 : private AuditLogFormatter getAuditLogFormatter(Account account) 163 : throws IOException, ConfigInvalidException { 164 0 : String serverId = new GerritServerIdProvider(flags.cfg, site).get(); 165 0 : return AuditLogFormatter.createBackedBy(ImmutableSet.of(account), ImmutableSet.of(), serverId); 166 : } 167 : 168 : private void commit(Repository repository, GroupConfig groupConfig, Instant groupCreatedOn) 169 : throws IOException { 170 0 : PersonIdent personIdent = 171 0 : new PersonIdent(new GerritPersonIdentProvider(flags.cfg).get(), groupCreatedOn); 172 0 : try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(repository, personIdent)) { 173 0 : groupConfig.commit(metaDataUpdate); 174 : } 175 0 : } 176 : 177 : private MetaDataUpdate createMetaDataUpdate(Repository repository, PersonIdent personIdent) { 178 0 : MetaDataUpdate metaDataUpdate = 179 : new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsers, repository); 180 0 : metaDataUpdate.getCommitBuilder().setAuthor(personIdent); 181 0 : metaDataUpdate.getCommitBuilder().setCommitter(personIdent); 182 0 : return metaDataUpdate; 183 : } 184 : }