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