You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2022/03/15 13:12:25 UTC

[maven-resolver] branch master updated: [MRESOLVER-241] Resolver checksum calculation should be driven by layout (#154)

This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/master by this push:
     new 16b12f1  [MRESOLVER-241] Resolver checksum calculation should be driven by layout (#154)
16b12f1 is described below

commit 16b12f14eec8b6309a118c225f50f35292eb0a4f
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Tue Mar 15 14:12:15 2022 +0100

    [MRESOLVER-241] Resolver checksum calculation should be driven by layout (#154)
    
    Commit contains two fixes:
    * MRESOLVER-241
    * MRESOLVER-242
    
    High level explanations (as fix of two issues is overlapping)
    
    MRESOLVER-241 First part:
    Refactor checksum calculator and validator to rely on layout
    used checksums (layout always had these), instead of presence
    (or absence) or remote checksum locations.
    Checksum locations should drive only the checksum provision from
    remote, and nothing else. This change not only fixes the problem
    with signarure (they have no remote checksums), but also makes
    new "provided" and "included" checksum still work as expected
    (fun fact: central DOES provide checksum of checksum in header,
    even if layout does not).
    
    MRESOLVER-242 Second part:
    Change checksum validator to validate against remote checksum
    location ONLY if they are expected to exist. This fixes the issue that in some cases,
    layout returns empty collection for remote checksum locations
    (as was Maven2RepositoryLayoutEx for signatures). Moreover,
    introduce checksum filter to resolver to be able to configure ignored
    extensions instead to have "wired in" `.asc` extension only.
    The implementation supports "old way" forSignatures=true as well
    by having empty string for new configuration property (as then no
    extension will be omitted for checksums, so even signatures
    will have them created/deployed and fetched/validated).
    
    ---
    
    https://issues.apache.org/jira/browse/MRESOLVER-241
    https://issues.apache.org/jira/browse/MRESOLVER-242
---
 .../connector/basic/BasicRepositoryConnector.java  |  10 +-
 .../aether/connector/basic/ChecksumCalculator.java |  15 ++-
 .../aether/connector/basic/ChecksumValidator.java  |  28 +++---
 .../connector/basic/ChecksumCalculatorTest.java    |   9 +-
 .../connector/basic/ChecksumValidatorTest.java     |  20 +++-
 .../impl/Maven2RepositoryLayoutFactory.java        | 101 +++++++++++++--------
 .../impl/Maven2RepositoryLayoutFactoryTest.java    |  31 ++++++-
 .../checksum/ChecksumAlgorithmFactorySelector.java |   2 +-
 .../spi/connector/layout/RepositoryLayout.java     |  31 ++++++-
 src/site/markdown/configuration.md                 |   2 +-
 10 files changed, 172 insertions(+), 77 deletions(-)

diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
index c761941..e292de3 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
@@ -227,6 +227,7 @@ final class BasicRepositoryConnector
 
         Executor executor = getExecutor( artifactDownloads, metadataDownloads );
         RunnableErrorForwarder errorForwarder = new RunnableErrorForwarder();
+        List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = layout.getChecksumAlgorithmFactories();
 
         for ( MetadataDownload transfer : safe( metadataDownloads ) )
         {
@@ -244,7 +245,7 @@ final class BasicRepositoryConnector
             }
 
             Runnable task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy,
-                    checksumLocations, null, listener );
+                    checksumAlgorithmFactories, checksumLocations, null, listener );
             executor.execute( errorForwarder.wrap( task ) );
         }
 
@@ -254,7 +255,7 @@ final class BasicRepositoryConnector
             for ( ProvidedChecksumsSource providedChecksumsSource : providedChecksumsSources.values() )
             {
                 Map<String, String> provided = providedChecksumsSource.getProvidedArtifactChecksums(
-                    session, transfer, layout.getChecksumAlgorithmFactories() );
+                    session, transfer, checksumAlgorithmFactories );
 
                 if ( provided != null )
                 {
@@ -284,7 +285,7 @@ final class BasicRepositoryConnector
                 }
 
                 task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy,
-                    checksumLocations, providedChecksums, listener );
+                        checksumAlgorithmFactories, checksumLocations, providedChecksums, listener );
             }
             executor.execute( errorForwarder.wrap( task ) );
         }
@@ -434,13 +435,14 @@ final class BasicRepositoryConnector
         private final ChecksumValidator checksumValidator;
 
         GetTaskRunner( URI path, File file, ChecksumPolicy checksumPolicy,
+                       List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
                        List<RepositoryLayout.ChecksumLocation> checksumLocations,
                        Map<String, String> providedChecksums,
                        TransferTransportListener<?> listener )
         {
             super( path, listener );
             this.file = requireNonNull( file, "destination file cannot be null" );
-            checksumValidator = new ChecksumValidator( file, fileProcessor, this,
+            checksumValidator = new ChecksumValidator( file, checksumAlgorithmFactories, fileProcessor, this,
                     checksumPolicy, providedChecksums, safe( checksumLocations ) );
         }
 
diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java
index 87eeb78..ccf0431 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java
@@ -36,7 +36,6 @@ import java.util.Set;
 
 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm;
 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
-import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
 
 import static java.util.Objects.requireNonNull;
 
@@ -92,25 +91,25 @@ final class ChecksumCalculator
     private final File targetFile;
 
     public static ChecksumCalculator newInstance( File targetFile,
-                                                  Collection<RepositoryLayout.ChecksumLocation> checksumLocations )
+                                                  Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories )
     {
-        if ( checksumLocations == null || checksumLocations.isEmpty() )
+        if ( checksumAlgorithmFactories == null || checksumAlgorithmFactories.isEmpty() )
         {
             return null;
         }
-        return new ChecksumCalculator( targetFile, checksumLocations );
+        return new ChecksumCalculator( targetFile, checksumAlgorithmFactories );
     }
 
     private ChecksumCalculator( File targetFile,
-                                Collection<RepositoryLayout.ChecksumLocation> checksumLocations )
+                                Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories )
     {
         this.checksums = new ArrayList<>();
         Set<String> algos = new HashSet<>();
-        for ( RepositoryLayout.ChecksumLocation checksumLocation : checksumLocations )
+        for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories )
         {
-            if ( algos.add( checksumLocation.getChecksumAlgorithmFactory().getName() ) )
+            if ( algos.add( checksumAlgorithmFactory.getName() ) )
             {
-                this.checksums.add( new Checksum( checksumLocation.getChecksumAlgorithmFactory() ) );
+                this.checksums.add( new Checksum( checksumAlgorithmFactory ) );
             }
         }
         this.targetFile = targetFile;
diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java
index f6b0fef..14ae450 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java
@@ -59,6 +59,8 @@ final class ChecksumValidator
 
     private final File dataFile;
 
+    private final Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories;
+
     private final Collection<File> tempFiles;
 
     private final FileProcessor fileProcessor;
@@ -74,6 +76,7 @@ final class ChecksumValidator
     private final Map<File, Object> checksumFiles;
 
     ChecksumValidator( File dataFile,
+                       Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
                        FileProcessor fileProcessor,
                        ChecksumFetcher checksumFetcher,
                        ChecksumPolicy checksumPolicy,
@@ -81,6 +84,7 @@ final class ChecksumValidator
                        Collection<ChecksumLocation> checksumLocations )
     {
         this.dataFile = dataFile;
+        this.checksumAlgorithmFactories = checksumAlgorithmFactories;
         this.tempFiles = new HashSet<>();
         this.fileProcessor = fileProcessor;
         this.checksumFetcher = checksumFetcher;
@@ -94,7 +98,7 @@ final class ChecksumValidator
     {
         if ( checksumPolicy != null )
         {
-            return ChecksumCalculator.newInstance( targetFile, checksumLocations );
+            return ChecksumCalculator.newInstance( targetFile, checksumAlgorithmFactories );
         }
         return null;
     }
@@ -116,11 +120,14 @@ final class ChecksumValidator
         {
             return;
         }
-        if ( validateExternalChecksums( actualChecksums ) )
+        if ( !checksumLocations.isEmpty() )
         {
-            return;
+            if ( validateExternalChecksums( actualChecksums ) )
+            {
+                return;
+            }
+            checksumPolicy.onNoMoreChecksums();
         }
-        checksumPolicy.onNoMoreChecksums();
     }
 
     private boolean validateChecksums( Map<String, ?> actualChecksums, ChecksumKind kind, Map<String, ?> checksums )
@@ -134,27 +141,26 @@ final class ChecksumValidator
             {
                 continue;
             }
-            ChecksumLocation checksumLocation = checksumLocations.stream()
-                    .filter( a -> a.getChecksumAlgorithmFactory().getName().equals( algo ) )
+            ChecksumAlgorithmFactory checksumAlgorithmFactory = checksumAlgorithmFactories.stream()
+                    .filter( a -> a.getName().equals( algo ) )
                     .findFirst()
                     .orElse( null );
-            if ( checksumLocation == null )
+            if ( checksumAlgorithmFactory == null )
             {
                 continue;
             }
 
             String actual = String.valueOf( calculated );
             String expected = entry.getValue().toString();
-            ChecksumAlgorithmFactory factory = checksumLocation.getChecksumAlgorithmFactory();
-            checksumFiles.put( getChecksumFile( factory ), expected );
+            checksumFiles.put( getChecksumFile( checksumAlgorithmFactory ), expected );
 
             if ( !isEqualChecksum( expected, actual ) )
             {
-                checksumPolicy.onChecksumMismatch( factory.getName(), kind,
+                checksumPolicy.onChecksumMismatch( checksumAlgorithmFactory.getName(), kind,
                     new ChecksumFailureException( expected, kind.name(), actual )
                 );
             }
-            else if ( checksumPolicy.onChecksumMatch( factory.getName(), kind ) )
+            else if ( checksumPolicy.onChecksumMatch( checksumAlgorithmFactory.getName(), kind ) )
             {
                 return true;
             }
diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java
index 818de7c..6560537 100644
--- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java
+++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java
@@ -27,7 +27,6 @@ import static org.junit.Assert.*;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -35,7 +34,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.eclipse.aether.internal.test.util.TestFileUtils;
-import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -48,12 +47,12 @@ public class ChecksumCalculatorTest
 
     private ChecksumCalculator newCalculator( String... algos )
     {
-        List<RepositoryLayout.ChecksumLocation> checksumLocations = new ArrayList<>();
+        List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = new ArrayList<>();
         for ( String algo : algos )
         {
-            checksumLocations.add( new RepositoryLayout.ChecksumLocation( URI.create( "irrelevant" ), selector.select( algo ) ) );
+            checksumAlgorithmFactories.add( selector.select( algo ) );
         }
-        return ChecksumCalculator.newInstance( file, checksumLocations );
+        return ChecksumCalculator.newInstance( file, checksumAlgorithmFactories );
     }
 
     private ByteBuffer toBuffer( String data )
diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java
index cd171b0..530e5e7 100644
--- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java
+++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java
@@ -35,6 +35,7 @@ import java.util.Map;
 
 import org.eclipse.aether.internal.test.util.TestFileProcessor;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
@@ -185,17 +186,27 @@ public class ChecksumValidatorTest
 
     private static final TestChecksumAlgorithmSelector selector = new TestChecksumAlgorithmSelector();
 
+    private List<ChecksumAlgorithmFactory> newChecksumAlgorithmFactories( String... factories )
+    {
+        List<ChecksumAlgorithmFactory> checksums = new ArrayList<>();
+        for ( String factory : factories )
+        {
+            checksums.add( selector.select( factory ) );
+        }
+        return checksums;
+    }
+
     private static RepositoryLayout.ChecksumLocation newChecksum( String factory )
     {
         return RepositoryLayout.ChecksumLocation.forLocation( URI.create( "file" ), selector.select( factory ) );
     }
 
-    private List<RepositoryLayout.ChecksumLocation> newChecksums( String... factories )
+    private List<RepositoryLayout.ChecksumLocation> newChecksums( List<ChecksumAlgorithmFactory> checksumAlgorithmFactories )
     {
         List<RepositoryLayout.ChecksumLocation> checksums = new ArrayList<>();
-        for ( String factory : factories )
+        for ( ChecksumAlgorithmFactory factory : checksumAlgorithmFactories )
         {
-            checksums.add( newChecksum( factory ) );
+            checksums.add( RepositoryLayout.ChecksumLocation.forLocation( URI.create( "file" ), factory ) );
         }
         return checksums;
     }
@@ -207,7 +218,8 @@ public class ChecksumValidatorTest
 
     private ChecksumValidator newValidator( Map<String, String> providedChecksums, String... factories )
     {
-        return new ChecksumValidator( dataFile, new TestFileProcessor(), fetcher, policy, providedChecksums, newChecksums( factories ) );
+        List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = newChecksumAlgorithmFactories( factories );
+        return new ChecksumValidator( dataFile, checksumAlgorithmFactories, new TestFileProcessor(), fetcher, policy, providedChecksums, newChecksums( checksumAlgorithmFactories ) );
     }
 
     private Map<String, ?> checksums( String... algoDigestPairs )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
index d0b3de9..30c3e8a 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
@@ -26,6 +26,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -54,10 +56,14 @@ public final class Maven2RepositoryLayoutFactory
         implements RepositoryLayoutFactory
 {
 
-    static final String CONFIG_PROP_SIGNATURE_CHECKSUMS = "aether.checksums.forSignature";
-    static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms";
+    public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms";
 
-    static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
+    private static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
+
+    public static final String CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS =
+            "aether.checksums.omitChecksumsForExtensions";
+
+    private static final String DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS = ".asc";
 
     private float priority;
 
@@ -104,23 +110,41 @@ public final class Maven2RepositoryLayoutFactory
         {
             throw new NoRepositoryLayoutException( repository );
         }
-        boolean forSignature = ConfigUtils.getBoolean( session, false, CONFIG_PROP_SIGNATURE_CHECKSUMS );
-        // ensure order of (potentially user set) algorithm list is kept and is unique
-        LinkedHashSet<String> checksumsAlgorithmNames = new LinkedHashSet<>( Arrays.asList(
-                ConfigUtils.getString(
-                        session, DEFAULT_CHECKSUMS_ALGORITHMS, CONFIG_PROP_CHECKSUMS_ALGORITHMS
-                ).split( "," ) )
-        );
+        // ensure order and uniqueness of (potentially user set) algorithm list
+        LinkedHashSet<String> checksumsAlgorithmNames = Arrays.stream( ConfigUtils.getString(
+                        session, DEFAULT_CHECKSUMS_ALGORITHMS, CONFIG_PROP_CHECKSUMS_ALGORITHMS )
+                .split( "," )
+        ).filter( s -> s != null && !s.trim().isEmpty() ).collect( Collectors.toCollection( LinkedHashSet::new ) );
 
+        // validation: this loop implicitly validates the list above: selector will throw on unknown algorithm
         List<ChecksumAlgorithmFactory> checksumsAlgorithms = new ArrayList<>( checksumsAlgorithmNames.size() );
         for ( String checksumsAlgorithmName : checksumsAlgorithmNames )
         {
             checksumsAlgorithms.add( checksumAlgorithmFactorySelector.select( checksumsAlgorithmName ) );
         }
 
-        return forSignature
-                ? new Maven2RepositoryLayout( checksumsAlgorithms )
-                : new Maven2RepositoryLayoutEx( checksumsAlgorithms );
+        // ensure uniqueness of (potentially user set) extension list
+        Set<String> omitChecksumsForExtensions = Arrays.stream( ConfigUtils.getString(
+                        session, DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS, CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS )
+                .split( "," )
+        ).filter( s -> s != null && !s.trim().isEmpty() ).collect( Collectors.toSet() );
+
+        // validation: enforce that all strings in this set are having leading dot
+        if ( omitChecksumsForExtensions.stream().anyMatch( s -> !s.startsWith( "." ) ) )
+        {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "The configuration %s contains illegal values: %s (all entries must start with '.' (dot))",
+                            CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS,
+                            omitChecksumsForExtensions
+                    )
+            );
+        }
+
+        return new Maven2RepositoryLayout(
+                checksumsAlgorithms,
+                omitChecksumsForExtensions
+        );
     }
 
     private static class Maven2RepositoryLayout
@@ -129,9 +153,13 @@ public final class Maven2RepositoryLayoutFactory
 
         private final List<ChecksumAlgorithmFactory> checksumAlgorithms;
 
-        protected Maven2RepositoryLayout( List<ChecksumAlgorithmFactory> checksumAlgorithms )
+        private final Set<String> extensionsWithoutChecksums;
+
+        private Maven2RepositoryLayout( List<ChecksumAlgorithmFactory> checksumAlgorithms,
+                                        Set<String> extensionsWithoutChecksums )
         {
             this.checksumAlgorithms = Collections.unmodifiableList( checksumAlgorithms );
+            this.extensionsWithoutChecksums = requireNonNull( extensionsWithoutChecksums );
         }
 
         private URI toUri( String path )
@@ -153,6 +181,20 @@ public final class Maven2RepositoryLayoutFactory
         }
 
         @Override
+        public boolean hasChecksums( Artifact artifact )
+        {
+            String artifactExtension = artifact.getExtension(); // ie. pom.asc
+            for ( String extensionWithoutChecksums : extensionsWithoutChecksums )
+            {
+                if ( artifactExtension.endsWith( extensionWithoutChecksums ) )
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
         public URI getLocation( Artifact artifact, boolean upload )
         {
             StringBuilder path = new StringBuilder( 128 );
@@ -206,6 +248,10 @@ public final class Maven2RepositoryLayoutFactory
         @Override
         public List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location )
         {
+            if ( !hasChecksums( artifact ) || isChecksum( artifact.getExtension() ) )
+            {
+                return Collections.emptyList();
+            }
             return getChecksumLocations( location );
         }
 
@@ -225,32 +271,9 @@ public final class Maven2RepositoryLayoutFactory
             return checksumLocations;
         }
 
-    }
-
-    private static class Maven2RepositoryLayoutEx
-            extends Maven2RepositoryLayout
-    {
-
-        protected Maven2RepositoryLayoutEx( List<ChecksumAlgorithmFactory> checksumAlgorithms )
-        {
-            super( checksumAlgorithms );
-        }
-
-        @Override
-        public List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location )
-        {
-            if ( isSignature( artifact.getExtension() ) )
-            {
-                return Collections.emptyList();
-            }
-            return super.getChecksumLocations( artifact, upload, location );
-        }
-
-        private boolean isSignature( String extension )
+        private boolean isChecksum( String extension )
         {
-            return extension.endsWith( ".asc" );
+            return checksumAlgorithms.stream().anyMatch( a -> extension.endsWith( "." + a.getFileExtension() ) );
         }
-
     }
-
 }
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java
index 108bf90..0fc7a29 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java
@@ -311,7 +311,7 @@ public class Maven2RepositoryLayoutFactoryTest
     public void testSignatureChecksums_Force()
         throws Exception
     {
-        session.setConfigProperty( Maven2RepositoryLayoutFactory.CONFIG_PROP_SIGNATURE_CHECKSUMS, "true" );
+        session.setConfigProperty( Maven2RepositoryLayoutFactory.CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, "" );
         layout = factory.newInstance( session, newRepo( "default" ) );
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "jar.asc", "1.0" );
         URI uri = layout.getLocation( artifact, true );
@@ -319,4 +319,33 @@ public class Maven2RepositoryLayoutFactoryTest
         assertChecksums( checksums, "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.jar.asc", SHA1, MD5 );
     }
 
+    @Test
+    public void testCustomChecksumsIgnored()
+            throws Exception
+    {
+        session.setConfigProperty( Maven2RepositoryLayoutFactory.CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, ".asc,.foo" );
+        layout = factory.newInstance( session, newRepo( "default" ) );
+        DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "jar.foo", "1.0" );
+        URI uri = layout.getLocation( artifact, true );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, true, uri );
+        assertEquals( 0, checksums.size() );
+    }
+
+    @Test
+    public void testCustomChecksumsIgnored_IllegalInout()
+            throws Exception
+    {
+        session.setConfigProperty( Maven2RepositoryLayoutFactory.CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, ".asc,foo" );
+        try
+        {
+            layout = factory.newInstance( session, newRepo( "default" ) );
+            fail( "Should not get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            String message = e.getMessage();
+            assertTrue( message, message.contains( Maven2RepositoryLayoutFactory.CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS ) );
+        }
+    }
+
 }
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java
index 5bc0aa2..4b958a6 100644
--- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java
@@ -36,7 +36,7 @@ public interface ChecksumAlgorithmFactorySelector
     ChecksumAlgorithmFactory select( String algorithmName );
 
     /**
-     * Returns a collection of supported algorithm names. This set represents ALL the algorithms supported by Resolver,
+     * Returns a collection of supported algorithms. This set represents ALL the algorithms supported by Resolver,
      * and is NOT in any relation to given repository layout used checksums, returned by method {@link
      * org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmFactories()} (in fact, is super set
      * of it).
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java
index 4a02b06..83b4352 100644
--- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java
@@ -127,13 +127,36 @@ public interface RepositoryLayout
 
     /**
      * Returns immutable list of {@link ChecksumAlgorithmFactory} this instance of layout uses, never {@code null}.
-     * The order represents the order how checksums are validated (hence retrieved).
+     * The order also represents the order how remote external checksums are retrieved and validated.
      *
+     * @see org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind
      * @since 1.8.0
      */
     List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories();
 
     /**
+     * Tells whether given artifact have remote external checksums according to current layout or not. If it returns
+     * {@code true}, then layout configured checksums will be expected: on upload they will be calculated and deployed
+     * along artifact, on download they will be retrieved and validated.
+     *
+     * If it returns {@code false} the given artifacts will have checksums omitted: on upload they will not be
+     * calculated and deployed, and on download they will be not retrieved nor validated.
+     *
+     * The result affects only layout provided checksums. See
+     * {@link org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind#REMOTE_EXTERNAL}.
+     * On download, the {@link org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmFactories()}
+     * layout required checksums are calculated, and non layout-provided checksums are still utilized.
+     *
+     * Typical case to return {@code false} (to omit checksums) is for artifact signatures, that are already a
+     * "sub-artifact" of some main artifact (for example a JAR), and they can be validated by some other means.
+     *
+     * @see org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind
+     * @see #getChecksumAlgorithmFactories()
+     * @since 1.8.0
+     */
+    boolean hasChecksums( Artifact artifact );
+
+    /**
      * Gets the location within a remote repository where the specified artifact resides. The URI is relative to the
      * root directory of the repository.
      *
@@ -164,7 +187,8 @@ public interface RepositoryLayout
      *                 being uploaded/created.
      * @param location The relative URI to the artifact within the repository as previously obtained from
      *                 {@link #getLocation(Artifact, boolean)}, must not be {@code null}.
-     * @return The checksum files for the given artifact, possibly empty but never {@code null}.
+     * @return The checksum files for the given artifact, possibly empty but never {@code null}. If empty, that means
+     * that this layout does not provide checksums for given artifact.
      */
     List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location );
 
@@ -177,7 +201,8 @@ public interface RepositoryLayout
      *                 being uploaded/created.
      * @param location The relative URI to the metadata within the repository as previously obtained from
      *                 {@link #getLocation(Metadata, boolean)}, must not be {@code null}.
-     * @return The checksum files for the given metadata, possibly empty but never {@code null}.
+     * @return The checksum files for the given metadata, possibly empty but never {@code null}. If empty, that means
+     * that this layout does not provide checksums for given artifact.
      */
     List<ChecksumLocation> getChecksumLocations( Metadata metadata, boolean upload, URI location );
 
diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md
index 5480f5f..926b688 100644
--- a/src/site/markdown/configuration.md
+++ b/src/site/markdown/configuration.md
@@ -21,7 +21,7 @@ under the License.
 Option | Type | Description | Default Value | Supports Repo ID Suffix
 --- | --- | --- | --- | ---
 `aether.artifactResolver.snapshotNormalization` | boolean | It replaces the timestamped snapshot file name with a filename containing the `SNAPSHOT` qualifier only. This only affects resolving/retrieving artifacts but not uploading those. | `true` | no
-`aether.checksums.forSignature` | boolean | Flag indicating if signature artifacts (`.asc`) should have checksums. | `false` | no
+`aether.checksums.omitChecksumsForExtensions` | String | Comma separated list of extensions with leading dot (example `.asc`) that should have checksums omitted. These are applied to sub-artifacts only. Note: to achieve 1.7.x `aether.checksums.forSignature=true` behaviour, pass empty string as value for this property. | `.asc` | no
 `aether.checksums.algorithms` | String | Comma separated list of checksum algorithms with which checksums are validated (downloaded) and generated (uploaded). Resolver by default supports following algorithms: `MD5`, `SHA-1`, `SHA-256` and `SHA-512`. New algorithms can be added by implementing `ChecksumAlgorithmFactory` component. | `"SHA-1,MD5"` | no
 `aether.conflictResolver.verbose` | boolean | Flag controlling the conflict resolver's verbose mode. | `false` | no
 `aether.connector.basic.threads` or `maven.artifact.threads` | int | Number of threads to use for uploading/downloading. | `5` | no