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 org.hamcrest.MatcherAssert.assertThat;
048import static org.hamcrest.Matchers.containsString;
049import static org.hamcrest.Matchers.empty;
050import static org.hamcrest.Matchers.equalTo;
051import static org.hamcrest.Matchers.not;
052import static org.hamcrest.Matchers.notNullValue;
053
054/**
055 * UT for {@link TrustedChecksumsArtifactResolverPostProcessor}.
056 */
057public class TrustedChecksumsArtifactResolverPostProcessorTest implements TrustedChecksumsSource
058{
059    private static final String TRUSTED_SOURCE_NAME = "test";
060
061    private Artifact artifactWithoutTrustedChecksum;
062
063    private Artifact artifactWithTrustedChecksum;
064
065    private String artifactTrustedChecksum;
066
067    protected DefaultRepositorySystemSession session;
068
069    protected ChecksumAlgorithmFactory checksumAlgorithmFactory = new Sha1ChecksumAlgorithmFactory();
070
071    private TrustedChecksumsArtifactResolverPostProcessor subject;
072
073    private TrustedChecksumsSource.Writer trustedChecksumsWriter;
074
075    @Before
076    public void prepareSubject() throws IOException
077    {
078        // make the two artifacts, BOTH as resolved
079        File tmp = Files.createTempFile( "artifact", "tmp" ).toFile();
080        artifactWithoutTrustedChecksum = new DefaultArtifact( "test:test:1.0" ).setFile( tmp );
081        artifactWithTrustedChecksum = new DefaultArtifact( "test:test:2.0" ).setFile( tmp );
082        artifactTrustedChecksum = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; // empty file
083
084        session = TestUtils.newSession();
085        ChecksumAlgorithmFactorySelector selector = new ChecksumAlgorithmFactorySelector()
086        {
087            @Override
088            public ChecksumAlgorithmFactory select( String algorithmName )
089            {
090                if ( checksumAlgorithmFactory.getName().equals( algorithmName ) )
091                {
092                    return checksumAlgorithmFactory;
093                }
094                throw new IllegalArgumentException("no alg factory for " + algorithmName);
095            }
096
097            @Override
098            public Collection<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories()
099            {
100                return Collections.singletonList( checksumAlgorithmFactory );
101            }
102        };
103        subject = new TrustedChecksumsArtifactResolverPostProcessor( selector,
104                Collections.singletonMap( TRUSTED_SOURCE_NAME, this ) );
105        trustedChecksumsWriter = null;
106        session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums", Boolean.TRUE.toString() );
107    }
108
109    // -- TrustedChecksumsSource interface BEGIN
110
111    @Override
112    public Map<String, String> getTrustedArtifactChecksums( RepositorySystemSession session, Artifact artifact,
113                                                            ArtifactRepository artifactRepository,
114                                                            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories )
115    {
116        if ( ArtifactIdUtils.toId( artifactWithTrustedChecksum ).equals( ArtifactIdUtils.toId( artifact ) ) )
117        {
118            return Collections.singletonMap( checksumAlgorithmFactory.getName(), artifactTrustedChecksum );
119        }
120        else
121        {
122            return Collections.emptyMap();
123        }
124    }
125
126    @Override
127    public Writer getTrustedArtifactChecksumsWriter( RepositorySystemSession session )
128    {
129        return trustedChecksumsWriter;
130    }
131
132    // -- TrustedChecksumsSource interface END
133
134    private ArtifactResult createArtifactResult( Artifact artifact )
135    {
136        ArtifactResult artifactResult = new ArtifactResult( new ArtifactRequest().setArtifact( artifact ) );
137        artifactResult.setArtifact( artifact );
138        return artifactResult;
139    }
140
141    // UTs below
142
143    @Test
144    public void haveMatchingChecksumPass()
145    {
146        ArtifactResult artifactResult = createArtifactResult( artifactWithTrustedChecksum );
147        assertThat( artifactResult.isResolved(), equalTo( true ) );
148
149        subject.postProcess( session, Collections.singletonList( artifactResult ) );
150        assertThat( artifactResult.isResolved(), equalTo( true ) );
151    }
152
153    @Test
154    public void haveNoChecksumPass()
155    {
156        ArtifactResult artifactResult = createArtifactResult( artifactWithoutTrustedChecksum );
157        assertThat( artifactResult.isResolved(), equalTo( true ) );
158
159        subject.postProcess( session, Collections.singletonList( artifactResult ) );
160        assertThat( artifactResult.isResolved(), equalTo( true ) );
161    }
162
163    @Test
164    public void haveNoChecksumFailIfMissingEnabledFail()
165    {
166        session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums.failIfMissing",
167                Boolean.TRUE.toString() );
168        ArtifactResult artifactResult = createArtifactResult( artifactWithoutTrustedChecksum );
169        assertThat( artifactResult.isResolved(), equalTo( true ) );
170
171        subject.postProcess( session, Collections.singletonList( artifactResult ) );
172        assertThat( artifactResult.isResolved(), equalTo( false ) );
173        assertThat( artifactResult.getExceptions(), not( empty() ) );
174        assertThat( artifactResult.getExceptions().get( 0 ).getMessage(),
175                containsString( "Missing from " + TRUSTED_SOURCE_NAME + " trusted" ) );
176    }
177
178    @Test
179    public void haveMismatchingChecksumFail()
180    {
181        artifactTrustedChecksum = "foobar";
182        ArtifactResult artifactResult = createArtifactResult( artifactWithTrustedChecksum );
183        assertThat( artifactResult.isResolved(), equalTo( true ) );
184
185        subject.postProcess( session, Collections.singletonList( artifactResult ) );
186        assertThat( artifactResult.isResolved(), equalTo( false ) );
187        assertThat( artifactResult.getExceptions(), not( empty() ) );
188        assertThat( artifactResult.getExceptions().get( 0 ).getMessage(),
189                containsString( "trusted checksum mismatch" ) );
190        assertThat( artifactResult.getExceptions().get( 0 ).getMessage(),
191                containsString( TRUSTED_SOURCE_NAME + "=" + artifactTrustedChecksum ) );
192    }
193
194    @Test
195    public void recordCalculatedChecksum()
196    {
197        AtomicReference<String> recordedChecksum = new AtomicReference<>(null);
198        this.trustedChecksumsWriter = new Writer()
199        {
200            @Override
201            public void addTrustedArtifactChecksums( Artifact artifact, ArtifactRepository artifactRepository,
202                                                     List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
203                                                     Map<String, String> trustedArtifactChecksums )
204            {
205                recordedChecksum.set( trustedArtifactChecksums.get( checksumAlgorithmFactory.getName() ) );
206            }
207        };
208        session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums.record",
209                Boolean.TRUE.toString() );
210        ArtifactResult artifactResult = createArtifactResult( artifactWithTrustedChecksum );
211        assertThat( artifactResult.isResolved(), equalTo( true ) );
212
213        subject.postProcess( session, Collections.singletonList( artifactResult ) );
214        assertThat( artifactResult.isResolved(), equalTo( true ) );
215
216        String checksum = recordedChecksum.get();
217        assertThat( checksum, notNullValue() );
218        assertThat( checksum, equalTo( artifactTrustedChecksum ) );
219    }
220}