Line data Source code
1 : // Copyright (C) 2019 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.git; 16 : 17 : import static java.util.stream.Collectors.toMap; 18 : 19 : import com.google.common.base.Preconditions; 20 : import com.google.common.collect.ImmutableList; 21 : import com.google.common.collect.Iterables; 22 : import com.google.gerrit.server.permissions.PermissionBackend; 23 : import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions; 24 : import com.google.gerrit.server.permissions.PermissionBackendException; 25 : import java.io.IOException; 26 : import java.util.ArrayList; 27 : import java.util.Collection; 28 : import java.util.Collections; 29 : import java.util.HashMap; 30 : import java.util.HashSet; 31 : import java.util.List; 32 : import java.util.Map; 33 : import java.util.Set; 34 : import java.util.function.Function; 35 : import java.util.stream.Collectors; 36 : import java.util.stream.Stream; 37 : import org.eclipse.jgit.annotations.NonNull; 38 : import org.eclipse.jgit.annotations.Nullable; 39 : import org.eclipse.jgit.lib.BatchRefUpdate; 40 : import org.eclipse.jgit.lib.ObjectId; 41 : import org.eclipse.jgit.lib.Ref; 42 : import org.eclipse.jgit.lib.RefRename; 43 : import org.eclipse.jgit.lib.RefUpdate; 44 : import org.eclipse.jgit.lib.Repository; 45 : 46 : /** 47 : * Wrapper around {@link DelegateRefDatabase} that filters all refs using {@link 48 : * com.google.gerrit.server.permissions.PermissionBackend}. 49 : */ 50 : public class PermissionAwareReadOnlyRefDatabase extends DelegateRefDatabase { 51 : 52 : private final PermissionBackend.ForProject forProject; 53 : 54 : PermissionAwareReadOnlyRefDatabase( 55 : Repository delegateRepository, PermissionBackend.ForProject forProject) { 56 134 : super(delegateRepository); 57 134 : this.forProject = forProject; 58 134 : } 59 : 60 : @Override 61 : public boolean isNameConflicting(String name) { 62 0 : throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only"); 63 : } 64 : 65 : @Override 66 : public Collection<String> getConflictingNames(String name) throws IOException { 67 0 : throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only"); 68 : } 69 : 70 : @Override 71 : public RefUpdate newUpdate(String name, boolean detach) { 72 0 : throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only"); 73 : } 74 : 75 : @Override 76 : public RefRename newRename(String fromName, String toName) { 77 0 : throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only"); 78 : } 79 : 80 : @Override 81 : public BatchRefUpdate newBatchUpdate() { 82 0 : throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only"); 83 : } 84 : 85 : @Nullable 86 : @Override 87 : public Ref exactRef(String name) throws IOException { 88 64 : Ref ref = getDelegate().getRefDatabase().exactRef(name); 89 64 : if (ref == null) { 90 1 : return null; 91 : } 92 : 93 : Collection<Ref> result; 94 : try { 95 64 : result = forProject.filter(ImmutableList.of(ref), getDelegate(), RefFilterOptions.defaults()); 96 0 : } catch (PermissionBackendException e) { 97 0 : if (e.getCause() instanceof IOException) { 98 0 : throw (IOException) e.getCause(); 99 : } 100 0 : throw new IOException(e); 101 64 : } 102 64 : if (result.isEmpty()) { 103 2 : return null; 104 : } 105 : 106 64 : Preconditions.checkState( 107 64 : result.size() == 1, "Only one element expected, but was: " + result.size()); 108 64 : return Iterables.getOnlyElement(result); 109 : } 110 : 111 : // WARNING: This method is deprecated in JGit's RefDatabase and it will be removed on master. 112 : // Do not add any logic here but rather enrich the getRefsByPrefix method below. 113 : @Override 114 : public Map<String, Ref> getRefs(String prefix) throws IOException { 115 0 : return buildPrefixRefMap(prefix, getRefsByPrefix(prefix)); 116 : } 117 : 118 : private Map<String, Ref> buildPrefixRefMap(String prefix, Collection<Ref> refs) { 119 0 : int prefixSlashPos = prefix.lastIndexOf('/') + 1; 120 0 : if (prefixSlashPos > 0) { 121 0 : return refs.stream() 122 0 : .collect( 123 0 : Collectors.toMap( 124 0 : (Ref ref) -> ref.getName().substring(prefixSlashPos), Function.identity())); 125 : } 126 : 127 0 : return refs.stream().collect(toMap(Ref::getName, r -> r)); 128 : } 129 : 130 : @Override 131 : public List<Ref> getRefsByPrefix(String prefix) throws IOException { 132 134 : List<Ref> refs = getDelegate().getRefDatabase().getRefsByPrefix(prefix); 133 134 : if (refs.isEmpty()) { 134 14 : return Collections.emptyList(); 135 : } 136 : 137 : Collection<Ref> result; 138 : try { 139 134 : result = forProject.filter(refs, getDelegate(), RefFilterOptions.defaults()); 140 0 : } catch (PermissionBackendException e) { 141 0 : throw new IOException("", e); 142 134 : } 143 134 : return result.stream().collect(Collectors.toList()); 144 : } 145 : 146 : @Override 147 : public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes) 148 : throws IOException { 149 0 : Stream<Ref> refs = getRefs(include).values().stream(); 150 0 : for (String exclude : excludes) { 151 0 : refs = refs.filter(r -> !r.getName().startsWith(exclude)); 152 0 : } 153 0 : return Collections.unmodifiableList(refs.collect(Collectors.toList())); 154 : } 155 : 156 : @Override 157 : public List<Ref> getRefsByPrefix(String... prefixes) throws IOException { 158 13 : List<Ref> result = new ArrayList<>(); 159 13 : for (String prefix : prefixes) { 160 13 : result.addAll(getRefsByPrefix(prefix)); 161 : } 162 13 : return Collections.unmodifiableList(result); 163 : } 164 : 165 : @Override 166 : @NonNull 167 : public Map<String, Ref> exactRef(String... refs) throws IOException { 168 0 : Map<String, Ref> result = new HashMap<>(refs.length); 169 0 : for (String name : refs) { 170 0 : Ref ref = exactRef(name); 171 0 : if (ref != null) { 172 0 : result.put(name, ref); 173 : } 174 : } 175 0 : return result; 176 : } 177 : 178 : @Override 179 : @Nullable 180 : public Ref firstExactRef(String... refs) throws IOException { 181 0 : for (String name : refs) { 182 0 : Ref ref = exactRef(name); 183 0 : if (ref != null) { 184 0 : return ref; 185 : } 186 : } 187 0 : return null; 188 : } 189 : 190 : @Override 191 : public List<Ref> getRefs() throws IOException { 192 134 : return getRefsByPrefix(ALL); 193 : } 194 : 195 : @Override 196 : @NonNull 197 : public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException { 198 1 : Set<Ref> unfiltered = super.getTipsWithSha1(id); 199 1 : Set<Ref> result = new HashSet<>(unfiltered.size()); 200 1 : for (Ref ref : unfiltered) { 201 0 : if (exactRef(ref.getName()) != null) { 202 0 : result.add(ref); 203 : } 204 0 : } 205 1 : return result; 206 : } 207 : 208 : @Override 209 : public boolean hasRefs() throws IOException { 210 0 : return !getRefs().isEmpty(); 211 : } 212 : }