001package org.eclipse.aether.internal.impl.resolution;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.IOException;
024import java.nio.file.Files;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.List;
028import java.util.Map;
029import java.util.concurrent.atomic.AtomicReference;
030
031import org.eclipse.aether.DefaultRepositorySystemSession;
032import org.eclipse.aether.RepositorySystemSession;
033import org.eclipse.aether.artifact.Artifact;
034import org.eclipse.aether.artifact.DefaultArtifact;
035import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory;
036import org.eclipse.aether.internal.test.util.TestUtils;
037import org.eclipse.aether.repository.ArtifactRepository;
038import org.eclipse.aether.resolution.ArtifactRequest;
039import org.eclipse.aether.resolution.ArtifactResult;
040import org.eclipse.aether.spi.checksums.TrustedChecksumsSource;
041import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
042import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
043import org.eclipse.aether.util.artifact.ArtifactIdUtils;
044import org.junit.Before;
045import org.junit.Test;
046
047import static java.util.stream.Collectors.toList;
048import static org.hamcrest.MatcherAssert.assertThat;
049import static org.hamcrest.Matchers.containsString;
050import static org.hamcrest.Matchers.empty;
051import static org.hamcrest.Matchers.equalTo;
052import static org.hamcrest.Matchers.not;
053import static org.hamcrest.Matchers.notNullValue;
054
055/**
056 * UT for {@link TrustedChecksumsArtifactResolverPostProcessor}.
057 */
058public class TrustedChecksumsArtifactResolverPostProcessorTest implements TrustedChecksumsSource
059{
060    private static final String TRUSTED_SOURCE_NAME = "test";
061
062    private Artifact artifactWithoutTrustedChecksum;
063
064    private Artifact artifactWithTrustedChecksum;
065
066    private String artifactTrustedChecksum;
067
068    protected DefaultRepositorySystemSession session;
069
070    protected ChecksumAlgorithmFactory checksumAlgorithmFactory = new Sha1ChecksumAlgorithmFactory();
071
072    private TrustedChecksumsArtifactResolverPostProcessor subject;
073
074    private TrustedChecksumsSource.Writer trustedChecksumsWriter;
075
076    @Before
077    public void prepareSubject() throws IOException
078    {
079        // make the two artifacts, BOTH as resolved
080        File tmp = Files.createTempFile( "artifact", "tmp" ).toFile();
081        artifactWithoutTrustedChecksum = new DefaultArtifact( "test:test:1.0" ).setFile( tmp );
082        artifactWithTrustedChecksum = new DefaultArtifact( "test:test:2.0" ).setFile( tmp );
083        artifactTrustedChecksum = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; // empty file
084
085        session = TestUtils.newSession();
086        ChecksumAlgorithmFactorySelector selector = new ChecksumAlgorithmFactorySelector()
087        {
088            @Override
089            public ChecksumAlgorithmFactory select( String algorithmName )
090            {
091                if ( checksumAlgorithmFactory.getName().equals( algorithmName ) )
092                {
093                    return checksumAlgorithmFactory;
094                }
095                throw new IllegalArgumentException("no alg factory for " + algorithmName);
096            }
097
098            @Override
099            public List<ChecksumAlgorithmFactory> selectList( Collection<String> algorithmNames )
100            {
101                return algorithmNames.stream()
102                        .map( this::select )
103                        .collect( toList() );
104            }
105
106            @Override
107            public Collection<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories()
108            {
109                return Collections.singletonList( checksumAlgorithmFactory );
110            }
111
112            @Override
113            public boolean isChecksumExtension( String extension )
114            {
115                throw new RuntimeException( "not implemented" );
116            }
117        };
118        subject = new TrustedChecksumsArtifactResolverPostProcessor( selector,
119                Collections.singletonMap( TRUSTED_SOURCE_NAME, this ) );
120        trustedChecksumsWriter = null;
121        session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums", Boolean.TRUE.toString() );
122    }
123
124    // -- TrustedChecksumsSource interface BEGIN
125
126    @Override
127    public Map<String, String> getTrustedArtifactChecksums( RepositorySystemSession session, Artifact artifact,
128                                                            ArtifactRepository artifactRepository,
129                                                            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories )
130    {
131        if ( ArtifactIdUtils.toId( artifactWithTrustedChecksum ).equals( ArtifactIdUtils.toId( artifact ) ) )
132        {
133            return Collections.singletonMap( checksumAlgorithmFactory.getName(), artifactTrustedChecksum );
134        }
135        else
136        {
137            return Collections.emptyMap();
138        }
139    }
140
141    @Override
142    public Writer getTrustedArtifactChecksumsWriter( RepositorySystemSession session )
143    {
144        return trustedChecksumsWriter;
145    }
146
147    // -- TrustedChecksumsSource interface END
148
149    private ArtifactResult createArtifactResult( Artifact artifact )
150    {
151        ArtifactResult artifactResult = new ArtifactResult( new ArtifactRequest().setArtifact( artifact ) );
152        artifactResult.setArtifact( artifact );
153        return artifactResult;
154    }
155
156    // UTs below
157
158    @Test
159    public void haveMatchingChecksumPass()
160    {
161        ArtifactResult artifactResult = createArtifactResult( artifactWithTrustedChecksum );
162        assertThat( artifactResult.isResolved(), equalTo( true ) );
163
164        subject.postProcess( session, Collections.singletonList( artifactResult ) );
165        assertThat( artifactResult.isResolved(), equalTo( true ) );
166    }
167
168    @Test
169    public void haveNoChecksumPass()
170    {
171        ArtifactResult artifactResult = createArtifactResult( artifactWithoutTrustedChecksum );
172        assertThat( artifactResult.isResolved(), equalTo( true ) );
173
174        subject.postProcess( session, Collections.singletonList( artifactResult ) );
175        assertThat( artifactResult.isResolved(), equalTo( true ) );
176    }
177
178    @Test
179    public void haveNoChecksumFailIfMissingEnabledFail()
180    {
181        session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums.failIfMissing",
182                Boolean.TRUE.toString() );
183        ArtifactResult artifactResult = createArtifactResult( artifactWithoutTrustedChecksum );
184        assertThat( artifactResult.isResolved(), equalTo( true ) );
185
186        subject.postProcess( session, Collections.singletonList( artifactResult ) );
187        assertThat( artifactResult.isResolved(), equalTo( false ) );
188        assertThat( artifactResult.getExceptions(), not( empty() ) );
189        assertThat( artifactResult.getExceptions().get( 0 ).getMessage(),
190                containsString( "Missing from " + TRUSTED_SOURCE_NAME + " trusted" ) );
191    }
192
193    @Test
194    public void haveMismatchingChecksumFail()
195    {
196        artifactTrustedChecksum = "foobar";
197        ArtifactResult artifactResult = createArtifactResult( artifactWithTrustedChecksum );
198        assertThat( artifactResult.isResolved(), equalTo( true ) );
199
200        subject.postProcess( session, Collections.singletonList( artifactResult ) );
201        assertThat( artifactResult.isResolved(), equalTo( false ) );
202        assertThat( artifactResult.getExceptions(), not( empty() ) );
203        assertThat( artifactResult.getExceptions().get( 0 ).getMessage(),
204                containsString( "trusted checksum mismatch" ) );
205        assertThat( artifactResult.getExceptions().get( 0 ).getMessage(),
206                containsString( TRUSTED_SOURCE_NAME + "=" + artifactTrustedChecksum ) );
207    }
208
209    @Test
210    public void recordCalculatedChecksum()
211    {
212        AtomicReference<String> recordedChecksum = new AtomicReference<>(null);
213        this.trustedChecksumsWriter = new Writer()
214        {
215            @Override
216            public void addTrustedArtifactChecksums( Artifact artifact, ArtifactRepository artifactRepository,
217                                                     List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
218                                                     Map<String, String> trustedArtifactChecksums )
219            {
220                recordedChecksum.set( trustedArtifactChecksums.get( checksumAlgorithmFactory.getName() ) );
221            }
222        };
223        session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums.record",
224                Boolean.TRUE.toString() );
225        ArtifactResult artifactResult = createArtifactResult( artifactWithTrustedChecksum );
226        assertThat( artifactResult.isResolved(), equalTo( true ) );
227
228        subject.postProcess( session, Collections.singletonList( artifactResult ) );
229        assertThat( artifactResult.isResolved(), equalTo( true ) );
230
231        String checksum = recordedChecksum.get();
232        assertThat( checksum, notNullValue() );
233        assertThat( checksum, equalTo( artifactTrustedChecksum ) );
234    }
235}