1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl.filter;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.BufferedReader;
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.io.UncheckedIOException;
29 import java.nio.charset.StandardCharsets;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.concurrent.ConcurrentHashMap;
37
38 import org.eclipse.aether.RepositorySystemSession;
39 import org.eclipse.aether.artifact.Artifact;
40 import org.eclipse.aether.metadata.Metadata;
41 import org.eclipse.aether.repository.RemoteRepository;
42 import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
43 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
44 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
45 import org.eclipse.aether.transfer.NoRepositoryLayoutException;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import static java.util.Objects.requireNonNull;
50 import static java.util.stream.Collectors.toList;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 @Singleton
76 @Named(PrefixesRemoteRepositoryFilterSource.NAME)
77 public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepositoryFilterSourceSupport {
78 public static final String NAME = "prefixes";
79
80 static final String PREFIXES_FILE_PREFIX = "prefixes-";
81
82 static final String PREFIXES_FILE_SUFFIX = ".txt";
83
84 private static final Logger LOGGER = LoggerFactory.getLogger(PrefixesRemoteRepositoryFilterSource.class);
85
86 private final RepositoryLayoutProvider repositoryLayoutProvider;
87
88 private final ConcurrentHashMap<RemoteRepository, Node> prefixes;
89
90 private final ConcurrentHashMap<RemoteRepository, RepositoryLayout> layouts;
91
92 @Inject
93 public PrefixesRemoteRepositoryFilterSource(RepositoryLayoutProvider repositoryLayoutProvider) {
94 super(NAME);
95 this.repositoryLayoutProvider = requireNonNull(repositoryLayoutProvider);
96 this.prefixes = new ConcurrentHashMap<>();
97 this.layouts = new ConcurrentHashMap<>();
98 }
99
100 @Override
101 public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession session) {
102 if (isEnabled(session)) {
103 return new PrefixesFilter(session, getBasedir(session, false));
104 }
105 return null;
106 }
107
108
109
110
111
112
113 private RepositoryLayout cacheLayout(RepositorySystemSession session, RemoteRepository remoteRepository) {
114 return layouts.computeIfAbsent(remoteRepository, r -> {
115 try {
116 return repositoryLayoutProvider.newRepositoryLayout(session, remoteRepository);
117 } catch (NoRepositoryLayoutException e) {
118 return null;
119 }
120 });
121 }
122
123
124
125
126 private Node cacheNode(Path basedir, RemoteRepository remoteRepository) {
127 return prefixes.computeIfAbsent(remoteRepository, r -> loadRepositoryPrefixes(basedir, remoteRepository));
128 }
129
130
131
132
133 private Node loadRepositoryPrefixes(Path baseDir, RemoteRepository remoteRepository) {
134 Path filePath = baseDir.resolve(PREFIXES_FILE_PREFIX + remoteRepository.getId() + PREFIXES_FILE_SUFFIX);
135 if (Files.isReadable(filePath)) {
136 try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
137 LOGGER.debug(
138 "Loading prefixes for remote repository {} from file '{}'", remoteRepository.getId(), filePath);
139 Node root = new Node("");
140 String prefix;
141 int lines = 0;
142 while ((prefix = reader.readLine()) != null) {
143 if (!prefix.startsWith("#") && !prefix.trim().isEmpty()) {
144 lines++;
145 Node currentNode = root;
146 for (String element : elementsOf(prefix)) {
147 currentNode = currentNode.addSibling(element);
148 }
149 }
150 }
151 LOGGER.info("Loaded {} prefixes for remote repository {}", lines, remoteRepository.getId());
152 return root;
153 } catch (FileNotFoundException e) {
154
155 } catch (IOException e) {
156 throw new UncheckedIOException(e);
157 }
158 }
159 LOGGER.debug("Prefix file for remote repository {} not found at '{}'", remoteRepository, filePath);
160 return NOT_PRESENT_NODE;
161 }
162
163 private class PrefixesFilter implements RemoteRepositoryFilter {
164 private final RepositorySystemSession session;
165
166 private final Path basedir;
167
168 private PrefixesFilter(RepositorySystemSession session, Path basedir) {
169 this.session = session;
170 this.basedir = basedir;
171 }
172
173 @Override
174 public Result acceptArtifact(RemoteRepository remoteRepository, Artifact artifact) {
175 RepositoryLayout repositoryLayout = cacheLayout(session, remoteRepository);
176 if (repositoryLayout == null) {
177 return new SimpleResult(true, "Unsupported layout: " + remoteRepository);
178 }
179 return acceptPrefix(
180 remoteRepository,
181 repositoryLayout.getLocation(artifact, false).getPath());
182 }
183
184 @Override
185 public Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadata) {
186 RepositoryLayout repositoryLayout = cacheLayout(session, remoteRepository);
187 if (repositoryLayout == null) {
188 return new SimpleResult(true, "Unsupported layout: " + remoteRepository);
189 }
190 return acceptPrefix(
191 remoteRepository,
192 repositoryLayout.getLocation(metadata, false).getPath());
193 }
194
195 private Result acceptPrefix(RemoteRepository remoteRepository, String path) {
196 Node root = cacheNode(basedir, remoteRepository);
197 if (NOT_PRESENT_NODE == root) {
198 return NOT_PRESENT_RESULT;
199 }
200 List<String> prefix = new ArrayList<>();
201 final List<String> pathElements = elementsOf(path);
202 Node currentNode = root;
203 for (String pathElement : pathElements) {
204 prefix.add(pathElement);
205 currentNode = currentNode.getSibling(pathElement);
206 if (currentNode == null || currentNode.isLeaf()) {
207 break;
208 }
209 }
210 if (currentNode != null && currentNode.isLeaf()) {
211 return new SimpleResult(
212 true, "Prefix " + String.join("/", prefix) + " allowed from " + remoteRepository);
213 } else {
214 return new SimpleResult(
215 false, "Prefix " + String.join("/", prefix) + " NOT allowed from " + remoteRepository);
216 }
217 }
218 }
219
220 private static final Node NOT_PRESENT_NODE = new Node("not-present-node");
221
222 private static final RemoteRepositoryFilter.Result NOT_PRESENT_RESULT =
223 new SimpleResult(true, "Prefix file not present");
224
225 private static class Node {
226 private final String name;
227
228 private final HashMap<String, Node> siblings;
229
230 private Node(String name) {
231 this.name = name;
232 this.siblings = new HashMap<>();
233 }
234
235 public String getName() {
236 return name;
237 }
238
239 public boolean isLeaf() {
240 return siblings.isEmpty();
241 }
242
243 public Node addSibling(String name) {
244 Node sibling = siblings.get(name);
245 if (sibling == null) {
246 sibling = new Node(name);
247 siblings.put(name, sibling);
248 }
249 return sibling;
250 }
251
252 public Node getSibling(String name) {
253 return siblings.get(name);
254 }
255 }
256
257 private static List<String> elementsOf(final String path) {
258 return Arrays.stream(path.split("/"))
259 .filter(e -> e != null && !e.isEmpty())
260 .collect(toList());
261 }
262 }