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