1 package org.eclipse.aether.internal.impl.resolution;
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.IOException;
27 import java.io.UncheckedIOException;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.Set;
33
34 import org.eclipse.aether.RepositorySystemSession;
35 import org.eclipse.aether.artifact.Artifact;
36 import org.eclipse.aether.repository.ArtifactRepository;
37 import org.eclipse.aether.resolution.ArtifactResult;
38 import org.eclipse.aether.spi.checksums.TrustedChecksumsSource;
39 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
40 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
41 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper;
42 import org.eclipse.aether.transfer.ChecksumFailureException;
43 import org.eclipse.aether.util.ConfigUtils;
44 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import static java.util.Objects.requireNonNull;
49
50
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
76
77
78 @Singleton
79 @Named( TrustedChecksumsArtifactResolverPostProcessor.NAME )
80 public final class TrustedChecksumsArtifactResolverPostProcessor
81 extends ArtifactResolverPostProcessorSupport
82 {
83 public static final String NAME = "trustedChecksums";
84
85 private static final String CONF_NAME_CHECKSUM_ALGORITHMS = "checksumAlgorithms";
86
87 private static final String DEFAULT_CHECKSUM_ALGORITHMS = "SHA-1";
88
89 private static final String CONF_NAME_FAIL_IF_MISSING = "failIfMissing";
90
91 private static final String CONF_NAME_SNAPSHOTS = "snapshots";
92
93 private static final String CONF_NAME_RECORD = "record";
94
95 private static final String CHECKSUM_ALGORITHMS_CACHE_KEY =
96 TrustedChecksumsArtifactResolverPostProcessor.class.getName() + ".checksumAlgorithms";
97
98 private static final Logger LOGGER = LoggerFactory.getLogger( TrustedChecksumsArtifactResolverPostProcessor.class );
99
100 private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
101
102 private final Map<String, TrustedChecksumsSource> trustedChecksumsSources;
103
104 @Inject
105 public TrustedChecksumsArtifactResolverPostProcessor(
106 ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector,
107 Map<String, TrustedChecksumsSource> trustedChecksumsSources )
108 {
109 super( NAME );
110 this.checksumAlgorithmFactorySelector = requireNonNull( checksumAlgorithmFactorySelector );
111 this.trustedChecksumsSources = requireNonNull( trustedChecksumsSources );
112 }
113
114 @SuppressWarnings( "unchecked" )
115 @Override
116 protected void doPostProcess( RepositorySystemSession session, List<ArtifactResult> artifactResults )
117 {
118 final List<ChecksumAlgorithmFactory> checksumAlgorithms = (List<ChecksumAlgorithmFactory>) session.getData()
119 .computeIfAbsent( CHECKSUM_ALGORITHMS_CACHE_KEY, () ->
120 checksumAlgorithmFactorySelector.selectList(
121 ConfigUtils.parseCommaSeparatedUniqueNames( ConfigUtils.getString(
122 session, DEFAULT_CHECKSUM_ALGORITHMS, CONF_NAME_CHECKSUM_ALGORITHMS ) )
123 ) );
124
125 final boolean failIfMissing = ConfigUtils.getBoolean(
126 session, false, configPropKey( CONF_NAME_FAIL_IF_MISSING ) );
127 final boolean record = ConfigUtils.getBoolean(
128 session, false, configPropKey( CONF_NAME_RECORD ) );
129 final boolean snapshots = ConfigUtils.getBoolean(
130 session, false, configPropKey( CONF_NAME_SNAPSHOTS ) );
131
132 for ( ArtifactResult artifactResult : artifactResults )
133 {
134 if ( artifactResult.getArtifact().isSnapshot() && !snapshots )
135 {
136 continue;
137 }
138 if ( artifactResult.isResolved() )
139 {
140 if ( record )
141 {
142 recordArtifactChecksums( session, artifactResult, checksumAlgorithms );
143 }
144 else if ( !validateArtifactChecksums( session, artifactResult, checksumAlgorithms, failIfMissing ) )
145 {
146 artifactResult.setArtifact( artifactResult.getArtifact().setFile( null ) );
147 }
148 }
149 }
150 }
151
152
153
154
155 private void recordArtifactChecksums( RepositorySystemSession session,
156 ArtifactResult artifactResult,
157 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories )
158 {
159 Artifact artifact = artifactResult.getArtifact();
160 ArtifactRepository artifactRepository = artifactResult.getRepository();
161 try
162 {
163 final Map<String, String> calculatedChecksums = ChecksumAlgorithmHelper.calculate(
164 artifact.getFile(), checksumAlgorithmFactories );
165
166 for ( TrustedChecksumsSource trustedChecksumsSource : trustedChecksumsSources.values() )
167 {
168 TrustedChecksumsSource.Writer writer = trustedChecksumsSource
169 .getTrustedArtifactChecksumsWriter( session );
170 if ( writer != null )
171 {
172 try
173 {
174 writer.addTrustedArtifactChecksums( artifact, artifactRepository, checksumAlgorithmFactories,
175 calculatedChecksums );
176 }
177 catch ( IOException e )
178 {
179 throw new UncheckedIOException( "Could not write required checksums for "
180 + artifact.getFile(), e );
181 }
182 }
183 }
184 }
185 catch ( IOException e )
186 {
187 throw new UncheckedIOException( "Could not calculate required checksums for "
188 + artifact.getFile(), e );
189 }
190 }
191
192
193
194
195
196 private boolean validateArtifactChecksums( RepositorySystemSession session,
197 ArtifactResult artifactResult,
198 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
199 boolean failIfMissing )
200 {
201 Artifact artifact = artifactResult.getArtifact();
202 ArtifactRepository artifactRepository = artifactResult.getRepository();
203 boolean valid = true;
204 boolean validated = false;
205 try
206 {
207
208 final Map<String, String> calculatedChecksums = ChecksumAlgorithmHelper.calculate(
209 artifact.getFile(), checksumAlgorithmFactories );
210
211 for ( Map.Entry<String, TrustedChecksumsSource> entry : trustedChecksumsSources.entrySet() )
212 {
213 final String trustedSourceName = entry.getKey();
214 final TrustedChecksumsSource trustedChecksumsSource = entry.getValue();
215
216
217 Map<String, String> trustedChecksums = trustedChecksumsSource.getTrustedArtifactChecksums(
218 session, artifact, artifactRepository, checksumAlgorithmFactories );
219
220 if ( trustedChecksums == null )
221 {
222 continue;
223 }
224 validated = true;
225
226 if ( !calculatedChecksums.equals( trustedChecksums ) )
227 {
228 Set<String> missingTrustedAlg = new HashSet<>( calculatedChecksums.keySet() );
229 missingTrustedAlg.removeAll( trustedChecksums.keySet() );
230
231 if ( !missingTrustedAlg.isEmpty() && failIfMissing )
232 {
233 artifactResult.addException( new ChecksumFailureException( "Missing from " + trustedSourceName
234 + " trusted checksum(s) " + missingTrustedAlg + " for artifact "
235 + ArtifactIdUtils.toId( artifact ) ) );
236 valid = false;
237 }
238
239
240
241 for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories )
242 {
243 String calculatedChecksum = calculatedChecksums.get( checksumAlgorithmFactory.getName() );
244 String trustedChecksum = trustedChecksums.get( checksumAlgorithmFactory.getName() );
245 if ( trustedChecksum != null && !Objects.equals( calculatedChecksum, trustedChecksum ) )
246 {
247 artifactResult.addException( new ChecksumFailureException( "Artifact "
248 + ArtifactIdUtils.toId( artifact ) + " trusted checksum mismatch: "
249 + trustedSourceName + "=" + trustedChecksum + "; calculated="
250 + calculatedChecksum ) );
251 valid = false;
252 }
253 }
254 }
255 }
256
257 if ( !validated && failIfMissing )
258 {
259 artifactResult.addException( new ChecksumFailureException( "There are no enabled trusted checksums"
260 + " source(s) to validate against." ) );
261 valid = false;
262 }
263 }
264 catch ( IOException e )
265 {
266 throw new UncheckedIOException( e );
267 }
268 return valid;
269 }
270 }