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