diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/FileSystemTufStore.java b/sigstore-java/src/main/java/dev/sigstore/tuf/FileSystemTufStore.java index d2f6857d..6b7ba676 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/FileSystemTufStore.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/FileSystemTufStore.java @@ -18,14 +18,15 @@ import static dev.sigstore.json.GsonSupplier.GSON; import com.google.common.annotations.VisibleForTesting; +import dev.sigstore.tuf.model.Role; import dev.sigstore.tuf.model.Root; +import dev.sigstore.tuf.model.SignedTufMeta; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; -import javax.annotation.Nullable; /** Uses a local file system directory to store the trusted TUF metadata. */ public class FileSystemTufStore implements TufLocalStore { @@ -34,44 +35,50 @@ public class FileSystemTufStore implements TufLocalStore { private static final String SNAPSHOT_FILE_NAME = "snapshot.json"; private static final String TIMESTAMP_FILE_NAME = "timestamp.json"; private Path repoBaseDir; - private Root trustedRoot; @VisibleForTesting - FileSystemTufStore(Path repoBaseDir, @Nullable Root trustedRoot) { + FileSystemTufStore(Path repoBaseDir) { this.repoBaseDir = repoBaseDir; - this.trustedRoot = trustedRoot; } - static TufLocalStore newFileSystemStore(Path repoBaseDir) throws IOException { - Path rootFile = repoBaseDir.resolve(ROOT_FILE_NAME); - Root trustedRoot = null; - if (rootFile.toFile().exists()) { - trustedRoot = GSON.get().fromJson(Files.readString(rootFile), Root.class); + static TufLocalStore newFileSystemStore(Path repoBaseDir) { + if (!repoBaseDir.toFile().isDirectory()) { + throw new IllegalArgumentException(repoBaseDir + " must be a file system directory."); } - return new FileSystemTufStore(repoBaseDir, trustedRoot); + return new FileSystemTufStore(repoBaseDir); } @Override - public Optional getTrustedRoot() { - return Optional.ofNullable(trustedRoot); + public Optional loadTrustedRoot() throws IOException { + return loadRole(Role.Name.ROOT, Root.class); } - @Override - public void setTrustedRoot(Root root) throws IOException { - if (root == null) { - throw new NullPointerException("Root should not be null"); + Optional loadRole(Role.Name roleName, Class tClass) + throws IOException { + Path roleFile = repoBaseDir.resolve(roleName + ".json"); + if (!roleFile.toFile().exists()) { + return Optional.empty(); } - Path rootPath = repoBaseDir.resolve(ROOT_FILE_NAME); - if (trustedRoot != null) { - // back it up - Files.move( - rootPath, - repoBaseDir.resolve(trustedRoot.getSignedMeta().getVersion() + "." + ROOT_FILE_NAME)); + return Optional.of(GSON.get().fromJson(Files.readString(roleFile), tClass)); + } + + void saveRole(T role) throws IOException { + try (FileWriter fileWriter = + new FileWriter(repoBaseDir.resolve(role.getSignedMeta().getType() + ".json").toFile())) { + fileWriter.write(GSON.get().toJson(role)); } - trustedRoot = root; - try (FileWriter fileWriter = new FileWriter(rootPath.toFile())) { - fileWriter.write(GSON.get().toJson(trustedRoot)); + } + + @Override + public void storeTrustedRoot(Root root) throws IOException { + Optional trustedRoot = loadTrustedRoot(); + if (trustedRoot.isPresent()) { + Files.move( + repoBaseDir.resolve(ROOT_FILE_NAME), + repoBaseDir.resolve( + trustedRoot.get().getSignedMeta().getVersion() + "." + ROOT_FILE_NAME)); } + saveRole(root); } @Override diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/TufClient.java b/sigstore-java/src/main/java/dev/sigstore/tuf/TufClient.java index 633618c0..88829e64 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/TufClient.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/TufClient.java @@ -154,7 +154,7 @@ public void updateRoot(Path trustedRootPath, URL mirror, TufLocalStore localStor // 5.3.7) set the trusted root metadata to the new root trustedRoot = newRoot; // 5.3.8) persist to repo - localStore.setTrustedRoot(trustedRoot); + localStore.storeTrustedRoot(trustedRoot); // 5.3.9) see if there are more versions go back 5.3.3 nextVersion++; } diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/TufLocalStore.java b/sigstore-java/src/main/java/dev/sigstore/tuf/TufLocalStore.java index 5c68f4d6..01b8eab7 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/TufLocalStore.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/TufLocalStore.java @@ -26,7 +26,7 @@ public interface TufLocalStore { * If the local store has a root that has been blessed safe either by the client or through update * and verification, then this method returns it. */ - Optional getTrustedRoot(); + Optional loadTrustedRoot() throws IOException; /** * Once you have ascertained that your root is trustworthy use this method to persist it to your @@ -39,7 +39,7 @@ public interface TufLocalStore { * @throws IOException since some implementations may persist the root to disk or over the network * we throw {@code IOException} in case of IO error. */ - void setTrustedRoot(Root root) throws IOException; + void storeTrustedRoot(Root root) throws IOException; /** * This clears out the snapshot and timestamp metadata from the store, as required when snapshot diff --git a/sigstore-java/src/test/java/dev/sigstore/tuf/FileSystemTufStoreTest.java b/sigstore-java/src/test/java/dev/sigstore/tuf/FileSystemTufStoreTest.java index e44f74d3..44c21ce8 100644 --- a/sigstore-java/src/test/java/dev/sigstore/tuf/FileSystemTufStoreTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/tuf/FileSystemTufStoreTest.java @@ -28,7 +28,7 @@ class FileSystemTufStoreTest { @Test void newFileSystemStore_empty(@TempDir Path repoBase) throws IOException { TufLocalStore tufLocalStore = FileSystemTufStore.newFileSystemStore(repoBase); - assertFalse(tufLocalStore.getTrustedRoot().isPresent()); + assertFalse(tufLocalStore.loadTrustedRoot().isPresent()); } @Test @@ -36,14 +36,14 @@ void newFileSystemStore_hasRepo(@TempDir Path repoBase) throws IOException { String repoName = "remote-repo-prod"; TestResources.setupRepoFiles(repoName, repoBase, "root.json"); TufLocalStore tufLocalStore = FileSystemTufStore.newFileSystemStore(repoBase); - assertTrue(tufLocalStore.getTrustedRoot().isPresent()); + assertTrue(tufLocalStore.loadTrustedRoot().isPresent()); } @Test void setTrustedRoot_noPrevious(@TempDir Path repoBase) throws IOException { TufLocalStore tufLocalStore = FileSystemTufStore.newFileSystemStore(repoBase); assertFalse(repoBase.resolve("root.json").toFile().exists()); - tufLocalStore.setTrustedRoot(TestResources.loadRoot(TestResources.CLIENT_TRUSTED_ROOT)); + tufLocalStore.storeTrustedRoot(TestResources.loadRoot(TestResources.CLIENT_TRUSTED_ROOT)); assertEquals(1, repoBase.toFile().list().length); assertTrue(repoBase.resolve("root.json").toFile().exists()); } @@ -53,9 +53,9 @@ void setTrustedRoot_backupPerformed(@TempDir Path repoBase) throws IOException { String repoName = "remote-repo-prod"; TestResources.setupRepoFiles(repoName, repoBase, "root.json"); TufLocalStore tufLocalStore = FileSystemTufStore.newFileSystemStore(repoBase); - int version = tufLocalStore.getTrustedRoot().get().getSignedMeta().getVersion(); + int version = tufLocalStore.loadTrustedRoot().get().getSignedMeta().getVersion(); assertFalse(repoBase.resolve(version + ".root.json").toFile().exists()); - tufLocalStore.setTrustedRoot(TestResources.loadRoot(TestResources.CLIENT_TRUSTED_ROOT)); + tufLocalStore.storeTrustedRoot(TestResources.loadRoot(TestResources.CLIENT_TRUSTED_ROOT)); assertTrue(repoBase.resolve(version + ".root.json").toFile().exists()); }