From 2e2018dbacab91af76b4fb340f902d0a02d5f4e0 Mon Sep 17 00:00:00 2001 From: Martin Stockhammer Date: Sat, 4 May 2019 21:18:26 +0200 Subject: [PATCH] Improving checksum implementation. Adding multiple algorithms. --- .../org/apache/archiva/checksum/Checksum.java | 58 -------- .../archiva/checksum/ChecksumAlgorithm.java | 40 ++++-- .../apache/archiva/checksum/ChecksumUtil.java | 100 +++++++++++++ .../archiva/checksum/ChecksummedFile.java | 134 +++++++++--------- .../apache/archiva/checksum/UpdateStatus.java | 89 ++++++++++++ .../archiva/checksum/UpdateStatusList.java | 87 ++++++++++++ .../apache/archiva/checksum/ChecksumTest.java | 5 +- .../archiva/checksum/ChecksummedFileTest.java | 8 +- .../ArchivaRuntimeConfiguration.java | 54 +++++++ .../registry/ConfigurationRegistryReader.java | 3 + .../registry/ConfigurationRegistryWriter.java | 16 +++ .../archiva/configuration/default-archiva.xml | 8 ++ .../ArtifactMissingChecksumsConsumer.java | 79 +++++------ .../core/ValidateChecksumConsumer.java | 2 +- .../core/AbstractArtifactConsumerTest.java | 3 + .../archiva/policies/ChecksumPolicy.java | 3 +- .../web/api/DefaultFileUploadService.java | 29 +++- .../archiva/upload/UploadArtifactsTest.java | 23 +++ 18 files changed, 548 insertions(+), 193 deletions(-) create mode 100644 archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java create mode 100644 archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatus.java create mode 100644 archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatusList.java diff --git a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/Checksum.java b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/Checksum.java index 5dd05d582..2a198877e 100644 --- a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/Checksum.java +++ b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/Checksum.java @@ -19,60 +19,22 @@ package org.apache.archiva.checksum; * under the License. */ -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.NullOutputStream; - -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.List; /** * Checksum - simple checksum hashing routines. */ public class Checksum { - private static final int BUFFER_SIZE = 32768; private byte[] result = new byte[0]; - public static void update( List checksums, Path file ) - throws ChecksumValidationException - { - long fileSize; - try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ )) { - fileSize = channel.size(); - long pos = 0; - while (pos getExtensions() { + public static Set getAllExtensions() { return extensionMap.keySet(); } @@ -74,7 +78,7 @@ public enum ChecksumAlgorithm { private final String algorithm; /** - * The file extension for this ChecksumAlgorithm. + * The file extensions for this ChecksumAlgorithm. */ private final List ext; @@ -87,8 +91,8 @@ public enum ChecksumAlgorithm { * Construct a ChecksumAlgorithm * * @param algorithm the MessageDigest algorithm - * @param ext the file extension. - * @param type the checksum type. + * @param type a unique identifier for the type + * @param ext the list of file extensions */ private ChecksumAlgorithm( String algorithm, String type, String... ext ) { @@ -98,20 +102,40 @@ public enum ChecksumAlgorithm { } + /** + * Returns the message digest algorithm identifier + * @return + */ public String getAlgorithm() { return algorithm; } + /** + * Returns the list of extensions + * @return + */ public List getExt() { return ext; } + /** + * Returns the checksum identifier + * @return + */ public String getType() { return type; } + + /** + * Returns the default extension of the current algorithm + * @return + */ + public String getDefaultExtension() { + return ext.get(0); + } } diff --git a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java new file mode 100644 index 000000000..5a11b910f --- /dev/null +++ b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java @@ -0,0 +1,100 @@ +package org.apache.archiva.checksum; + +/* + * 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.io.FileNotFoundException; +import java.io.IOException; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Utility class that handles multiple checksums for a single file. + */ +public class ChecksumUtil { + + + static final int BUFFER_SIZE = 32768; + + public static void update(List checksumList, Path file ) throws IOException { + long fileSize; + try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ )) { + fileSize = channel.size(); + long pos = 0; + while (pos < fileSize) { + long bufferSize = Math.min(BUFFER_SIZE, fileSize - pos); + MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, pos, bufferSize); + for (Checksum checksum : checksumList) { + checksum.update(buffer); + buffer.rewind(); + } + fileSize = channel.size(); + pos += BUFFER_SIZE; + } + for (Checksum checksum : checksumList) { + checksum.finish(); + } + } + } + + public static void update(Checksum checksum, Path file) + throws IOException + { + long fileSize; + try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ )) { + fileSize = channel.size(); + long pos = 0; + while (pos initializeChecksums(Path file, List checksumAlgorithms) throws IOException { + final List checksums = newChecksums(checksumAlgorithms); + update(checksums, file); + return checksums; + } + + /** + * Returns the list of configured checksum types. + * + * @param checksumTypes The list of checksum strings + * @return The list of checksum objects + */ + public static List getAlgorithms(List checksumTypes) { + return checksumTypes.stream().map(ca -> + ChecksumAlgorithm.valueOf(ca.toUpperCase())).collect(Collectors.toList()); + } + + public static List newChecksums(List checksumAlgorithms) { + return checksumAlgorithms.stream().map( a -> new Checksum(a)).collect(Collectors.toList()); + } +} diff --git a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksummedFile.java b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksummedFile.java index b8ff4e338..1279bdcd5 100644 --- a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksummedFile.java +++ b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksummedFile.java @@ -23,7 +23,7 @@ import org.apache.archiva.common.utils.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.xml.bind.ValidationException; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; @@ -98,21 +98,21 @@ public class ChecksummedFile { Checksum checksum = new Checksum( checksumAlgorithm ); - checksum.update( referenceFile ); + ChecksumUtil.update(checksum, referenceFile ); return checksum.getChecksum( ); } /** - * Creates a checksum file of the provided referenceFile. + * Writes a checksum file for the referenceFile. * * @param checksumAlgorithm the hash to use. * @return the checksum File that was created. * @throws IOException if there was a problem either reading the referenceFile, or writing the checksum file. */ - public Path createChecksum( ChecksumAlgorithm checksumAlgorithm ) + public Path writeFile(ChecksumAlgorithm checksumAlgorithm ) throws IOException { - Path checksumFile = referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getExt( ).get( 0 ) ); + Path checksumFile = referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getDefaultExtension() ); Files.deleteIfExists( checksumFile ); String checksum = calculateChecksum( checksumAlgorithm ); Files.write( checksumFile, // @@ -123,6 +123,8 @@ public class ChecksummedFile /** * Get the checksum file for the reference file and hash. + * It returns a file for the given checksum, if one exists with one of the possible extensions. + * If it does not exist, a default path will be returned. * * @param checksumAlgorithm the hash that we are interested in. * @return the checksum file to return @@ -137,7 +139,7 @@ public class ChecksummedFile return file; } } - return referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getExt( ).get( 0 ) ); + return referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getDefaultExtension() ); } /** @@ -175,66 +177,65 @@ public class ChecksummedFile } /** - * Checks if the checksums are valid for the referenced file. + * Checks if the checksum files are valid for the referenced file. + * It tries to find a checksum file for each algorithm in the same directory as the referenceFile. + * The method returns true, if at least one checksum file exists for one of the given algorithms + * and all existing checksum files are valid. + * * This method throws only exceptions, if throwExceptions is true. Otherwise false will be returned instead. + * + * It verifies only the existing checksum files. If the checksum file for a particular algorithm does not exist, + * but others exist and are valid, it will return true. + * * @param algorithms The algorithms to verify * @param throwExceptions If true, exceptions will be thrown, otherwise false will be returned, if a exception occurred. - * @return True, if it is valid, otherwise false. + * @return True, if it is valid for all existing checksum files, otherwise false. * @throws ChecksumValidationException */ public boolean isValidChecksums( List algorithms, boolean throwExceptions) throws ChecksumValidationException { - List checksums = new ArrayList<>( algorithms.size() ); - // Create checksum object for each algorithm. - for ( ChecksumAlgorithm checksumAlgorithm : algorithms ) - { - Path checksumFile = getChecksumFile( checksumAlgorithm ); - - // Only add algorithm if checksum file exists. - if ( Files.exists( checksumFile ) ) - { - checksums.add( new Checksum( checksumAlgorithm ) ); - } - } - - // Any checksums? - if ( checksums.isEmpty( ) ) - { - // No checksum objects, no checksum files, default to is invalid. - return false; - } - + List checksums; // Parse file once, for all checksums. try { - Checksum.update( checksums, referenceFile ); + checksums = ChecksumUtil.initializeChecksums( referenceFile, algorithms ); } - catch ( ChecksumValidationException e ) + catch (IOException e ) { log.warn( "Unable to update checksum:{}", e.getMessage( ) ); if (throwExceptions) { - throw e; + if (e instanceof FileNotFoundException) { + throw new ChecksumValidationException(ChecksumValidationException.ValidationError.FILE_NOT_FOUND, e); + } else { + throw new ChecksumValidationException(ChecksumValidationException.ValidationError.READ_ERROR, e); + } } else { return false; } } boolean valid = true; + boolean fileExists = false; + + // No file exists -> return false + // if at least one file exists: + // -> all existing files must be valid // check the checksum files try { + for ( Checksum checksum : checksums ) { ChecksumAlgorithm checksumAlgorithm = checksum.getAlgorithm( ); Path checksumFile = getChecksumFile( checksumAlgorithm ); - String expectedChecksum = parseChecksum( checksumFile, checksumAlgorithm, referenceFile.getFileName( ).toString( ), FILE_ENCODING ); + if (Files.exists(checksumFile)) { + fileExists = true; + String expectedChecksum = parseChecksum(checksumFile, checksumAlgorithm, referenceFile.getFileName().toString(), FILE_ENCODING); - if ( !checksum.compare( expectedChecksum ) ) - { - valid = false; + valid &= checksum.compare(expectedChecksum); } } } @@ -249,7 +250,7 @@ public class ChecksummedFile } } - return valid; + return fileExists && valid; } public Path getReferenceFile( ) @@ -259,40 +260,39 @@ public class ChecksummedFile - public boolean fixChecksum(ChecksumAlgorithm algorithm) { + public UpdateStatusList fixChecksum(ChecksumAlgorithm algorithm) { return fixChecksums( Arrays.asList(algorithm) ); } + /** - * Fix or create checksum files for the reference file. + * Writes a checksum file, if it does not exist or if it exists and has a different + * checksum value. * * @param algorithms the hashes to check for. * @return true if checksums were created successfully. */ - public boolean fixChecksums( List algorithms ) + public UpdateStatusList fixChecksums( List algorithms ) { - List checksums = new ArrayList<>( algorithms.size() ); - // Create checksum object for each algorithm. - for ( ChecksumAlgorithm checksumAlgorithm : algorithms ) - { - checksums.add( new Checksum( checksumAlgorithm ) ); - } + UpdateStatusList result = UpdateStatusList.INITIALIZE(algorithms); + List checksums; - // Any checksums? - if ( checksums.isEmpty( ) ) - { - // No checksum objects, no checksum files, default to is valid. - return true; - } try { // Parse file once, for all checksums. - Checksum.update( checksums, referenceFile ); + checksums = ChecksumUtil.initializeChecksums(getReferenceFile(), algorithms); } - catch ( ChecksumValidationException e ) + catch (IOException e ) { log.warn( e.getMessage( ), e ); - return false; + result.setTotalError(e); + return result; + } + // Any checksums? + if ( checksums.isEmpty( ) ) + { + // No checksum objects, no checksum files, default to is valid. + return result; } boolean valid = true; @@ -316,23 +316,25 @@ public class ChecksummedFile if ( !checksum.compare( expectedChecksum ) ) { - // create checksum (again) + // overwrite checksum file writeChecksumFile( checksumFile, FILE_ENCODING, checksum.getChecksum( ) ); + result.setStatus(checksumAlgorithm,UpdateStatus.UPDATED); } } else { writeChecksumFile( checksumFile, FILE_ENCODING, checksum.getChecksum( ) ); + result.setStatus(checksumAlgorithm, UpdateStatus.CREATED); } } catch ( ChecksumValidationException e ) { log.warn( e.getMessage( ), e ); - valid = false; + result.setErrorStatus(checksumAlgorithm, e); } } - return valid; + return result; } @@ -362,33 +364,33 @@ public class ChecksummedFile *

* * @param checksumFile The file where the checksum is stored - * @param expectedHash The checksum algorithm to check - * @param expectedPath The filename of the reference file + * @param checksumAlgorithm The checksum algorithm to check + * @param fileName The filename of the reference file * @return * @throws IOException */ - public String parseChecksum( Path checksumFile, ChecksumAlgorithm expectedHash, String expectedPath, Charset encoding ) + public String parseChecksum( Path checksumFile, ChecksumAlgorithm checksumAlgorithm, String fileName, Charset encoding ) throws ChecksumValidationException { - ChecksumFileContent fc = parseChecksumFile( checksumFile, expectedHash, encoding ); - if ( fc.isFormatMatch() && !isValidChecksumPattern( fc.getFileReference( ), expectedPath ) ) + ChecksumFileContent fc = parseChecksumFile( checksumFile, checksumAlgorithm, encoding ); + if ( fc.isFormatMatch() && !isValidChecksumPattern( fc.getFileReference( ), fileName ) ) { throw new ChecksumValidationException(BAD_CHECKSUM_FILE_REF, - "The file reference '" + fc.getFileReference( ) + "' in the checksum file does not match expected file: '" + expectedPath + "'" ); + "The file reference '" + fc.getFileReference( ) + "' in the checksum file does not match expected file: '" + fileName + "'" ); } else if (!fc.isFormatMatch()) { throw new ChecksumValidationException( BAD_CHECKSUM_FILE, "The checksum file content could not be parsed: "+checksumFile ); } return fc.getChecksum( ); - } - public ChecksumFileContent parseChecksumFile( Path checksumFile, ChecksumAlgorithm expectedHash, Charset encoding ) + } + public ChecksumFileContent parseChecksumFile( Path checksumFile, ChecksumAlgorithm checksumAlgorithm, Charset encoding ) { ChecksumFileContent fc = new ChecksumFileContent( ); String rawChecksumString = FileUtils.readFileToString( checksumFile, encoding ); String trimmedChecksum = rawChecksumString.replace( '\n', ' ' ).trim( ); // Free-BSD / openssl - String regex = expectedHash.getType( ) + "\\s*\\(([^)]*)\\)\\s*=\\s*([a-fA-F0-9]+)"; + String regex = checksumAlgorithm.getType( ) + "\\s*\\(([^)]*)\\)\\s*=\\s*([a-fA-F0-9]+)"; Matcher m = Pattern.compile( regex ).matcher( trimmedChecksum ); if ( m.matches( ) ) { diff --git a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatus.java b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatus.java new file mode 100644 index 000000000..038732b18 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatus.java @@ -0,0 +1,89 @@ +package org.apache.archiva.checksum; + +/* + * 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. + */ + +/** + * Status of checksum update for specific algorithm. + */ +public class UpdateStatus { + + /** + * Checksum file did not exist before and was created + */ + public static final int CREATED = 1; + /** + * Checksum file existed, but content differed + */ + public static final int UPDATED = 2; + /** + * Nothing changed + */ + public static final int NONE = 0; + /** + * Error occured during update/creation of the checksum file + */ + public static final int ERROR = -1; + + private final ChecksumAlgorithm algorithm; + private final int status; + private final Throwable error; + + public UpdateStatus(ChecksumAlgorithm algorithm) { + this.algorithm = algorithm; + status = NONE; + error = null; + } + + public UpdateStatus(ChecksumAlgorithm algorithm, int status) { + this.algorithm = algorithm; + this.status = status; + error = null; + } + + public UpdateStatus(ChecksumAlgorithm algorithm, Throwable error) { + this.algorithm = algorithm; + this.status = ERROR; + this.error = error; + } + + /** + * Return the status value. + * @return The value + */ + public int getValue() { + return status; + } + + /** + * Return error, if exists, otherwise null will be returned. + * @return + */ + public Throwable getError() { + return error; + } + + /** + * Return the algorithm, this status is assigned to. + * @return The checksum algorithm + */ + public ChecksumAlgorithm getAlgorithm() { + return algorithm; + } +} diff --git a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatusList.java b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatusList.java new file mode 100644 index 000000000..cf3325a37 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/UpdateStatusList.java @@ -0,0 +1,87 @@ +package org.apache.archiva.checksum; + +/* + * 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.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Container for a list of update status objects. + * + * If there is a overall error that is not specific to a algorithm, the total status + * flag is set to error. + */ +public class UpdateStatusList { + + private int totalStatus = UpdateStatus.NONE; + private Throwable error; + private Map statusList = new TreeMap<>(); + + public UpdateStatusList() { + + } + + public void addStatus(UpdateStatus status) { + statusList.put(status.getAlgorithm(), status); + } + + public static UpdateStatusList INITIALIZE(List algorithms) { + final UpdateStatusList list = new UpdateStatusList(); + for(ChecksumAlgorithm algorithm : algorithms) { + list.addStatus(new UpdateStatus(algorithm)); + } + return list; + } + + public int getTotalStatus() { + return totalStatus; + } + + public void setTotalError(Throwable e) { + this.error = e; + this.totalStatus = UpdateStatus.ERROR; + } + + public Throwable getTotalError() { + return error; + } + + public List getStatusList() { + return new ArrayList(statusList.values()); + } + + public void setStatus(ChecksumAlgorithm algorithm, UpdateStatus status) { + statusList.put(algorithm, status); + } + + public void setStatus(ChecksumAlgorithm algorithm, int status) { + statusList.put(algorithm, new UpdateStatus(algorithm, status)); + } + + public void setErrorStatus(ChecksumAlgorithm algorithm, Throwable e) { + statusList.put(algorithm, new UpdateStatus(algorithm,e)); + } + + public UpdateStatus getStatus(ChecksumAlgorithm algorithm) { + return statusList.get(algorithm); + } +} diff --git a/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksumTest.java b/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksumTest.java index 55746f6b9..ca3dab891 100644 --- a/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksumTest.java +++ b/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksumTest.java @@ -19,15 +19,12 @@ package org.apache.archiva.checksum; * under the License. */ -import junit.framework.TestCase; import org.apache.archiva.common.utils.FileUtils; import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.file.Path; import java.util.ArrayList; @@ -85,7 +82,7 @@ public class ChecksumTest FileUtils.writeStringToFile( checkFile, FILE_ENCODING, "You know, I'm sick of following my dreams, man. " + "I'm just going to ask where they're going and hook up with 'em later. - Mitch Hedberg"); - Checksum.update( checksums, checkFile ); + ChecksumUtil.update( checksums, checkFile ); assertEquals( "Checksum SHA1", "e396119ae0542e85a74759602fd2f81e5d36d762", checksumSha1.getChecksum() ); assertEquals( "Checksum MD5", "21c2c5ca87ec018adacb2e2fb3432219", checksumMd5.getChecksum() ); diff --git a/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksummedFileTest.java b/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksummedFileTest.java index 727953ec9..33c7416ee 100644 --- a/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksummedFileTest.java +++ b/archiva-modules/archiva-base/archiva-checksum/src/test/java/org/apache/archiva/checksum/ChecksummedFileTest.java @@ -130,7 +130,7 @@ public class ChecksummedFileTest { Path testableJar = createTestableJar( "examples/redback-authz-open.jar" ); ChecksummedFile checksummedFile = new ChecksummedFile( testableJar ); - checksummedFile.createChecksum( ChecksumAlgorithm.SHA1 ); + checksummedFile.writeFile( ChecksumAlgorithm.SHA1 ); Path hashFile = checksummedFile.getChecksumFile( ChecksumAlgorithm.SHA1 ); assertTrue( "ChecksumAlgorithm file should exist.", Files.exists(hashFile) ); String hashContents = org.apache.commons.io.FileUtils.readFileToString( hashFile.toFile(), "UTF-8" ); @@ -152,8 +152,10 @@ public class ChecksummedFileTest assertFalse( "ChecksummedFile.isValid(SHA1) == false", checksummedFile.isValidChecksum( ChecksumAlgorithm.SHA1 ) ); - boolean fixed = checksummedFile.fixChecksums( Arrays.asList( ChecksumAlgorithm.SHA1 ) ); - assertTrue( "ChecksummedFile.fixChecksums() == true", fixed ); + UpdateStatusList fixed = checksummedFile.fixChecksums( Arrays.asList( ChecksumAlgorithm.SHA1 ) ); + assertEquals(1, fixed.getStatusList().size()); + assertFalse(fixed.getTotalStatus()==UpdateStatus.ERROR); + assertTrue( "ChecksummedFile.fixChecksums() == true", fixed.getStatusList().get(0).getValue()==UpdateStatus.UPDATED ); assertTrue( "ChecksummedFile.isValid(SHA1) == true", checksummedFile.isValidChecksum( ChecksumAlgorithm.SHA1 ) ); diff --git a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/ArchivaRuntimeConfiguration.java b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/ArchivaRuntimeConfiguration.java index 7d6f5d38a..ae9b9bb90 100644 --- a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/ArchivaRuntimeConfiguration.java +++ b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/ArchivaRuntimeConfiguration.java @@ -19,6 +19,10 @@ package org.apache.archiva.configuration; * under the License. */ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * * The runtime configuration. @@ -76,6 +80,11 @@ public class ArchivaRuntimeConfiguration */ private String languageRange = "en,fr,de"; + /** + * List of checksum types (algorithms) that should be applied to repository artifacts. + */ + private List checksumTypes = new ArrayList(Arrays.asList("MD5","SHA1","SHA256")); + //-----------/ //- Methods -/ @@ -233,4 +242,49 @@ public class ArchivaRuntimeConfiguration this.urlFailureCacheConfiguration = urlFailureCacheConfiguration; } //-- void setUrlFailureCacheConfiguration( CacheConfiguration ) + + /** + * Returns the list of checksum types to generate + * @return + */ + public List getChecksumTypes() + { + if ( this.checksumTypes == null ) + { + this.checksumTypes = new java.util.ArrayList(); + } + + return this.checksumTypes; + } + + /** + * Adds a checksum type + * @param type + */ + public void addChecksumType(String type) { + + if (!getChecksumTypes().contains(type)) { + getChecksumTypes().add(type); + } + } + + /** + * Removes a checksum type + * @param type + */ + public void removeChecksumType(String type) { + getChecksumTypes().remove(type); + } + + /** + * Set all checksum types + * @param checksumTypes + */ + public void setChecksumTypes(List checksumTypes) { + if (checksumTypes!=null) { + getChecksumTypes().clear(); + getChecksumTypes().addAll(checksumTypes); + } + } + } diff --git a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java index 0abe06aa9..d8783453e 100644 --- a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java +++ b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryReader.java @@ -1423,6 +1423,9 @@ public class ConfigurationRegistryReader { value.setLanguageRange(languageRange); + List checksumTypeList = registry.getList(prefix + "checksumTypes.type"); + value.setChecksumTypes(checksumTypeList); + return value; } diff --git a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java index bb94913c0..31ffc6b9d 100644 --- a/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java +++ b/archiva-modules/archiva-base/archiva-configuration/src/main/java/org/apache/archiva/configuration/io/registry/ConfigurationRegistryWriter.java @@ -24,6 +24,7 @@ import org.apache.archiva.configuration.*; import org.apache.archiva.redback.components.registry.Registry; import java.util.Iterator; +import java.util.List; // Util imports // Model class imports @@ -37,6 +38,20 @@ public class ConfigurationRegistryWriter { writeConfiguration("", model, registry); } + private void writeList(Registry registry, List subList, String subsetPath, String elementName) { + if (subList != null && subList.size() > 0 + ) { + registry.removeSubset(subsetPath); + + int count = 0; + for (Iterator iter = subList.iterator(); iter.hasNext(); count++) { + String name = subsetPath + "." + elementName + "(" + count + ")"; + String value = iter.next(); + registry.setString(name, value); + } + } + } + private void writeConfiguration(String prefix, Configuration value, Registry registry) { if (value != null) { if (value.getVersion() != null @@ -882,6 +897,7 @@ public class ConfigurationRegistryWriter { String languageRange = "languageRange"; registry.setString(prefix + languageRange, value.getLanguageRange()); } + writeList(registry, value.getChecksumTypes(), prefix+"checksumTypes", "type"); } } diff --git a/archiva-modules/archiva-base/archiva-configuration/src/main/resources/org/apache/archiva/configuration/default-archiva.xml b/archiva-modules/archiva-base/archiva-configuration/src/main/resources/org/apache/archiva/configuration/default-archiva.xml index c5acb803d..5f3731dab 100644 --- a/archiva-modules/archiva-base/archiva-configuration/src/main/resources/org/apache/archiva/configuration/default-archiva.xml +++ b/archiva-modules/archiva-base/archiva-configuration/src/main/resources/org/apache/archiva/configuration/default-archiva.xml @@ -142,6 +142,14 @@ + + + MD5 + SHA1 + SHA256 + + + jpa diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ArtifactMissingChecksumsConsumer.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ArtifactMissingChecksumsConsumer.java index f90fb10c6..559de35e0 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ArtifactMissingChecksumsConsumer.java +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ArtifactMissingChecksumsConsumer.java @@ -19,8 +19,7 @@ package org.apache.archiva.consumers.core; * under the License. */ -import org.apache.archiva.checksum.ChecksumAlgorithm; -import org.apache.archiva.checksum.ChecksummedFile; +import org.apache.archiva.checksum.*; import org.apache.archiva.configuration.ArchivaConfiguration; import org.apache.archiva.configuration.FileTypes; import org.apache.archiva.consumers.AbstractMonitoredConsumer; @@ -58,14 +57,12 @@ public class ArtifactMissingChecksumsConsumer private String id = "create-missing-checksums"; - private String description = "Create Missing and/or Fix Invalid Checksums (.sha1, .md5)"; + private String description = "Create Missing and/or Fix Invalid Checksum files."; private ArchivaConfiguration configuration; private FileTypes filetypes; - private ChecksummedFile checksum; - private static final String TYPE_CHECKSUM_NOT_FILE = "checksum-bad-not-file"; private static final String TYPE_CHECKSUM_CANNOT_CALC = "checksum-calc-failure"; @@ -75,6 +72,7 @@ public class ArtifactMissingChecksumsConsumer private Path repositoryDir; private List includes = new ArrayList<>( 0 ); + private List algorithms; @Inject public ArtifactMissingChecksumsConsumer( ArchivaConfiguration configuration, FileTypes filetypes ) @@ -141,8 +139,36 @@ public class ArtifactMissingChecksumsConsumer public void processFile( String path ) throws ConsumerException { - createFixChecksum( path, ChecksumAlgorithm.SHA1 ); - createFixChecksum( path, ChecksumAlgorithm.MD5 ); + Path artifactPath = repositoryDir.resolve(path); + ChecksummedFile csFile = new ChecksummedFile(artifactPath); + UpdateStatusList result = csFile.fixChecksums(algorithms); + if (result.getTotalStatus()== UpdateStatus.ERROR) { + log.warn( "Error accessing file {}. ", path ); + triggerConsumerWarning( TYPE_CHECKSUM_NOT_FILE, + "Error accessing file " + path + "." ); + } else { + result.getStatusList().stream().forEach(st -> + triggerInfo(path, st)); + } + } + + private void triggerInfo(String path, UpdateStatus status) { + switch (status.getValue()) { + case UpdateStatus.ERROR: + log.error( "Cannot create checksum for file {} :", path, status.getError() ); + triggerConsumerError( TYPE_CHECKSUM_CANNOT_CREATE, "Cannot create checksum for file " + path + + ": " + status.getError().getMessage( ) ); + break; + case UpdateStatus.CREATED: + log.info( "Created missing checksum file {}", path ); + triggerConsumerInfo( "Created missing checksum file " + path ); + break; + case UpdateStatus.UPDATED: + log.info( "Fixed checksum file {}", path ); + triggerConsumerInfo( "Fixed checksum file " + path ); + break; + + } } @Override @@ -152,44 +178,6 @@ public class ArtifactMissingChecksumsConsumer processFile( path ); } - private void createFixChecksum( String path, ChecksumAlgorithm checksumAlgorithm ) - { - Path artifactFile = repositoryDir.resolve(path); - Path checksumFile = repositoryDir.resolve(path + "." + checksumAlgorithm.getExt( ).get(0) ); - - if ( Files.exists(checksumFile) ) - { - checksum = new ChecksummedFile( artifactFile); - if ( !checksum.isValidChecksum( checksumAlgorithm ) ) - { - checksum.fixChecksum( checksumAlgorithm ); - log.info( "Fixed checksum file {}", checksumFile.toAbsolutePath( ) ); - triggerConsumerInfo( "Fixed checksum file " + checksumFile.toAbsolutePath( ) ); - } - } - else if ( !Files.exists(checksumFile) ) - { - checksum = new ChecksummedFile( artifactFile); - try - { - checksum.createChecksum( checksumAlgorithm ); - log.info( "Created missing checksum file {}", checksumFile.toAbsolutePath( ) ); - triggerConsumerInfo( "Created missing checksum file " + checksumFile.toAbsolutePath( ) ); - } - catch ( IOException e ) - { - log.error( "Cannot create checksum for file {} :", checksumFile, e ); - triggerConsumerError( TYPE_CHECKSUM_CANNOT_CREATE, "Cannot create checksum for file " + checksumFile + - ": " + e.getMessage( ) ); - } - } - else - { - log.warn( "Checksum file {} is not a file. ", checksumFile.toAbsolutePath( ) ); - triggerConsumerWarning( TYPE_CHECKSUM_NOT_FILE, - "Checksum file " + checksumFile.toAbsolutePath( ) + " is not a file." ); - } - } /* @Override @@ -222,5 +210,6 @@ public class ArtifactMissingChecksumsConsumer //configuration.addChangeListener( this ); initIncludes( ); + algorithms = ChecksumUtil.getAlgorithms(configuration.getConfiguration().getArchivaRuntimeConfiguration().getChecksumTypes()); } } diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ValidateChecksumConsumer.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ValidateChecksumConsumer.java index e4648a8a7..2121fd355 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ValidateChecksumConsumer.java +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/ValidateChecksumConsumer.java @@ -159,7 +159,7 @@ public class ValidateChecksumConsumer @PostConstruct public void initialize( ) { - Set extensions = ChecksumAlgorithm.getExtensions(); + Set extensions = ChecksumAlgorithm.getAllExtensions(); includes = new ArrayList<>( extensions.size() ); for ( String ext : extensions ) { diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java index 4d50b8ce0..f06a67649 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/test/java/org/apache/archiva/consumers/core/AbstractArtifactConsumerTest.java @@ -63,6 +63,9 @@ public abstract class AbstractArtifactConsumerTest (FileType) archivaConfiguration.getConfiguration().getRepositoryScanning().getFileTypes().get( 0 ); assertEquals( FileTypes.ARTIFACTS, fileType.getId() ); fileType.addPattern( "**/*.xml" ); + archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().addChecksumType("MD5"); + archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().addChecksumType("SHA1"); + archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().addChecksumType("SHA256"); repoLocation = Paths.get( "target/test-" + getName() + "/test-repo" ); } diff --git a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java index c2004d139..82d4d3da4 100644 --- a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java +++ b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java @@ -21,6 +21,7 @@ package org.apache.archiva.policies; import org.apache.archiva.checksum.ChecksumAlgorithm; import org.apache.archiva.checksum.ChecksummedFile; +import org.apache.archiva.checksum.UpdateStatus; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -146,7 +147,7 @@ public class ChecksumPolicy if ( FIX.equals( policySetting ) ) { ChecksummedFile checksum = new ChecksummedFile( localFile ); - if ( checksum.fixChecksums( algorithms ) ) + if ( checksum.fixChecksums( algorithms ).getTotalStatus() != UpdateStatus.ERROR ) { log.debug( "Checksum policy set to FIX, checksum files have been updated." ); return; diff --git a/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/api/DefaultFileUploadService.java b/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/api/DefaultFileUploadService.java index 934da8097..afe40d285 100644 --- a/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/api/DefaultFileUploadService.java +++ b/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/api/DefaultFileUploadService.java @@ -25,9 +25,11 @@ import org.apache.archiva.admin.model.admin.ArchivaAdministration; import org.apache.archiva.admin.model.beans.ManagedRepository; import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; import org.apache.archiva.checksum.ChecksumAlgorithm; +import org.apache.archiva.checksum.ChecksumUtil; import org.apache.archiva.checksum.ChecksummedFile; import org.apache.archiva.common.utils.VersionComparator; import org.apache.archiva.common.utils.VersionUtil; +import org.apache.archiva.configuration.ArchivaConfiguration; import org.apache.archiva.maven2.metadata.MavenMetadataReader; import org.apache.archiva.metadata.model.facets.AuditEvent; import org.apache.archiva.model.ArchivaRepositoryMetadata; @@ -59,6 +61,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; import javax.servlet.http.HttpServletRequest; @@ -74,9 +77,14 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; /** + * + * Service for uploading files to the repository. + * * @author Olivier Lamy + * @author Martin Stockhammer */ @Service("fileUploadService#rest") public class DefaultFileUploadService @@ -96,7 +104,10 @@ public class DefaultFileUploadService @Inject private ArchivaAdministration archivaAdministration; - private List algorithms = Arrays.asList(ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5); + @Inject + ArchivaConfiguration configuration; + + private List algorithms; private final String FS = FileSystems.getDefault().getSeparator(); @@ -111,6 +122,11 @@ public class DefaultFileUploadService StringUtils.trim(URLDecoder.decode(IOUtils.toString(attachment.getDataHandler().getInputStream(), "UTF-8"), "UTF-8")); } + @PostConstruct + private void initialize() { + algorithms = ChecksumUtil.getAlgorithms(configuration.getConfiguration().getArchivaRuntimeConfiguration().getChecksumTypes()); + } + @Override public FileMetadata post(MultipartBody multipartBody) throws ArchivaRestServiceException { @@ -233,12 +249,10 @@ public class DefaultFileUploadService @Override public List getSessionFileMetadatas() throws ArchivaRestServiceException { - @SuppressWarnings("unchecked") List fileMetadatas = - (List) httpServletRequest.getSession().getAttribute(FILES_SESSION_KEY); - - return fileMetadatas == null ? Collections.emptyList() : fileMetadatas; + return getSessionFilesList(); } + private boolean hasValidChars(String checkString) { if (checkString.contains(FS)) { return false; @@ -427,8 +441,9 @@ public class DefaultFileUploadService filename = filename.replaceAll(VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber); } - boolean fixChecksums = - !(archivaAdministration.getKnownContentConsumers().contains("create-missing-checksums")); + // We always fix checksums for newly uploaded files, even if the content consumer is active. + boolean fixChecksums = true; + // !(archivaAdministration.getKnownContentConsumers().contains("create-missing-checksums")); try { Path targetFile = targetPath.resolve(filename); diff --git a/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/upload/UploadArtifactsTest.java b/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/upload/UploadArtifactsTest.java index 78debb1e0..e501d1cdf 100644 --- a/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/upload/UploadArtifactsTest.java +++ b/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/upload/UploadArtifactsTest.java @@ -21,6 +21,7 @@ package org.apache.archiva.upload; import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; import org.apache.archiva.configuration.ArchivaConfiguration; import org.apache.archiva.redback.rest.services.AbstractRestServicesTest; +import org.apache.archiva.redback.rest.services.FakeCreateAdminService; import org.apache.archiva.rest.api.services.ArchivaRestServiceException; import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner; import org.apache.archiva.web.api.FileUploadService; @@ -248,4 +249,26 @@ public class UploadArtifactsTest service.post(body); service.save("internal", "org.apache.archiva", "archiva-model", "1.2", "jar", true); } + + @Test + public void saveFileWithOtherExtension() throws IOException, ArchivaRestServiceException { + log.debug("Starting saveFileWithOtherExtension()"); + + Path path = Paths.get("target/appserver-base/repositories/internal/data/repositories/internal/org/apache/archiva/archiva-model/1.2/archiva-model-1.2.bin"); + log.debug("Jar exists: {}",Files.exists(path)); + Files.deleteIfExists(path); + Path pomPath = Paths.get("target/appserver-base/repositories/internal/data/repositories/internal/org/apache/archiva/archiva-model/1.2/archiva-model-1.2.pom"); + Files.deleteIfExists(pomPath); + FileUploadService service = getUploadService(); + service.clearUploadedFiles(); + Path file = Paths.get("src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar"); + log.debug("Upload file exists: {}", Files.exists(file)); + final Attachment fileAttachment = new AttachmentBuilder().object(Files.newInputStream(file)).contentDisposition(new ContentDisposition("form-data; filename=\"archiva-model.bin\"; name=\"files[]\"")).build(); + MultipartBody body = new MultipartBody(fileAttachment); + service.post(body); + assertTrue(service.save("internal", "org.apache.archiva", "archiva-model", "1.2", "bin", false)); + assertTrue(Files.exists(path)); + } + + } -- 2.39.5