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 private RepositoryLayout cacheLayout( RepositorySystemSession session, RemoteRepository remoteRepository )
118 {
119 return layouts.computeIfAbsent( remoteRepository, r ->
120 {
121 try
122 {
123 return repositoryLayoutProvider.newRepositoryLayout( session, remoteRepository );
124 }
125 catch ( NoRepositoryLayoutException e )
126 {
127 throw new RuntimeException( e );
128 }
129 } );
130 }
131
132
133
134
135 private Node cacheNode( Path basedir,
136 RemoteRepository remoteRepository )
137 {
138 return prefixes.computeIfAbsent( remoteRepository, r -> loadRepositoryPrefixes( basedir, remoteRepository ) );
139 }
140
141
142
143
144 private Node loadRepositoryPrefixes( Path baseDir, RemoteRepository remoteRepository )
145 {
146 Path filePath = baseDir.resolve( PREFIXES_FILE_PREFIX + remoteRepository.getId() + PREFIXES_FILE_SUFFIX );
147 if ( Files.isReadable( filePath ) )
148 {
149 try ( BufferedReader reader = Files.newBufferedReader( filePath, StandardCharsets.UTF_8 ) )
150 {
151 LOGGER.debug( "Loading prefixes for remote repository {} from file '{}'", remoteRepository.getId(),
152 filePath );
153 Node root = new Node( "" );
154 String prefix;
155 int lines = 0;
156 while ( ( prefix = reader.readLine() ) != null )
157 {
158 if ( !prefix.startsWith( "#" ) && !prefix.trim().isEmpty() )
159 {
160 lines++;
161 Node currentNode = root;
162 for ( String element : elementsOf( prefix ) )
163 {
164 currentNode = currentNode.addSibling( element );
165 }
166 }
167 }
168 LOGGER.info( "Loaded {} prefixes for remote repository {}", lines, remoteRepository.getId() );
169 return root;
170 }
171 catch ( FileNotFoundException e )
172 {
173
174 }
175 catch ( IOException e )
176 {
177 throw new UncheckedIOException( e );
178 }
179 }
180 LOGGER.debug( "Prefix file for remote repository {} not found at '{}'", remoteRepository, filePath );
181 return NOT_PRESENT_NODE;
182 }
183
184 private class PrefixesFilter implements RemoteRepositoryFilter
185 {
186 private final RepositorySystemSession session;
187
188 private final Path basedir;
189
190 private PrefixesFilter( RepositorySystemSession session, Path basedir )
191 {
192 this.session = session;
193 this.basedir = basedir;
194 }
195
196 @Override
197 public Result acceptArtifact( RemoteRepository remoteRepository, Artifact artifact )
198 {
199 return acceptPrefix( remoteRepository,
200 cacheLayout( session, remoteRepository ).getLocation( artifact, false ).getPath() );
201 }
202
203 @Override
204 public Result acceptMetadata( RemoteRepository remoteRepository, Metadata metadata )
205 {
206 return acceptPrefix( remoteRepository,
207 cacheLayout( session, remoteRepository ).getLocation( metadata, false ).getPath() );
208 }
209
210 private Result acceptPrefix( RemoteRepository remoteRepository, String path )
211 {
212 Node root = cacheNode( basedir, remoteRepository );
213 if ( NOT_PRESENT_NODE == root )
214 {
215 return NOT_PRESENT_RESULT;
216 }
217 List<String> prefix = new ArrayList<>();
218 final List<String> pathElements = elementsOf( path );
219 Node currentNode = root;
220 for ( String pathElement : pathElements )
221 {
222 prefix.add( pathElement );
223 currentNode = currentNode.getSibling( pathElement );
224 if ( currentNode == null || currentNode.isLeaf() )
225 {
226 break;
227 }
228 }
229 if ( currentNode != null && currentNode.isLeaf() )
230 {
231 return new SimpleResult( true, "Prefix "
232 + String.join( "/", prefix ) + " allowed from " + remoteRepository );
233 }
234 else
235 {
236 return new SimpleResult( false, "Prefix "
237 + String.join( "/", prefix ) + " NOT allowed from " + remoteRepository );
238 }
239 }
240 }
241
242 private static final Node NOT_PRESENT_NODE = new Node(
243 "not-present-node" );
244
245 private static final RemoteRepositoryFilter.Result NOT_PRESENT_RESULT = new SimpleResult(
246 true, "Prefix file not present" );
247
248 private static class Node
249 {
250 private final String name;
251
252 private final HashMap<String, Node> siblings;
253
254 private Node( String name )
255 {
256 this.name = name;
257 this.siblings = new HashMap<>();
258 }
259
260 public String getName()
261 {
262 return name;
263 }
264
265 public boolean isLeaf()
266 {
267 return siblings.isEmpty();
268 }
269
270 public Node addSibling( String name )
271 {
272 Node sibling = siblings.get( name );
273 if ( sibling == null )
274 {
275 sibling = new Node( name );
276 siblings.put( name, sibling );
277 }
278 return sibling;
279 }
280
281 public Node getSibling( String name )
282 {
283 return siblings.get( name );
284 }
285 }
286
287 private static List<String> elementsOf( final String path )
288 {
289 return Arrays.stream( path.split( "/" ) ).filter( e -> e != null && !e.isEmpty() ).collect( toList() );
290 }
291 }