All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.aether.internal.impl.resolution.TrustedChecksumsArtifactResolverPostProcessor Maven / Gradle / Ivy

There is a newer version: 3.0.0-alpha-3
Show newest version
/*
 * 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.
 */
package org.eclipse.aether.internal.impl.resolution;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.repository.ArtifactRepository;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.checksums.TrustedChecksumsSource;
import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper;
import org.eclipse.aether.transfer.ChecksumFailureException;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.artifact.ArtifactIdUtils;

import static java.util.Objects.requireNonNull;

/**
 * Artifact resolver processor that verifies the checksums of all resolved artifacts against trusted checksums. Is also
 * able to "record" (calculate and write them) to trusted checksum sources, that do support this operation.
 * 

* It uses a list of {@link ChecksumAlgorithmFactory}ies to work with, by default SHA-1. *

* Configuration keys: *

    *
  • {@code aether.artifactResolver.postProcessor.trustedChecksums.checksumAlgorithms} - Comma separated * list of {@link ChecksumAlgorithmFactory} names to use (default "SHA-1").
  • *
  • {@code aether.artifactResolver.postProcessor.trustedChecksums.failIfMissing} - To fail if artifact * being validated is missing a trusted checksum (default {@code false}).
  • *
  • {@code aether.artifactResolver.postProcessor.trustedChecksums.snapshots} - Should snapshot artifacts be * handled (validated or recorded). Snapshots are by "best practice" in-house produced, hence should be trusted * (default {@code false}).
  • *
  • {@code aether.artifactResolver.postProcessor.trustedChecksums.record} - If this value set to {@code true}, * this component with not validate but "record" encountered artifact checksums instead * (default {@code false}).
  • *
*

* This component uses {@link TrustedChecksumsSource} as source of checksums for validation and also to "record" the * calculated checksums. To have this component usable, there must exist at least one enabled checksum source. In case * of multiple checksum sources enabled, ALL of them are used as source for validation or recording. This * implies that if two enabled checksum sources "disagree" about an artifact checksum, the validation failure is * inevitable. * * @since 1.9.0 */ @Singleton @Named(TrustedChecksumsArtifactResolverPostProcessor.NAME) public final class TrustedChecksumsArtifactResolverPostProcessor extends ArtifactResolverPostProcessorSupport { public static final String NAME = "trustedChecksums"; private static final String CONF_NAME_CHECKSUM_ALGORITHMS = "checksumAlgorithms"; private static final String DEFAULT_CHECKSUM_ALGORITHMS = "SHA-1"; private static final String CONF_NAME_FAIL_IF_MISSING = "failIfMissing"; private static final String CONF_NAME_SNAPSHOTS = "snapshots"; private static final String CONF_NAME_RECORD = "record"; private static final String CHECKSUM_ALGORITHMS_CACHE_KEY = TrustedChecksumsArtifactResolverPostProcessor.class.getName() + ".checksumAlgorithms"; private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector; private final Map trustedChecksumsSources; @Inject public TrustedChecksumsArtifactResolverPostProcessor( ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector, Map trustedChecksumsSources) { super(NAME); this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector); this.trustedChecksumsSources = requireNonNull(trustedChecksumsSources); } @SuppressWarnings("unchecked") @Override protected void doPostProcess(RepositorySystemSession session, List artifactResults) { final List checksumAlgorithms = (List) session.getData() .computeIfAbsent( CHECKSUM_ALGORITHMS_CACHE_KEY, () -> checksumAlgorithmFactorySelector.selectList( ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString( session, DEFAULT_CHECKSUM_ALGORITHMS, configPropKey(CONF_NAME_CHECKSUM_ALGORITHMS))))); final boolean failIfMissing = ConfigUtils.getBoolean(session, false, configPropKey(CONF_NAME_FAIL_IF_MISSING)); final boolean record = ConfigUtils.getBoolean(session, false, configPropKey(CONF_NAME_RECORD)); final boolean snapshots = ConfigUtils.getBoolean(session, false, configPropKey(CONF_NAME_SNAPSHOTS)); for (ArtifactResult artifactResult : artifactResults) { if (artifactResult.getArtifact().isSnapshot() && !snapshots) { continue; } if (artifactResult.isResolved()) { if (record) { recordArtifactChecksums(session, artifactResult, checksumAlgorithms); } else if (!validateArtifactChecksums(session, artifactResult, checksumAlgorithms, failIfMissing)) { artifactResult.setArtifact(artifactResult.getArtifact().setFile(null)); // make it unresolved } } } } /** * Calculates and records checksums into trusted sources that support writing. */ private void recordArtifactChecksums( RepositorySystemSession session, ArtifactResult artifactResult, List checksumAlgorithmFactories) { Artifact artifact = artifactResult.getArtifact(); ArtifactRepository artifactRepository = artifactResult.getRepository(); try { final Map calculatedChecksums = ChecksumAlgorithmHelper.calculate(artifact.getFile(), checksumAlgorithmFactories); for (TrustedChecksumsSource trustedChecksumsSource : trustedChecksumsSources.values()) { TrustedChecksumsSource.Writer writer = trustedChecksumsSource.getTrustedArtifactChecksumsWriter(session); if (writer != null) { try { writer.addTrustedArtifactChecksums( artifact, artifactRepository, checksumAlgorithmFactories, calculatedChecksums); } catch (IOException e) { throw new UncheckedIOException( "Could not write required checksums for " + artifact.getFile(), e); } } } } catch (IOException e) { throw new UncheckedIOException("Could not calculate required checksums for " + artifact.getFile(), e); } } /** * Validates trusted checksums against {@link ArtifactResult}, returns {@code true} denoting "valid" checksums or * {@code false} denoting "invalid" checksums. */ private boolean validateArtifactChecksums( RepositorySystemSession session, ArtifactResult artifactResult, List checksumAlgorithmFactories, boolean failIfMissing) { Artifact artifact = artifactResult.getArtifact(); ArtifactRepository artifactRepository = artifactResult.getRepository(); boolean valid = true; boolean validated = false; try { // full set: calculate all algorithms we were asked for final Map calculatedChecksums = ChecksumAlgorithmHelper.calculate(artifact.getFile(), checksumAlgorithmFactories); for (Map.Entry entry : trustedChecksumsSources.entrySet()) { final String trustedSourceName = entry.getKey(); final TrustedChecksumsSource trustedChecksumsSource = entry.getValue(); // upper bound set: ask source for checksums, ideally same as calculatedChecksums but may be less Map trustedChecksums = trustedChecksumsSource.getTrustedArtifactChecksums( session, artifact, artifactRepository, checksumAlgorithmFactories); if (trustedChecksums == null) { continue; // not enabled } validated = true; if (!calculatedChecksums.equals(trustedChecksums)) { Set missingTrustedAlg = new HashSet<>(calculatedChecksums.keySet()); missingTrustedAlg.removeAll(trustedChecksums.keySet()); if (!missingTrustedAlg.isEmpty() && failIfMissing) { artifactResult.addException(new ChecksumFailureException("Missing from " + trustedSourceName + " trusted checksum(s) " + missingTrustedAlg + " for artifact " + ArtifactIdUtils.toId(artifact))); valid = false; } // compare values but only present ones, failIfMissing handled above // we still want to report all: algX - missing, algY - mismatch, etc for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) { String calculatedChecksum = calculatedChecksums.get(checksumAlgorithmFactory.getName()); String trustedChecksum = trustedChecksums.get(checksumAlgorithmFactory.getName()); if (trustedChecksum != null && !Objects.equals(calculatedChecksum, trustedChecksum)) { artifactResult.addException(new ChecksumFailureException("Artifact " + ArtifactIdUtils.toId(artifact) + " trusted checksum mismatch: " + trustedSourceName + "=" + trustedChecksum + "; calculated=" + calculatedChecksum)); valid = false; } } } } if (!validated && failIfMissing) { artifactResult.addException(new ChecksumFailureException( "There are no enabled trusted checksums" + " source(s) to validate against.")); valid = false; } } catch (IOException e) { throw new UncheckedIOException(e); } return valid; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy