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/09/14 08:30:56 UTC

[maven-resolver] 01/01: Remote repository filtering (draft)

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

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

commit 7a8aac64fbc0c5c6dce8d1ae039cfdc9625eb89a
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Tue Sep 13 20:24:24 2022 +0200

    Remote repository filtering (draft)
    
    Introduce new feature to be able to filter artifacts for
    given remote repository instance, that would allow things
    like "scoping" a remote repository for given groupId,
    use prefixes published by repository etc.
---
 .../eclipse/aether/impl/DefaultServiceLocator.java |   3 +
 .../internal/impl/DefaultArtifactResolver.java     | 100 +++++++++++-----
 .../internal/impl/DefaultMetadataResolver.java     |  27 ++++-
 .../impl/DefaultRepositoryConnectorProvider.java   |  24 +++-
 .../DefaultRemoteRepositoryFilterSource.java       |  90 ++++++++++++++
 .../impl/filter/FilteringRepositoryConnector.java  | 130 +++++++++++++++++++++
 .../internal/impl/DefaultArtifactResolverTest.java |   6 +-
 .../internal/impl/DefaultMetadataResolverTest.java |   2 +
 .../connector/filter/RemoteRepositoryFilter.java   |  51 ++++++++
 .../filter/RemoteRepositoryFilterSource.java       |  35 ++++++
 10 files changed, 432 insertions(+), 36 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
index 815c07cd..4a3e2b05 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
@@ -56,12 +56,14 @@ import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer;
 import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
 import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
 import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
+import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterSource;
 import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
 import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory;
 import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector;
 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
 import org.eclipse.aether.spi.connector.transport.TransporterProvider;
@@ -230,6 +232,7 @@ public final class DefaultServiceLocator
         addService( NamedLockFactorySelector.class, SimpleNamedLockFactorySelector.class );
         addService( ChecksumAlgorithmFactorySelector.class, DefaultChecksumAlgorithmFactorySelector.class );
         addService( LocalPathComposer.class, DefaultLocalPathComposer.class );
+        addService( RemoteRepositoryFilterSource.class, DefaultRemoteRepositoryFilterSource.class );
     }
 
     private <T> Entry<T> getEntry( Class<T> type, boolean create )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java
index 430ea381..27d39a8e 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java
@@ -45,6 +45,8 @@ import org.eclipse.aether.impl.OfflineController;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.RepositoryConnectorProvider;
 import org.eclipse.aether.impl.RepositoryEventDispatcher;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
 import org.eclipse.aether.impl.UpdateCheck;
 import org.eclipse.aether.impl.UpdateCheckManager;
@@ -106,6 +108,8 @@ public class DefaultArtifactResolver
 
     private OfflineController offlineController;
 
+    private RemoteRepositoryFilterSource remoteRepositoryFilterSource;
+
     public DefaultArtifactResolver()
     {
         // enables default constructor
@@ -117,7 +121,8 @@ public class DefaultArtifactResolver
                              VersionResolver versionResolver, UpdateCheckManager updateCheckManager,
                              RepositoryConnectorProvider repositoryConnectorProvider,
                              RemoteRepositoryManager remoteRepositoryManager, SyncContextFactory syncContextFactory,
-                             OfflineController offlineController )
+                             OfflineController offlineController,
+                             RemoteRepositoryFilterSource remoteRepositoryFilterSource )
     {
         setFileProcessor( fileProcessor );
         setRepositoryEventDispatcher( repositoryEventDispatcher );
@@ -127,6 +132,7 @@ public class DefaultArtifactResolver
         setRemoteRepositoryManager( remoteRepositoryManager );
         setSyncContextFactory( syncContextFactory );
         setOfflineController( offlineController );
+        setRemoteRepositoryFilterSource( remoteRepositoryFilterSource );
     }
 
     public void initService( ServiceLocator locator )
@@ -139,6 +145,7 @@ public class DefaultArtifactResolver
         setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
         setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
         setOfflineController( locator.getService( OfflineController.class ) );
+        setRemoteRepositoryFilterSource( locator.getService( RemoteRepositoryFilterSource.class ) );
     }
 
     /**
@@ -203,6 +210,14 @@ public class DefaultArtifactResolver
         return this;
     }
 
+    public DefaultArtifactResolver setRemoteRepositoryFilterSource(
+            RemoteRepositoryFilterSource remoteRepositoryFilterSource )
+    {
+        this.remoteRepositoryFilterSource = requireNonNull( remoteRepositoryFilterSource,
+                "remote repository filter source cannot be null" );
+        return this;
+    }
+
     public ArtifactResult resolveArtifact( RepositorySystemSession session, ArtifactRequest request )
         throws ArtifactResolutionException
     {
@@ -244,6 +259,7 @@ public class DefaultArtifactResolver
         List<ArtifactResult> results = new ArrayList<>( requests.size() );
         boolean failures = false;
 
+        RemoteRepositoryFilter filter = remoteRepositoryFilterSource.getRemoteRepositoryFilter( session );
         LocalRepositoryManager lrm = session.getLocalRepositoryManager();
         WorkspaceReader workspace = session.getWorkspaceReader();
 
@@ -257,7 +273,6 @@ public class DefaultArtifactResolver
             results.add( result );
 
             Artifact artifact = request.getArtifact();
-            List<RemoteRepository> repos = request.getRepositories();
 
             artifactResolving( session, trace, artifact );
 
@@ -280,10 +295,29 @@ public class DefaultArtifactResolver
                 continue;
             }
 
+            List<RemoteRepository> remoteRepositories = request.getRepositories();
+            List<RemoteRepository> filteredRemoteRepositories = new ArrayList<>( remoteRepositories.size() );
+            for ( RemoteRepository repository : remoteRepositories )
+            {
+                if ( !filter.acceptArtifact( repository, artifact ) )
+                {
+                    result.addException(  new ArtifactNotFoundException( artifact, repository,
+                            "Artifact " + artifact + " not allowed from " + repository ) );
+                }
+                else
+                {
+                    filteredRemoteRepositories.add( repository );
+                }
+            }
+            final boolean remoteConsidered = !remoteRepositories.isEmpty();
+            final boolean filteringApplied = remoteConsidered
+                    && !remoteRepositories.equals( filteredRemoteRepositories );
+
             VersionResult versionResult;
             try
             {
-                VersionRequest versionRequest = new VersionRequest( artifact, repos, request.getRequestContext() );
+                VersionRequest versionRequest = new VersionRequest(
+                        artifact, remoteRepositories, request.getRequestContext() );
                 versionRequest.setTrace( trace );
                 versionResult = versionResolver.resolveVersion( session, versionRequest );
             }
@@ -299,11 +333,11 @@ public class DefaultArtifactResolver
             {
                 if ( versionResult.getRepository() instanceof RemoteRepository )
                 {
-                    repos = Collections.singletonList( (RemoteRepository) versionResult.getRepository() );
+                    remoteRepositories = Collections.singletonList( (RemoteRepository) versionResult.getRepository() );
                 }
                 else
                 {
-                    repos = Collections.emptyList();
+                    remoteRepositories = Collections.emptyList();
                 }
             }
 
@@ -320,49 +354,55 @@ public class DefaultArtifactResolver
                 }
             }
 
-            LocalArtifactResult local =
-                lrm.find( session, new LocalArtifactRequest( artifact, repos, request.getRequestContext() ) );
+            LocalArtifactResult local = lrm.find(
+                    session, new LocalArtifactRequest( artifact, remoteRepositories, request.getRequestContext() ) );
             if ( isLocallyInstalled( local, versionResult ) )
             {
+                boolean originAccepted = true;
                 if ( local.getRepository() != null )
                 {
                     result.setRepository( local.getRepository() );
+                    originAccepted = !filteringApplied || filteredRemoteRepositories.contains( local.getRepository() );
                 }
                 else
                 {
                     result.setRepository( lrm.getRepository() );
                 }
-                try
-                {
-                    artifact = artifact.setFile( getFile( session, artifact, local.getFile() ) );
-                    result.setArtifact( artifact );
-                    artifactResolved( session, trace, artifact, result.getRepository(), null );
-                }
-                catch ( ArtifactTransferException e )
-                {
-                    result.addException( e );
-                }
-                if ( !local.isAvailable() )
+
+                if ( originAccepted )
                 {
-                    /*
-                     * NOTE: Interop with simple local repository: An artifact installed by a simple local repo manager
-                     * will not show up in the repository tracking file of the enhanced local repository. If however the
-                     * maven-metadata-local.xml tells us the artifact was installed locally, we sync the repository
-                     * tracking file.
-                     */
-                    lrm.add( session, new LocalArtifactRegistration( artifact ) );
+                    try
+                    {
+                        artifact = artifact.setFile( getFile( session, artifact, local.getFile() ) );
+                        result.setArtifact( artifact );
+                        artifactResolved( session, trace, artifact, result.getRepository(), null );
+                    }
+                    catch ( ArtifactTransferException e )
+                    {
+                        result.addException( e );
+                    }
+                    if ( !local.isAvailable() )
+                    {
+                        /*
+                         * NOTE: Interop with simple local repository: An artifact installed by a simple local repo
+                         * manager will not show up in the repository tracking file of the enhanced local repository.
+                         * If however the maven-metadata-local.xml tells us the artifact was installed locally, we
+                         * sync the repository tracking file.
+                         */
+                        lrm.add( session, new LocalArtifactRegistration( artifact ) );
+                    }
+                    continue;
                 }
-                continue;
             }
             else if ( local.getFile() != null )
             {
-                LOGGER.debug( "Verifying availability of {} from {}", local.getFile(), repos );
+                LOGGER.debug( "Verifying availability of {} from {}", local.getFile(), remoteRepositories );
             }
 
-            LOGGER.debug( "Resolving artifact {} from {}", artifact, repos );
+            LOGGER.debug( "Resolving artifact {} from {}", artifact, remoteRepositories );
             AtomicBoolean resolved = new AtomicBoolean( false );
             Iterator<ResolutionGroup> groupIt = groups.iterator();
-            for ( RemoteRepository repo : repos )
+            for ( RemoteRepository repo : remoteRepositories )
             {
                 if ( !repo.getPolicy( artifact.isSnapshot() ).isEnabled() )
                 {
@@ -397,7 +437,7 @@ public class DefaultArtifactResolver
                 {
                     group = new ResolutionGroup( repo );
                     groups.add( group );
-                    groupIt = Collections.<ResolutionGroup>emptyList().iterator();
+                    groupIt = Collections.emptyIterator();
                 }
                 group.items.add( new ResolutionItem( trace, artifact, resolved, result, local, repo ) );
             }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java
index f65f0c2f..ad2d1224 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java
@@ -47,6 +47,8 @@ import org.eclipse.aether.impl.OfflineController;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.RepositoryConnectorProvider;
 import org.eclipse.aether.impl.RepositoryEventDispatcher;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
 import org.eclipse.aether.impl.UpdateCheck;
 import org.eclipse.aether.impl.UpdateCheckManager;
@@ -95,6 +97,8 @@ public class DefaultMetadataResolver
 
     private OfflineController offlineController;
 
+    private RemoteRepositoryFilterSource remoteRepositoryFilterSource;
+
     public DefaultMetadataResolver()
     {
         // enables default constructor
@@ -105,7 +109,8 @@ public class DefaultMetadataResolver
                              UpdateCheckManager updateCheckManager,
                              RepositoryConnectorProvider repositoryConnectorProvider,
                              RemoteRepositoryManager remoteRepositoryManager, SyncContextFactory syncContextFactory,
-                             OfflineController offlineController )
+                             OfflineController offlineController,
+                             RemoteRepositoryFilterSource remoteRepositoryFilterSource )
     {
         setRepositoryEventDispatcher( repositoryEventDispatcher );
         setUpdateCheckManager( updateCheckManager );
@@ -113,6 +118,7 @@ public class DefaultMetadataResolver
         setRemoteRepositoryManager( remoteRepositoryManager );
         setSyncContextFactory( syncContextFactory );
         setOfflineController( offlineController );
+        setRemoteRepositoryFilterSource( remoteRepositoryFilterSource );
     }
 
     public void initService( ServiceLocator locator )
@@ -123,6 +129,7 @@ public class DefaultMetadataResolver
         setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
         setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
         setOfflineController( locator.getService( OfflineController.class ) );
+        setRemoteRepositoryFilterSource( locator.getService( RemoteRepositoryFilterSource.class ) );
     }
 
     public DefaultMetadataResolver setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher )
@@ -165,6 +172,14 @@ public class DefaultMetadataResolver
         return this;
     }
 
+    public DefaultMetadataResolver setRemoteRepositoryFilterSource(
+            RemoteRepositoryFilterSource remoteRepositoryFilterSource )
+    {
+        this.remoteRepositoryFilterSource = requireNonNull( remoteRepositoryFilterSource,
+                "remote repository filter cannot be null" );
+        return this;
+    }
+
     public List<MetadataResult> resolveMetadata( RepositorySystemSession session,
                                                  Collection<? extends MetadataRequest> requests )
     {
@@ -194,6 +209,9 @@ public class DefaultMetadataResolver
 
         Map<File, Long> localLastUpdates = new HashMap<>();
 
+        RemoteRepositoryFilter remoteRepositoryFilter = remoteRepositoryFilterSource
+                .getRemoteRepositoryFilter( session );
+
         for ( MetadataRequest request : requests )
         {
             RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
@@ -226,6 +244,13 @@ public class DefaultMetadataResolver
                 continue;
             }
 
+            if ( !remoteRepositoryFilter.acceptMetadata( repository, metadata ) )
+            {
+                result.setException( new MetadataNotFoundException( metadata, repository,
+                        "Metadata " + metadata + " not allowed from " + repository ) );
+                continue;
+            }
+
             List<RemoteRepository> repositories = getEnabledSourceRepositories( repository, metadata.getNature() );
 
             if ( repositories.isEmpty() )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryConnectorProvider.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryConnectorProvider.java
index e1f0b319..5a36c69e 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryConnectorProvider.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryConnectorProvider.java
@@ -31,11 +31,13 @@ import javax.inject.Singleton;
 
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.impl.RepositoryConnectorProvider;
+import org.eclipse.aether.internal.impl.filter.FilteringRepositoryConnector;
 import org.eclipse.aether.repository.Authentication;
 import org.eclipse.aether.repository.Proxy;
 import org.eclipse.aether.repository.RemoteRepository;
 import org.eclipse.aether.spi.connector.RepositoryConnector;
 import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
 import org.eclipse.aether.spi.locator.Service;
 import org.eclipse.aether.spi.locator.ServiceLocator;
 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
@@ -54,20 +56,25 @@ public class DefaultRepositoryConnectorProvider
 
     private Collection<RepositoryConnectorFactory> connectorFactories = new ArrayList<>();
 
+    private RemoteRepositoryFilterSource remoteRepositoryFilterSource;
+
     public DefaultRepositoryConnectorProvider()
     {
         // enables default constructor
     }
 
     @Inject
-    DefaultRepositoryConnectorProvider( Set<RepositoryConnectorFactory> connectorFactories )
+    DefaultRepositoryConnectorProvider( Set<RepositoryConnectorFactory> connectorFactories,
+                                        RemoteRepositoryFilterSource remoteRepositoryFilterSource )
     {
         setRepositoryConnectorFactories( connectorFactories );
+        setRemoteRepositoryFilterSource( remoteRepositoryFilterSource );
     }
 
     public void initService( ServiceLocator locator )
     {
-        connectorFactories = locator.getServices( RepositoryConnectorFactory.class );
+        setRepositoryConnectorFactories( locator.getServices( RepositoryConnectorFactory.class ) );
+        setRemoteRepositoryFilterSource( locator.getService( RemoteRepositoryFilterSource.class ) );
     }
 
     public DefaultRepositoryConnectorProvider addRepositoryConnectorFactory( RepositoryConnectorFactory factory )
@@ -90,6 +97,13 @@ public class DefaultRepositoryConnectorProvider
         return this;
     }
 
+    public DefaultRepositoryConnectorProvider setRemoteRepositoryFilterSource(
+            RemoteRepositoryFilterSource remoteRepositoryFilterSource )
+    {
+        this.remoteRepositoryFilterSource = requireNonNull( remoteRepositoryFilterSource );
+        return this;
+    }
+
     public RepositoryConnector newRepositoryConnector( RepositorySystemSession session, RemoteRepository repository )
         throws NoRepositoryConnectorException
     {
@@ -137,7 +151,11 @@ public class DefaultRepositoryConnectorProvider
                     LOGGER.debug( buffer.toString() );
                 }
 
-                return connector;
+                return new FilteringRepositoryConnector(
+                        repository,
+                        connector,
+                        remoteRepositoryFilterSource.getRemoteRepositoryFilter( session )
+                );
             }
             catch ( NoRepositoryConnectorException e )
             {
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterSource.java
new file mode 100644
index 00000000..2773c9cb
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterSource.java
@@ -0,0 +1,90 @@
+package org.eclipse.aether.internal.impl.filter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
+import org.eclipse.aether.util.ConfigUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default implementation of remote repository filter source.
+ *
+ * @since TBD
+ */
+@Singleton
+@Named
+public final class DefaultRemoteRepositoryFilterSource
+        implements RemoteRepositoryFilterSource
+{
+    private static final String CONFIG_PROP_VALUE = "aether.remoteRepositoryFilterSource.filter";
+
+    private static final String INSTANCE_KEY = "aether.remoteRepositoryFilterSource.instance";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultRemoteRepositoryFilterSource.class );
+
+    private static final class StaticRemoteRepositoryFilter implements RemoteRepositoryFilter
+    {
+        private final boolean value;
+
+        private StaticRemoteRepositoryFilter( boolean value )
+        {
+            this.value = value;
+        }
+
+        @Override
+        public boolean acceptArtifact( RemoteRepository remoteRepository, Artifact artifact )
+        {
+            return value;
+        }
+
+        @Override
+        public boolean acceptMetadata( RemoteRepository remoteRepository, Metadata metadata )
+        {
+            return value;
+        }
+    }
+
+    private static final StaticRemoteRepositoryFilter ALWAYS_ALLOW_ALL = new StaticRemoteRepositoryFilter( true );
+
+    @Override
+    public RemoteRepositoryFilter getRemoteRepositoryFilter( RepositorySystemSession session )
+    {
+        // TODO: implement several strategies
+        // - user provided G/A/V list
+        // - auto discovery of prefixes
+        // - maybe blacklist? like vuln log4j?
+        boolean value = ConfigUtils.getBoolean( session, true, CONFIG_PROP_VALUE );
+        return (RemoteRepositoryFilter) session.getData().computeIfAbsent( INSTANCE_KEY, () ->
+                {
+                    LOGGER.debug( "Creating static filter with value={}", value );
+                    return new StaticRemoteRepositoryFilter( value );
+                }
+        );
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/FilteringRepositoryConnector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/FilteringRepositoryConnector.java
new file mode 100644
index 00000000..0dfc2ffb
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/FilteringRepositoryConnector.java
@@ -0,0 +1,130 @@
+package org.eclipse.aether.internal.impl.filter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.ArtifactDownload;
+import org.eclipse.aether.spi.connector.ArtifactUpload;
+import org.eclipse.aether.spi.connector.MetadataDownload;
+import org.eclipse.aether.spi.connector.MetadataUpload;
+import org.eclipse.aether.spi.connector.RepositoryConnector;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+import org.eclipse.aether.transfer.MetadataNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A filtering connector that filter transfers using remote repository filter and delegates to another connector.
+ *
+ * @since TBD
+ */
+public final class FilteringRepositoryConnector
+        implements RepositoryConnector
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger( FilteringRepositoryConnector.class );
+
+    private final RemoteRepository remoteRepository;
+
+    private final RepositoryConnector repositoryConnector;
+
+    private final RemoteRepositoryFilter remoteRepositoryFilter;
+
+    public FilteringRepositoryConnector( RemoteRepository remoteRepository,
+                                         RepositoryConnector repositoryConnector,
+                                         RemoteRepositoryFilter remoteRepositoryFilter )
+    {
+        this.remoteRepository = requireNonNull( remoteRepository );
+        this.repositoryConnector = requireNonNull( repositoryConnector );
+        this.remoteRepositoryFilter = requireNonNull( remoteRepositoryFilter );
+    }
+
+    @Override
+    public void close()
+    {
+        repositoryConnector.close();
+    }
+
+    @Override
+    public void get( Collection<? extends ArtifactDownload> artifactDownloads,
+                     Collection<? extends MetadataDownload> metadataDownloads )
+    {
+        List<ArtifactDownload> filteredArtifactDownloads = null;
+        if ( artifactDownloads != null && !artifactDownloads.isEmpty() )
+        {
+            filteredArtifactDownloads = new ArrayList<>( artifactDownloads.size() );
+            for ( ArtifactDownload artifactDownload : artifactDownloads )
+            {
+                if ( remoteRepositoryFilter.acceptArtifact( remoteRepository, artifactDownload.getArtifact() ) )
+                {
+                    filteredArtifactDownloads.add( artifactDownload );
+                }
+                else
+                {
+                    LOGGER.debug( "Artifact {} filtered out from remote repository {}",
+                            artifactDownload.getArtifact(), remoteRepository );
+                    artifactDownload.setException( new ArtifactNotFoundException( artifactDownload.getArtifact(),
+                            remoteRepository, "Artifact " + artifactDownload.getArtifact() + " not allowed from "
+                            + remoteRepository ) );
+                }
+            }
+        }
+        List<MetadataDownload> filteredMetadataDownloads = null;
+        if ( metadataDownloads != null && !metadataDownloads.isEmpty() )
+        {
+            filteredMetadataDownloads = new ArrayList<>( metadataDownloads.size() );
+            for ( MetadataDownload metadataDownload : metadataDownloads )
+            {
+                if ( remoteRepositoryFilter.acceptMetadata( remoteRepository, metadataDownload.getMetadata() ) )
+                {
+                    filteredMetadataDownloads.add( metadataDownload );
+                }
+                else
+                {
+                    LOGGER.debug( "Metadata {} filtered out from remote repository {}",
+                            metadataDownload.getMetadata(), remoteRepository );
+                    metadataDownload.setException( new MetadataNotFoundException( metadataDownload.getMetadata(),
+                            remoteRepository, "Metadata " + metadataDownload.getMetadata() + " not allowed from "
+                            + remoteRepository ) );
+                }
+            }
+        }
+        repositoryConnector.get( filteredArtifactDownloads, filteredMetadataDownloads );
+    }
+
+    @Override
+    public void put( Collection<? extends ArtifactUpload> artifactUploads,
+                     Collection<? extends MetadataUpload> metadataUploads )
+    {
+        repositoryConnector.put( artifactUploads, metadataUploads );
+    }
+
+    @Override
+    public String toString()
+    {
+        return "filtered(" + repositoryConnector.toString() + ")";
+    }
+}
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
index 65ad4880..17c98caa 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
@@ -38,6 +38,7 @@ import org.eclipse.aether.artifact.ArtifactProperties;
 import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.impl.UpdateCheckManager;
 import org.eclipse.aether.impl.VersionResolver;
+import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterSource;
 import org.eclipse.aether.internal.test.util.TestFileProcessor;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
@@ -103,6 +104,7 @@ public class DefaultArtifactResolverTest
         resolver.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
         resolver.setSyncContextFactory( new StubSyncContextFactory() );
         resolver.setOfflineController( new DefaultOfflineController() );
+        resolver.setRemoteRepositoryFilterSource( new DefaultRemoteRepositoryFilterSource() );
 
         artifact = new DefaultArtifact( "gid", "aid", "", "ext", "ver" );
 
@@ -742,7 +744,7 @@ public class DefaultArtifactResolverTest
 
             public LocalRepository getRepository()
             {
-                return null;
+                return new LocalRepository( new File("") );
             }
 
             public String getPathForRemoteMetadata( Metadata metadata, RemoteRepository repository, String context )
@@ -825,7 +827,7 @@ public class DefaultArtifactResolverTest
 
             public LocalRepository getRepository()
             {
-                return null;
+                return new LocalRepository( new File( "" ) );
             }
 
             public String getPathForRemoteMetadata( Metadata metadata, RemoteRepository repository, String context )
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java
index 27fec64c..e0c26aa9 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java
@@ -31,6 +31,7 @@ import java.util.Set;
 
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.internal.impl.DefaultMetadataResolver;
+import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterSource;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
 import org.eclipse.aether.internal.test.util.TestUtils;
@@ -80,6 +81,7 @@ public class DefaultMetadataResolverTest
         resolver.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
         resolver.setSyncContextFactory( new StubSyncContextFactory() );
         resolver.setOfflineController( new DefaultOfflineController() );
+        resolver.setRemoteRepositoryFilterSource( new DefaultRemoteRepositoryFilterSource() );
         repository =
             new RemoteRepository.Builder( "test-DMRT", "default",
                                           TestFileUtils.createTempDir().toURI().toURL().toString() ).build();
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilter.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilter.java
new file mode 100644
index 00000000..6a5dbd68
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilter.java
@@ -0,0 +1,51 @@
+package org.eclipse.aether.spi.connector.filter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+import org.eclipse.aether.repository.RemoteRepository;
+
+/**
+ * Remote repository filter that decides should the given artifact or metadata be accepted (for further processing)
+ * from remote repository or not.
+ *
+ * @since TBD
+ */
+public interface RemoteRepositoryFilter
+{
+    /**
+     * Decides should artifact be accepted from given remote repository.
+     *
+     * @param remoteRepository The remote repository, not {@code null}.
+     * @param artifact         The artifact, not {@code null}.
+     * @return {@code true} if the artifact should be accepted.
+     */
+    boolean acceptArtifact( RemoteRepository remoteRepository, Artifact artifact );
+
+    /**
+     * Decides should metadata be accepted from given remote repository.
+     *
+     * @param remoteRepository The remote repository, not {@code null}.
+     * @param metadata         The artifact, not {@code null}.
+     * @return {@code true} if the metadata should be accepted.
+     */
+    boolean acceptMetadata( RemoteRepository remoteRepository, Metadata metadata );
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilterSource.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilterSource.java
new file mode 100644
index 00000000..86bfb3d8
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilterSource.java
@@ -0,0 +1,35 @@
+package org.eclipse.aether.spi.connector.filter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * Remote repository filter source component.
+ *
+ * @since TBD
+ */
+public interface RemoteRepositoryFilterSource
+{
+    /**
+     * Provides the filter instance, never {@code null}.
+     */
+    RemoteRepositoryFilter getRemoteRepositoryFilter( RepositorySystemSession session );
+}