Skip to content

Commit

Permalink
rework
Browse files Browse the repository at this point in the history
Signed-off-by: Appu Goundan <[email protected]>
  • Loading branch information
loosebazooka committed Oct 29, 2024
1 parent 70ebdbe commit be5deff
Show file tree
Hide file tree
Showing 16 changed files with 545 additions and 481 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@
import java.util.Optional;

/** Uses a local file system directory to store the trusted TUF metadata. */
public class FileSystemTufStore implements MutableTufStore {
public class FileSystemTufStore implements MetaStore, TargetStore {

private static final String ROOT_FILE_NAME = "root.json";
private static final String SNAPSHOT_FILE_NAME = "snapshot.json";
private static final String TIMESTAMP_FILE_NAME = "timestamp.json";
private Path repoBaseDir;
private Path targetsCache;
private final Path repoBaseDir;
private final Path targetsCache;

@VisibleForTesting
FileSystemTufStore(Path repoBaseDir, Path targetsCache) {
this.repoBaseDir = repoBaseDir;
this.targetsCache = targetsCache;
}

public static MutableTufStore newFileSystemStore(Path repoBaseDir) throws IOException {
public static FileSystemTufStore newFileSystemStore(Path repoBaseDir) throws IOException {
if (!Files.isDirectory(repoBaseDir)) {
throw new IllegalArgumentException(repoBaseDir + " must be a file system directory.");
}
Expand All @@ -53,7 +53,7 @@ public static MutableTufStore newFileSystemStore(Path repoBaseDir) throws IOExce
return newFileSystemStore(repoBaseDir, defaultTargetsCache);
}

public static MutableTufStore newFileSystemStore(Path repoBaseDir, Path targetsCache) {
public static FileSystemTufStore newFileSystemStore(Path repoBaseDir, Path targetsCache) {
if (!Files.isDirectory(repoBaseDir)) {
throw new IllegalArgumentException(repoBaseDir + " must be a file system directory.");
}
Expand All @@ -65,50 +65,26 @@ public static MutableTufStore newFileSystemStore(Path repoBaseDir, Path targetsC

@Override
public String getIdentifier() {
return repoBaseDir.toAbsolutePath().toString();
return "Meta: " + repoBaseDir.toAbsolutePath() + ", Targets:" + targetsCache.toAbsolutePath();
}

@Override
public Optional<Root> loadTrustedRoot() throws IOException {
return loadRole(RootRole.ROOT, Root.class);
}

@Override
public Optional<Timestamp> loadTimestamp() throws IOException {
return loadRole(RootRole.TIMESTAMP, Timestamp.class);
}

@Override
public Optional<Snapshot> loadSnapshot() throws IOException {
return loadRole(RootRole.SNAPSHOT, Snapshot.class);
}

@Override
public Optional<Targets> loadTargets() throws IOException {
return loadRole(RootRole.TARGETS, Targets.class);
}

@Override
public Optional<Targets> loadDelegatedTargets(String roleName) throws IOException {
return loadRole(roleName, Targets.class);
}

@Override
public void storeTargetFile(String targetName, byte[] targetContents) throws IOException {
public void writeTarget(String targetName, byte[] targetContents) throws IOException {
Files.write(targetsCache.resolve(targetName), targetContents);
}

@Override
public byte[] getTargetFile(String targetName) throws IOException {
public byte[] readTarget(String targetName) throws IOException {
return Files.readAllBytes(targetsCache.resolve(targetName));
}

@Override
public void storeMeta(String roleName, SignedTufMeta<?> meta) throws IOException {
public void setMeta(String roleName, SignedTufMeta<?> meta) throws IOException {
storeRole(roleName, meta);
}

<T extends SignedTufMeta<?>> Optional<T> loadRole(String roleName, Class<T> tClass)
@Override
public <T extends SignedTufMeta<?>> Optional<T> findMeta(String roleName, Class<T> tClass)
throws IOException {
Path roleFile = repoBaseDir.resolve(roleName + ".json");
if (!roleFile.toFile().exists()) {
Expand All @@ -125,8 +101,8 @@ <T extends SignedTufMeta<?>> void storeRole(String roleName, T role) throws IOEx
}

@Override
public void storeTrustedRoot(Root root) throws IOException {
Optional<Root> trustedRoot = loadTrustedRoot();
public void setRoot(Root root) throws IOException {
Optional<Root> trustedRoot = findMeta(RootRole.ROOT, Root.class);
if (trustedRoot.isPresent()) {
try {
Files.move(
Expand Down
36 changes: 36 additions & 0 deletions sigstore-java/src/main/java/dev/sigstore/tuf/MetaReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024 The Sigstore Authors.
*
* Licensed 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 dev.sigstore.tuf;

import dev.sigstore.tuf.model.SignedTufMeta;
import dev.sigstore.tuf.model.TufMeta;
import java.io.IOException;
import java.util.Optional;

public interface MetaReader {

/**
* Return a named metadata item if there is any.
*
* @param roleName the name of the role to load (root, timestamp, snapshot, targets, or a
* delegated target role)
* @param tClass the class type
* @return an instance of the signed metadata for the role if it was found
* @throws IOException if an error occurs reading from the backing store
*/
<T extends SignedTufMeta<? extends TufMeta>> Optional<T> findMeta(
String roleName, Class<T> tClass) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Sigstore Authors.
* Copyright 2024 The Sigstore Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,28 +15,25 @@
*/
package dev.sigstore.tuf;

import dev.sigstore.tuf.model.*;
import dev.sigstore.tuf.model.Root;
import dev.sigstore.tuf.model.SignedTufMeta;
import dev.sigstore.tuf.model.TufMeta;
import java.io.IOException;

/** Defines the set of actions needed to support a local repository of TUF metadata. */
public interface MutableTufStore extends TufStore {
/**
* Writes a TUF target to the local target store.
*
* @param targetName the name of the target file to write (e.g. ctfe.pub)
* @param targetContents the content of the target file as bytes
* @throws IOException if an error occurs
*/
void storeTargetFile(String targetName, byte[] targetContents) throws IOException;
/** Interface that defined a mutable meta store functionality. */
public interface MetaStore extends MetaReader {

String getIdentifier();

/**
* Generic method to store one of the {@link SignedTufMeta} resources in the local tuf store.
* Generic method to store one of the {@link SignedTufMeta} resources in the local tuf store. Do
* not use for Root role, use {@link #setRoot(Root)} instead.
*
* @param roleName the name of the role
* @param meta the metadata to store
* @throws IOException if writing the resource causes an IO error
*/
void storeMeta(String roleName, SignedTufMeta<?> meta) throws IOException;
void setMeta(String roleName, SignedTufMeta<? extends TufMeta> meta) throws IOException;

/**
* Once you have ascertained that your root is trustworthy use this method to persist it to your
Expand All @@ -49,7 +46,7 @@ public interface MutableTufStore extends TufStore {
* @see <a
* href="https://theupdateframework.github.io/specification/latest/#detailed-client-workflow">5.3.8</a>
*/
void storeTrustedRoot(Root root) throws IOException;
void setRoot(Root root) throws IOException;

/**
* This clears out the snapshot and timestamp metadata from the store, as required when snapshot
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2024 The Sigstore Authors.
*
* Licensed 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 dev.sigstore.tuf;

import dev.sigstore.tuf.model.Root;
import dev.sigstore.tuf.model.RootRole;
import dev.sigstore.tuf.model.SignedTufMeta;
import dev.sigstore.tuf.model.TufMeta;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/** An in memory cache that will pass through to a provided local tuf store. */
public class PassthroughCacheMetaStore implements MetaReader, MetaStore {
private final MetaStore localStore;
private final Map<String, SignedTufMeta<? extends TufMeta>> cache;

private PassthroughCacheMetaStore(MetaStore localStore) {
this.localStore = localStore;
this.cache = new HashMap<>();
}

@Override
public String getIdentifier() {
return "In memory cache backed by: " + localStore.getIdentifier();
}

public static PassthroughCacheMetaStore newPassthroughMetaCache(MetaStore localStore) {
return new PassthroughCacheMetaStore(localStore);
}

@Override
public void setRoot(Root root) throws IOException {
// call storeRoot instead of generic storeMeta because it does extra work when storing on disk
localStore.setRoot(root);
cache.put(RootRole.ROOT, root);
}

@Override
@SuppressWarnings("unchecked")
public <T extends SignedTufMeta<? extends TufMeta>> Optional<T> findMeta(
String roleName, Class<T> tClass) throws IOException {
// check memory cache
if (cache.containsKey(roleName)) {
return Optional.of((T) cache.get(roleName));
}

// check backing storage and write to memory if found
var value = localStore.findMeta(roleName, tClass);
value.ifPresent(v -> cache.put(roleName, v));

return value;
}

@Override
public void setMeta(String roleName, SignedTufMeta<? extends TufMeta> meta) throws IOException {
if (Objects.equals(roleName, RootRole.ROOT)) {
throw new IllegalArgumentException("Calling setMeta on root instead of setRoot");
}
localStore.setMeta(roleName, meta);
cache.put(roleName, meta);
}

@Override
public void clearMetaDueToKeyRotation() throws IOException {
localStore.clearMetaDueToKeyRotation();
cache.remove(RootRole.TIMESTAMP);
cache.remove(RootRole.SNAPSHOT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,18 @@ public SigstoreTufClient build() throws IOException {
remoteMirror.toString().endsWith("/")
? remoteMirror
: new URL(remoteMirror.toExternalForm() + "/");
var targetsLocation = new URL(normalizedRemoteMirror.toExternalForm() + "targets");
var remoteTargetsLocation = new URL(normalizedRemoteMirror.toExternalForm() + "targets");
var filesystemTufStore = FileSystemTufStore.newFileSystemStore(tufCacheLocation);
var tufUpdater =
Updater.builder()
.setTrustedRootPath(trustedRoot)
.setLocalStore(FileSystemTufStore.newFileSystemStore(tufCacheLocation))
.setTrustedMetaStore(
TrustedMetaStore.newTrustedMetaStore(
PassthroughCacheMetaStore.newPassthroughMetaCache(filesystemTufStore)))
.setTargetStore(filesystemTufStore)
.setMetaFetcher(
MetaFetcher.newFetcher(HttpFetcher.newFetcher(normalizedRemoteMirror)))
.setTargetFetcher(HttpFetcher.newFetcher(targetsLocation))
.setTargetFetcher(HttpFetcher.newFetcher(remoteTargetsLocation))
.build();
return new SigstoreTufClient(tufUpdater, cacheValidity);
}
Expand Down Expand Up @@ -166,7 +170,7 @@ public void forceUpdate()
JsonFormat.parser()
.merge(
new String(
updater.getLocalStore().getTargetFile(TRUST_ROOT_FILENAME), StandardCharsets.UTF_8),
updater.getTargetStore().readTarget(TRUST_ROOT_FILENAME), StandardCharsets.UTF_8),
trustedRootBuilder);
sigstoreTrustedRoot = SigstoreTrustedRoot.from(trustedRootBuilder.build());
}
Expand Down
30 changes: 30 additions & 0 deletions sigstore-java/src/main/java/dev/sigstore/tuf/TargetReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2024 The Sigstore Authors.
*
* Licensed 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 dev.sigstore.tuf;

import java.io.IOException;

public interface TargetReader {

/**
* Reads a TUF target file from the local TUF store
*
* @param targetName the name of the target file to read (e.g. ctfe.pub)
* @return the content of the file as bytes
* @throws IOException if an error occurs
*/
byte[] readTarget(String targetName) throws IOException;
}
33 changes: 33 additions & 0 deletions sigstore-java/src/main/java/dev/sigstore/tuf/TargetStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2024 The Sigstore Authors.
*
* Licensed 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 dev.sigstore.tuf;

import java.io.IOException;

/** Interface that defined a mutable meta store functionality. */
public interface TargetStore extends TargetReader {

String getIdentifier();

/**
* Writes a TUF target to the local target store.
*
* @param targetName the name of the target file to write (e.g. ctfe.pub)
* @param targetContents the content of the target file as bytes
* @throws IOException if an error occurs
*/
void writeTarget(String targetName, byte[] targetContents) throws IOException;
}
Loading

0 comments on commit be5deff

Please sign in to comment.