View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl.resolution;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.file.Files;
24  import java.nio.file.Paths;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.concurrent.atomic.AtomicReference;
30  
31  import org.eclipse.aether.DefaultRepositorySystemSession;
32  import org.eclipse.aether.RepositorySystemSession;
33  import org.eclipse.aether.artifact.Artifact;
34  import org.eclipse.aether.artifact.DefaultArtifact;
35  import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory;
36  import org.eclipse.aether.internal.test.util.TestUtils;
37  import org.eclipse.aether.repository.ArtifactRepository;
38  import org.eclipse.aether.resolution.ArtifactRequest;
39  import org.eclipse.aether.resolution.ArtifactResult;
40  import org.eclipse.aether.spi.checksums.TrustedChecksumsSource;
41  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
42  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
43  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
44  import org.junit.jupiter.api.BeforeEach;
45  import org.junit.jupiter.api.Test;
46  
47  import static java.util.stream.Collectors.toList;
48  import static org.junit.jupiter.api.Assertions.*;
49  
50  /**
51   * UT for {@link TrustedChecksumsArtifactResolverPostProcessor}.
52   */
53  public class TrustedChecksumsArtifactResolverPostProcessorTest implements TrustedChecksumsSource {
54      private static final String TRUSTED_SOURCE_NAME = "test";
55  
56      private Artifact artifactWithoutTrustedChecksum;
57  
58      private Artifact artifactWithTrustedChecksum;
59  
60      private String artifactTrustedChecksum;
61  
62      protected DefaultRepositorySystemSession session;
63  
64      protected ChecksumAlgorithmFactory checksumAlgorithmFactory = new Sha1ChecksumAlgorithmFactory();
65  
66      private TrustedChecksumsArtifactResolverPostProcessor subject;
67  
68      private TrustedChecksumsSource.Writer trustedChecksumsWriter;
69  
70      @BeforeEach
71      void prepareSubject() throws IOException {
72          Files.createDirectories(Paths.get(System.getProperty("java.io.tmpdir"))); // hack for Surefire
73          // make the two artifacts, BOTH as resolved
74          File tmp = Files.createTempFile("artifact", "tmp").toFile();
75          artifactWithoutTrustedChecksum = new DefaultArtifact("test:test:1.0").setFile(tmp);
76          artifactWithTrustedChecksum = new DefaultArtifact("test:test:2.0").setFile(tmp);
77          artifactTrustedChecksum = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; // empty file
78  
79          session = TestUtils.newSession();
80          ChecksumAlgorithmFactorySelector selector = new ChecksumAlgorithmFactorySelector() {
81              @Override
82              public ChecksumAlgorithmFactory select(String algorithmName) {
83                  if (checksumAlgorithmFactory.getName().equals(algorithmName)) {
84                      return checksumAlgorithmFactory;
85                  }
86                  throw new IllegalArgumentException("no alg factory for " + algorithmName);
87              }
88  
89              @Override
90              public List<ChecksumAlgorithmFactory> selectList(Collection<String> algorithmNames) {
91                  return algorithmNames.stream().map(this::select).collect(toList());
92              }
93  
94              @Override
95              public Collection<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() {
96                  return Collections.singletonList(checksumAlgorithmFactory);
97              }
98  
99              @Override
100             public boolean isChecksumExtension(String extension) {
101                 throw new RuntimeException("not implemented");
102             }
103         };
104         subject = new TrustedChecksumsArtifactResolverPostProcessor(
105                 selector, Collections.singletonMap(TRUSTED_SOURCE_NAME, this));
106         trustedChecksumsWriter = null;
107         session.setConfigProperty("aether.artifactResolver.postProcessor.trustedChecksums", Boolean.TRUE.toString());
108     }
109 
110     // -- TrustedChecksumsSource interface BEGIN
111 
112     @Override
113     public Map<String, String> getTrustedArtifactChecksums(
114             RepositorySystemSession session,
115             Artifact artifact,
116             ArtifactRepository artifactRepository,
117             List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
118         if (ArtifactIdUtils.toId(artifactWithTrustedChecksum).equals(ArtifactIdUtils.toId(artifact))) {
119             return Collections.singletonMap(checksumAlgorithmFactory.getName(), artifactTrustedChecksum);
120         } else {
121             return Collections.emptyMap();
122         }
123     }
124 
125     @Override
126     public Writer getTrustedArtifactChecksumsWriter(RepositorySystemSession session) {
127         return trustedChecksumsWriter;
128     }
129 
130     // -- TrustedChecksumsSource interface END
131 
132     private ArtifactResult createArtifactResult(Artifact artifact) {
133         ArtifactResult artifactResult = new ArtifactResult(new ArtifactRequest().setArtifact(artifact));
134         artifactResult.setArtifact(artifact);
135         return artifactResult;
136     }
137 
138     // UTs below
139 
140     @Test
141     void haveMatchingChecksumPass() {
142         ArtifactResult artifactResult = createArtifactResult(artifactWithTrustedChecksum);
143         assertTrue(artifactResult.isResolved());
144 
145         subject.postProcess(session, Collections.singletonList(artifactResult));
146         assertTrue(artifactResult.isResolved());
147     }
148 
149     @Test
150     void haveNoChecksumPass() {
151         ArtifactResult artifactResult = createArtifactResult(artifactWithoutTrustedChecksum);
152         assertTrue(artifactResult.isResolved());
153 
154         subject.postProcess(session, Collections.singletonList(artifactResult));
155         assertTrue(artifactResult.isResolved());
156     }
157 
158     @Test
159     void haveNoChecksumFailIfMissingEnabledFail() {
160         session.setConfigProperty(
161                 "aether.artifactResolver.postProcessor.trustedChecksums.failIfMissing", Boolean.TRUE.toString());
162         ArtifactResult artifactResult = createArtifactResult(artifactWithoutTrustedChecksum);
163         assertTrue(artifactResult.isResolved());
164 
165         subject.postProcess(session, Collections.singletonList(artifactResult));
166         assertFalse(artifactResult.isResolved());
167         assertFalse(artifactResult.getExceptions().isEmpty());
168         assertTrue(artifactResult
169                 .getExceptions()
170                 .get(0)
171                 .getMessage()
172                 .contains("Missing from " + TRUSTED_SOURCE_NAME + " trusted"));
173     }
174 
175     @Test
176     void haveMismatchingChecksumFail() {
177         artifactTrustedChecksum = "foobar";
178         ArtifactResult artifactResult = createArtifactResult(artifactWithTrustedChecksum);
179         assertTrue(artifactResult.isResolved());
180 
181         subject.postProcess(session, Collections.singletonList(artifactResult));
182         assertFalse(artifactResult.isResolved());
183         assertFalse(artifactResult.getExceptions().isEmpty());
184         assertTrue(artifactResult.getExceptions().get(0).getMessage().contains("trusted checksum mismatch"));
185         assertTrue(artifactResult
186                 .getExceptions()
187                 .get(0)
188                 .getMessage()
189                 .contains(TRUSTED_SOURCE_NAME + "=" + artifactTrustedChecksum));
190     }
191 
192     @Test
193     void recordCalculatedChecksum() {
194         AtomicReference<String> recordedChecksum = new AtomicReference<>(null);
195         this.trustedChecksumsWriter = new Writer() {
196             @Override
197             public void addTrustedArtifactChecksums(
198                     Artifact artifact,
199                     ArtifactRepository artifactRepository,
200                     List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
201                     Map<String, String> trustedArtifactChecksums) {
202                 recordedChecksum.set(trustedArtifactChecksums.get(checksumAlgorithmFactory.getName()));
203             }
204         };
205         session.setConfigProperty(
206                 "aether.artifactResolver.postProcessor.trustedChecksums.record", Boolean.TRUE.toString());
207         ArtifactResult artifactResult = createArtifactResult(artifactWithTrustedChecksum);
208         assertTrue(artifactResult.isResolved());
209 
210         subject.postProcess(session, Collections.singletonList(artifactResult));
211         assertTrue(artifactResult.isResolved());
212 
213         String checksum = recordedChecksum.get();
214         assertNotNull(checksum);
215         assertEquals(checksum, artifactTrustedChecksum);
216     }
217 }