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