blob: 18226aa78406f81564c42878907bd701fc7f4129 [file] [log] [blame]
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ 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.apache.sling.cli.impl.release;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Locale;
import org.apache.sling.cli.impl.Command;
import org.apache.sling.cli.impl.nexus.Artifact;
import org.apache.sling.cli.impl.nexus.LocalRepository;
import org.apache.sling.cli.impl.nexus.RepositoryDownloader;
import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
import org.apache.sling.cli.impl.pgp.PGPSignaturesValidator;
import org.apache.sling.cli.impl.pgp.SHA1HashResult;
import org.apache.sling.cli.impl.pgp.SHA1HashValidator;
import org.apache.sling.cli.impl.pgp.SignatureVerificationResult;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.util.encoders.Hex;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
@Component(service = Command.class,
property = {
Command.PROPERTY_NAME_COMMAND_GROUP + "=" + VerifyReleasesCommand.GROUP,
Command.PROPERTY_NAME_COMMAND_NAME + "=" + VerifyReleasesCommand.NAME
})
@CommandLine.Command(name = VerifyReleasesCommand.NAME,
description = "Downloads the staging repository and verifies the artifacts' signatures and hashes.",
subcommands = CommandLine.HelpCommand.class)
public class VerifyReleasesCommand implements Command {
private static final Logger LOGGER = LoggerFactory.getLogger(VerifyReleasesCommand.class);
static final String GROUP = "release";
static final String NAME = "verify";
@Reference
private StagingRepositoryFinder stagingRepositoryFinder;
@Reference
private RepositoryDownloader repositoryDownloader;
@Reference
private PGPSignaturesValidator pgpSignaturesValidator;
@Reference
private SHA1HashValidator sha1HashValidator;
@CommandLine.Option(names = {"-r", "--repository"},
description = "Nexus repository id",
required = true)
private Integer repositoryId;
@CommandLine.Mixin
private ReusableCLIOptions reusableCLIOptions;
@Override
public void run() {
try {
LocalRepository repository = repositoryDownloader.download(stagingRepositoryFinder.find(repositoryId));
Path repositoryRootPath = repository.getRootFolder();
for (Artifact artifact : repository.getArtifacts()) {
Path artifactFilePath = repositoryRootPath.resolve(artifact.getRepositoryRelativePath());
Path artifactSignaturePath = repositoryRootPath.resolve(artifact.getRepositoryRelativeSignaturePath());
SignatureVerificationResult signatureVerificationResult = pgpSignaturesValidator.verify(artifactFilePath,
artifactSignaturePath);
SHA1HashResult sha1HashResult = sha1HashValidator.validate(artifactFilePath,
repositoryRootPath.resolve(artifact.getRepositoryRelativeSha1SumPath()));
LOGGER.info("\n" + artifactFilePath.getFileName().toString());
PGPPublicKey key = signatureVerificationResult.getKey();
LOGGER.info("GPG: {}", signatureVerificationResult.isValid() ? String.format("signed by %s with key (id=0x%X; " +
"fingerprint=%s)", getKeyUserId(key), key.getKeyID(),
Hex.toHexString(key.getFingerprint()).toUpperCase(Locale.US)) : "INVALID");
LOGGER.info("SHA-1: {}", sha1HashResult.isValid() ? String.format("VALID (%s)", sha1HashResult.getActualHash()) :
String.format("INVALID (expected %s, got %s)", sha1HashResult.getExpectedHash(), sha1HashResult.getActualHash()));
}
} catch (IOException e) {
LOGGER.error("Command execution failed.", e);
}
}
private String getKeyUserId(PGPPublicKey key) {
Iterator<String> iterator = key.getUserIDs();
return iterator.hasNext() ? iterator.next() : "unknown";
}
}