Skip to content

Commit

Permalink
Configure Gradle
Browse files Browse the repository at this point in the history
- Defines initial versions of JSON model/configuration entities
- Creates encryption utility for key generation and key encryption tasks
- Implements local file metadat parser
- Adds Lombok configuration
- Adds tests
- Adds basic Abort-Mission configuration
- Fixes some Gradle plugin configuration issues

{patch}

Signed-off-by: Esta Nagy <[email protected]>
  • Loading branch information
nagyesta committed Sep 26, 2023
1 parent a9a2892 commit 25f32ff
Show file tree
Hide file tree
Showing 34 changed files with 1,685 additions and 42 deletions.
14 changes: 8 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
checkstyle
alias(libs.plugins.versioner)
alias(libs.plugins.lombok) apply false
alias(libs.plugins.abort.mission) apply false
alias(libs.plugins.index.scan)
alias(libs.plugins.owasp.dependencycheck)
}
Expand Down Expand Up @@ -79,6 +80,7 @@ subprojects {
apply(plugin = "io.freefair.lombok")
apply(plugin = "org.sonatype.gradle.plugins.scan")
apply(plugin = "org.owasp.dependencycheck")
apply(plugin = "com.github.nagyesta.abort-mission-gradle-plugin")

group = rootProject.group
version = rootProject.version
Expand All @@ -98,18 +100,18 @@ subprojects {
tasks.jacocoTestReport {
reports {
xml.required.set(true)
xml.outputLocation.set(layout.buildDirectory.file("/reports/jacoco/report.xml"))
xml.outputLocation.set(layout.buildDirectory.file("reports/jacoco/report.xml"))
csv.required.set(false)
html.required.set(true)
html.outputLocation.set(layout.buildDirectory.dir("/reports/jacoco/html"))
html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco/html"))
}
dependsOn(tasks.test)
finalizedBy(tasks.getByName("jacocoTestCoverageVerification"))
}

tasks.withType<JacocoCoverageVerification>().configureEach {
inputs.file(layout.buildDirectory.file("/reports/jacoco/report.xml"))
outputs.file(layout.buildDirectory.file("/reports/jacoco/jacocoTestCoverageVerification"))
inputs.file(layout.buildDirectory.file("reports/jacoco/report.xml"))
outputs.file(layout.buildDirectory.file("reports/jacoco/jacocoTestCoverageVerification"))

violationRules {
rule {
Expand Down Expand Up @@ -141,7 +143,7 @@ subprojects {
}
}
doLast {
layout.buildDirectory.file("/reports/jacoco/jacocoTestCoverageVerification").get().asFile.writeText("Passed")
layout.buildDirectory.file("reports/jacoco/jacocoTestCoverageVerification").get().asFile.writeText("Passed")
}
}

Expand All @@ -164,7 +166,7 @@ subprojects {
tasks.withType<Checkstyle>().configureEach {
configProperties = mutableMapOf<String, Any>(
"base_dir" to rootDir.absolutePath.toString(),
"cache_file" to layout.buildDirectory.file("/checkstyle/cacheFile").get().asFile.absolutePath.toString()
"cache_file" to layout.buildDirectory.file("checkstyle/cacheFile").get().asFile.absolutePath.toString()
)
checkstyle.toolVersion = rootProject.libs.versions.checkstyle.get()
checkstyle.configFile = rootProject.file("config/checkstyle/checkstyle.xml")
Expand Down
19 changes: 13 additions & 6 deletions file-barj-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ plugins {
id("java")
}

repositories {
mavenCentral()
}

dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
implementation(libs.bundles.jackson)
implementation(libs.commons.codec)
implementation(libs.commons.compress)
implementation(libs.commons.crypto)
implementation(libs.commons.io)
testImplementation(platform(libs.junit.bom))
testImplementation(libs.jupiter)
testImplementation(libs.abort.mission.jupiter)
testImplementation(libs.mockito.core)
}

tasks.test {
useJUnitPlatform()
}

abortMission {
toolVersion = libs.versions.abortMission.get()
}
4 changes: 4 additions & 0 deletions file-barj-core/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
lombok.nonNull.exceptionType = IllegalArgumentException
8 changes: 0 additions & 8 deletions file-barj-core/src/main/java/com/github/nagyesta/Main.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.nagyesta.filebarj.core.backup;

import com.github.nagyesta.filebarj.core.config.BackupJobConfiguration;
import com.github.nagyesta.filebarj.core.model.FileMetadata;

import java.io.File;

/**
* Parses metadata of Files.
*/
public interface FileMetadataParser {

/**
* Reads or calculates metadata of a file we need to include in the backup.
* @param file The current {@link File} we need ot evaluate
* @param configuration The backup configuration
* @return the parsed {@link FileMetadata}
*/
FileMetadata parse(File file, BackupJobConfiguration configuration);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.github.nagyesta.filebarj.core.backup;

import com.github.nagyesta.filebarj.core.config.BackupJobConfiguration;
import com.github.nagyesta.filebarj.core.model.FileMetadata;
import com.github.nagyesta.filebarj.core.model.enums.Change;
import com.github.nagyesta.filebarj.core.model.enums.FileType;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Optional;

/**
* Local file specific implementation of the {@link FileMetadataParser}.
*/
public class FileMetadataParserLocal implements FileMetadataParser {

@Override
public FileMetadata parse(final File file, final BackupJobConfiguration configuration) {
final var posixFileAttributes = posixPermissionsQuietly(file);
final var basicAttributes = basicAttributesQuietly(file);

return FileMetadata.builder()
.absolutePath(file.toPath().toAbsolutePath())
.owner(posixFileAttributes.owner().getName())
.group(posixFileAttributes.group().getName())
.posixPermissions(PosixFilePermissions.toString(posixFileAttributes.permissions()))
.lastModifiedUtcEpochSeconds(basicAttributes.lastModifiedTime().toInstant().getEpochSecond())
.originalSizeBytes(basicAttributes.size())
.fileType(FileType.findForAttributes(basicAttributes))
.originalChecksum(calculateChecksum(file, configuration))
.hidden(checkIsHiddenQuietly(file))
.status(Change.NEW)
.build();
}

private PosixFileAttributes posixPermissionsQuietly(final File file) {
try {
return Files.readAttributes(file.toPath(), PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}

private BasicFileAttributes basicAttributesQuietly(final File file) {
try {
return Files.readAttributes(file.toPath(), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}

private boolean checkIsHiddenQuietly(final File file) {
try {
return Files.isHidden(file.toPath());
} catch (final IOException e) {
throw new RuntimeException(e);
}
}


private String calculateChecksum(final File file, final BackupJobConfiguration configuration) {
try {
final var messageDigest = Optional.ofNullable(configuration.getChecksumAlgorithm().getAlgorithmName())
.map(DigestUtils::new);
final var attributes = basicAttributesQuietly(file);
if (messageDigest.isEmpty() || attributes.isOther()) {
return null;
} else {
if (attributes.isSymbolicLink()) {
return messageDigest.get().digestAsHex(Files.readSymbolicLink(file.toPath()).toAbsolutePath());
} else {
return messageDigest.get().digestAsHex(file);
}
}
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.github.nagyesta.filebarj.core.config;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.github.nagyesta.filebarj.core.config.enums.DuplicateHandlingStrategy;
import com.github.nagyesta.filebarj.core.config.enums.HashAlgorithm;
import com.github.nagyesta.filebarj.core.json.PublicKeyDeserializer;
import com.github.nagyesta.filebarj.core.json.PublicKeySerializer;
import com.github.nagyesta.filebarj.core.model.enums.BackupType;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.extern.jackson.Jacksonized;

import java.nio.file.Path;
import java.security.PublicKey;
import java.util.Set;

/**
* Configuration class defining the parameters of the backup/restore job.
*/
@Data
@EqualsAndHashCode
@Builder
@Jacksonized
public class BackupJobConfiguration {
/**
* The desired backup type which should be used when the job is executed.
* <br/><br/>
* NOTE: The backup will be automatically a {@link BackupType#FULL} backup
* every time when there is no previous increment or there is a change in
* the backup configuration since the last increment was saved. As a side
* effect, this property is ignored during the first execution after each
* configuration change.
*/
@NonNull
@JsonProperty("backup_type")
private final BackupType backupType;
/**
* The algorithm used for checksum calculations before and after archival.
* Useful for data integrity verifications.
* <br/><br/>
* NOTE: A change of this value requires a {@link BackupType#FULL} backup
* as the previous increments cannot use a different hash algorithm.
*/
@NonNull
@JsonProperty("checksum_algorithm")
private final HashAlgorithm checksumAlgorithm;
/**
* The public key of an RSA key pair used for encryption.
* The files will be encrypted using automatically generated AES keys (DEK)
* which will be encrypted using the RSA public key (KEK).
* <br/><br/>
* NOTE: A change of this value requires a {@link BackupType#FULL} backup
* as the previous increments cannot use a different encryption key.
*/
@JsonSerialize(using = PublicKeySerializer.class)
@JsonDeserialize(using = PublicKeyDeserializer.class)
@JsonProperty("encryption_key")
private final PublicKey encryptionKey;
/**
* The strategy used for handling duplicate files.
* <br/><br/>
* NOTE: A change of this value requires a {@link BackupType#FULL} backup
* as the previous increments cannot use a different duplicate handling
* strategy.
*/
@NonNull
@JsonProperty("duplicate_strategy")
private final DuplicateHandlingStrategy duplicateStrategy;
/**
* The desired maximum chunk size for the backup archive part.
* <br/><br/>
* NOTE: Using 0 means that the archive won't be chunked.
*/
@EqualsAndHashCode.Exclude
@JsonProperty("chunk_size_mebibyte")
private final int chunkSizeMebibyte;
/**
* The prefix of the backup file names.
* <br/><br/>
* NOTE: A change of this value requires a {@link BackupType#FULL} backup
* as the previous increments cannot use a different file name prefix.
*/
@NonNull
@JsonProperty("file_name_prefix")
private final String fileNamePrefix;
/**
* The destination where the backup files will be saved.
* <br/><br/>
* NOTE: A change of this value requires a {@link BackupType#FULL} backup
* as the metadata of the previous increments must be found in the destination
* in order to calculate changes.
*/
@NonNull
@JsonProperty("destination_directory")
private final Path destinationDirectory;
/**
* The source files we want to archive.
*/
@NonNull
@EqualsAndHashCode.Exclude
@JsonProperty("sources")
private final Set<BackupSource> sources;
}
Loading

0 comments on commit 25f32ff

Please sign in to comment.