TrustedMetaStore.java

/*
 * Copyright 2023 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.Snapshot;
import dev.sigstore.tuf.model.Targets;
import dev.sigstore.tuf.model.Timestamp;
import dev.sigstore.tuf.model.TufMeta;
import java.io.IOException;
import java.util.Optional;

/** Local storage for local state of TUF metadata. */
public class TrustedMetaStore {

  private final MetaStore metaStore;

  private TrustedMetaStore(MetaStore metaStore) {
    this.metaStore = metaStore;
  }

  public static TrustedMetaStore newTrustedMetaStore(MetaStore metaStore) {
    return new TrustedMetaStore(metaStore);
  }

  /**
   * A generic string for identifying the local store in debug messages. A file system based
   * implementation might return the path being used for storage, while an in-memory store may just
   * return something like 'in-memory'.
   */
  public String getIdentifier() {
    return metaStore.getIdentifier();
  }

  /**
   * Return a named metadata item. Fail if there isn't 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
   * @throws IllegalStateException if the data was never persisted and this function was called
   */
  <T extends SignedTufMeta<? extends TufMeta>> T getMeta(String roleName, Class<T> tClass)
      throws IOException {
    return metaStore
        .readMeta(roleName, tClass)
        .orElseThrow(
            () ->
                new IllegalStateException(
                    "No cached "
                        + roleName
                        + " to load. This error may occur when (1) update hasn't been called or (2) when find should have been used instead of get."));
  }

  public void setRoot(Root root) throws IOException {
    metaStore.writeMeta(RootRole.ROOT, root);
  }

  public Root getRoot() throws IOException {
    return getMeta(RootRole.ROOT, Root.class);
  }

  public Optional<Root> findRoot() throws IOException {
    return metaStore.readMeta(RootRole.ROOT, Root.class);
  }

  public void setTimestamp(Timestamp timestamp) throws IOException {
    metaStore.writeMeta(RootRole.TIMESTAMP, timestamp);
  }

  public Timestamp getTimestamp() throws IOException {
    return getMeta(RootRole.TIMESTAMP, Timestamp.class);
  }

  public Optional<Timestamp> findTimestamp() throws IOException {
    return metaStore.readMeta(RootRole.TIMESTAMP, Timestamp.class);
  }

  public void setSnapshot(Snapshot snapshot) throws IOException {
    metaStore.writeMeta(RootRole.SNAPSHOT, snapshot);
  }

  public Snapshot getSnapshot() throws IOException {
    return getMeta(RootRole.SNAPSHOT, Snapshot.class);
  }

  public Optional<Snapshot> findSnapshot() throws IOException {
    return metaStore.readMeta(RootRole.SNAPSHOT, Snapshot.class);
  }

  public void setTargets(Targets targets) throws IOException {
    metaStore.writeMeta(RootRole.TARGETS, targets);
  }

  public Targets getTargets() throws IOException {
    return getMeta(RootRole.TARGETS, Targets.class);
  }

  public Optional<Targets> findTargets() throws IOException {
    return metaStore.readMeta(RootRole.TARGETS, Targets.class);
  }

  public void clearMetaDueToKeyRotation() throws IOException {
    metaStore.clearMeta(RootRole.TIMESTAMP);
    metaStore.clearMeta(RootRole.SNAPSHOT);
  }
}