mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	update the dependency to v0.7.0 to be closer to what docker/cli uses; https://github.com/theupdateframework/notary/compare/v0.6.1...v0.7.0 Note that docker/cli is slightly ahead of v0.7.0, and uses bf96a202a09a; https://github.com/theupdateframework/notary/compare/v0.7.0...bf96a202a09a Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			258 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package client
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	canonicaljson "github.com/docker/go/canonical/json"
 | 
						|
	store "github.com/theupdateframework/notary/storage"
 | 
						|
	"github.com/theupdateframework/notary/tuf"
 | 
						|
	"github.com/theupdateframework/notary/tuf/data"
 | 
						|
	"github.com/theupdateframework/notary/tuf/utils"
 | 
						|
)
 | 
						|
 | 
						|
// Target represents a simplified version of the data TUF operates on, so external
 | 
						|
// applications don't have to depend on TUF data types.
 | 
						|
type Target struct {
 | 
						|
	Name   string                    // the name of the target
 | 
						|
	Hashes data.Hashes               // the hash of the target
 | 
						|
	Length int64                     // the size in bytes of the target
 | 
						|
	Custom *canonicaljson.RawMessage // the custom data provided to describe the file at TARGETPATH
 | 
						|
}
 | 
						|
 | 
						|
// TargetWithRole represents a Target that exists in a particular role - this is
 | 
						|
// produced by ListTargets and GetTargetByName
 | 
						|
type TargetWithRole struct {
 | 
						|
	Target
 | 
						|
	Role data.RoleName
 | 
						|
}
 | 
						|
 | 
						|
// TargetSignedStruct is a struct that contains a Target, the role it was found in, and the list of signatures for that role
 | 
						|
type TargetSignedStruct struct {
 | 
						|
	Role       data.DelegationRole
 | 
						|
	Target     Target
 | 
						|
	Signatures []data.Signature
 | 
						|
}
 | 
						|
 | 
						|
//ErrNoSuchTarget is returned when no valid trust data is found.
 | 
						|
type ErrNoSuchTarget string
 | 
						|
 | 
						|
func (f ErrNoSuchTarget) Error() string {
 | 
						|
	return fmt.Sprintf("No valid trust data for %s", string(f))
 | 
						|
}
 | 
						|
 | 
						|
// RoleWithSignatures is a Role with its associated signatures
 | 
						|
type RoleWithSignatures struct {
 | 
						|
	Signatures []data.Signature
 | 
						|
	data.Role
 | 
						|
}
 | 
						|
 | 
						|
// NewReadOnly is the base method that returns a new notary repository for reading.
 | 
						|
// It expects an initialized cache. In case of a nil remote store, a default
 | 
						|
// offline store is used.
 | 
						|
func NewReadOnly(repo *tuf.Repo) ReadOnly {
 | 
						|
	return &reader{tufRepo: repo}
 | 
						|
}
 | 
						|
 | 
						|
type reader struct {
 | 
						|
	tufRepo *tuf.Repo
 | 
						|
}
 | 
						|
 | 
						|
// ListTargets lists all targets for the current repository. The list of
 | 
						|
// roles should be passed in order from highest to lowest priority.
 | 
						|
//
 | 
						|
// IMPORTANT: if you pass a set of roles such as [ "targets/a", "targets/x"
 | 
						|
// "targets/a/b" ], even though "targets/a/b" is part of the "targets/a" subtree
 | 
						|
// its entries will be strictly shadowed by those in other parts of the "targets/a"
 | 
						|
// subtree and also the "targets/x" subtree, as we will defer parsing it until
 | 
						|
// we explicitly reach it in our iteration of the provided list of roles.
 | 
						|
func (r *reader) ListTargets(roles ...data.RoleName) ([]*TargetWithRole, error) {
 | 
						|
	if len(roles) == 0 {
 | 
						|
		roles = []data.RoleName{data.CanonicalTargetsRole}
 | 
						|
	}
 | 
						|
	targets := make(map[string]*TargetWithRole)
 | 
						|
	for _, role := range roles {
 | 
						|
		// Define an array of roles to skip for this walk (see IMPORTANT comment above)
 | 
						|
		skipRoles := utils.RoleNameSliceRemove(roles, role)
 | 
						|
 | 
						|
		// Define a visitor function to populate the targets map in priority order
 | 
						|
		listVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
 | 
						|
			// We found targets so we should try to add them to our targets map
 | 
						|
			for targetName, targetMeta := range tgt.Signed.Targets {
 | 
						|
				// Follow the priority by not overriding previously set targets
 | 
						|
				// and check that this path is valid with this role
 | 
						|
				if _, ok := targets[targetName]; ok || !validRole.CheckPaths(targetName) {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				targets[targetName] = &TargetWithRole{
 | 
						|
					Target: Target{
 | 
						|
						Name:   targetName,
 | 
						|
						Hashes: targetMeta.Hashes,
 | 
						|
						Length: targetMeta.Length,
 | 
						|
						Custom: targetMeta.Custom,
 | 
						|
					},
 | 
						|
					Role: validRole.Name,
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		r.tufRepo.WalkTargets("", role, listVisitorFunc, skipRoles...)
 | 
						|
	}
 | 
						|
 | 
						|
	var targetList []*TargetWithRole
 | 
						|
	for _, v := range targets {
 | 
						|
		targetList = append(targetList, v)
 | 
						|
	}
 | 
						|
 | 
						|
	return targetList, nil
 | 
						|
}
 | 
						|
 | 
						|
// GetTargetByName returns a target by the given name. If no roles are passed
 | 
						|
// it uses the targets role and does a search of the entire delegation
 | 
						|
// graph, finding the first entry in a breadth first search of the delegations.
 | 
						|
// If roles are passed, they should be passed in descending priority and
 | 
						|
// the target entry found in the subtree of the highest priority role
 | 
						|
// will be returned.
 | 
						|
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
 | 
						|
func (r *reader) GetTargetByName(name string, roles ...data.RoleName) (*TargetWithRole, error) {
 | 
						|
	if len(roles) == 0 {
 | 
						|
		roles = append(roles, data.CanonicalTargetsRole)
 | 
						|
	}
 | 
						|
	var resultMeta data.FileMeta
 | 
						|
	var resultRoleName data.RoleName
 | 
						|
	var foundTarget bool
 | 
						|
	for _, role := range roles {
 | 
						|
		// Define an array of roles to skip for this walk (see IMPORTANT comment above)
 | 
						|
		skipRoles := utils.RoleNameSliceRemove(roles, role)
 | 
						|
 | 
						|
		// Define a visitor function to find the specified target
 | 
						|
		getTargetVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
 | 
						|
			if tgt == nil {
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
			// We found the target and validated path compatibility in our walk,
 | 
						|
			// so we should stop our walk and set the resultMeta and resultRoleName variables
 | 
						|
			if resultMeta, foundTarget = tgt.Signed.Targets[name]; foundTarget {
 | 
						|
				resultRoleName = validRole.Name
 | 
						|
				return tuf.StopWalk{}
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		// Check that we didn't error, and that we assigned to our target
 | 
						|
		if err := r.tufRepo.WalkTargets(name, role, getTargetVisitorFunc, skipRoles...); err == nil && foundTarget {
 | 
						|
			return &TargetWithRole{Target: Target{Name: name, Hashes: resultMeta.Hashes, Length: resultMeta.Length, Custom: resultMeta.Custom}, Role: resultRoleName}, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil, ErrNoSuchTarget(name)
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all
 | 
						|
// roles, and returns a list of TargetSignedStructs for each time it finds the specified target.
 | 
						|
// If given an empty string for a target name, it will return back all targets signed into the repository in every role
 | 
						|
func (r *reader) GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error) {
 | 
						|
	var targetInfoList []TargetSignedStruct
 | 
						|
 | 
						|
	// Define a visitor function to find the specified target
 | 
						|
	getAllTargetInfoByNameVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
 | 
						|
		if tgt == nil {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		// We found a target and validated path compatibility in our walk,
 | 
						|
		// so add it to our list if we have a match
 | 
						|
		// if we have an empty name, add all targets, else check if we have it
 | 
						|
		var targetMetaToAdd data.Files
 | 
						|
		if name == "" {
 | 
						|
			targetMetaToAdd = tgt.Signed.Targets
 | 
						|
		} else {
 | 
						|
			if meta, ok := tgt.Signed.Targets[name]; ok {
 | 
						|
				targetMetaToAdd = data.Files{name: meta}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		for targetName, resultMeta := range targetMetaToAdd {
 | 
						|
			targetInfo := TargetSignedStruct{
 | 
						|
				Role:       validRole,
 | 
						|
				Target:     Target{Name: targetName, Hashes: resultMeta.Hashes, Length: resultMeta.Length, Custom: resultMeta.Custom},
 | 
						|
				Signatures: tgt.Signatures,
 | 
						|
			}
 | 
						|
			targetInfoList = append(targetInfoList, targetInfo)
 | 
						|
		}
 | 
						|
		// continue walking to all child roles
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Check that we didn't error, and that we found the target at least once
 | 
						|
	if err := r.tufRepo.WalkTargets(name, "", getAllTargetInfoByNameVisitorFunc); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if len(targetInfoList) == 0 {
 | 
						|
		return nil, ErrNoSuchTarget(name)
 | 
						|
	}
 | 
						|
	return targetInfoList, nil
 | 
						|
}
 | 
						|
 | 
						|
// ListRoles returns a list of RoleWithSignatures objects for this repo
 | 
						|
// This represents the latest metadata for each role in this repo
 | 
						|
func (r *reader) ListRoles() ([]RoleWithSignatures, error) {
 | 
						|
	// Get all role info from our updated keysDB, can be empty
 | 
						|
	roles := r.tufRepo.GetAllLoadedRoles()
 | 
						|
 | 
						|
	var roleWithSigs []RoleWithSignatures
 | 
						|
 | 
						|
	// Populate RoleWithSignatures with Role from keysDB and signatures from TUF metadata
 | 
						|
	for _, role := range roles {
 | 
						|
		roleWithSig := RoleWithSignatures{Role: *role, Signatures: nil}
 | 
						|
		switch role.Name {
 | 
						|
		case data.CanonicalRootRole:
 | 
						|
			roleWithSig.Signatures = r.tufRepo.Root.Signatures
 | 
						|
		case data.CanonicalTargetsRole:
 | 
						|
			roleWithSig.Signatures = r.tufRepo.Targets[data.CanonicalTargetsRole].Signatures
 | 
						|
		case data.CanonicalSnapshotRole:
 | 
						|
			roleWithSig.Signatures = r.tufRepo.Snapshot.Signatures
 | 
						|
		case data.CanonicalTimestampRole:
 | 
						|
			roleWithSig.Signatures = r.tufRepo.Timestamp.Signatures
 | 
						|
		default:
 | 
						|
			if !data.IsDelegation(role.Name) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if _, ok := r.tufRepo.Targets[role.Name]; ok {
 | 
						|
				// We'll only find a signature if we've published any targets with this delegation
 | 
						|
				roleWithSig.Signatures = r.tufRepo.Targets[role.Name].Signatures
 | 
						|
			}
 | 
						|
		}
 | 
						|
		roleWithSigs = append(roleWithSigs, roleWithSig)
 | 
						|
	}
 | 
						|
	return roleWithSigs, nil
 | 
						|
}
 | 
						|
 | 
						|
// GetDelegationRoles returns the keys and roles of the repository's delegations
 | 
						|
// Also converts key IDs to canonical key IDs to keep consistent with signing prompts
 | 
						|
func (r *reader) GetDelegationRoles() ([]data.Role, error) {
 | 
						|
	// All top level delegations (ex: targets/level1) are stored exclusively in targets.json
 | 
						|
	_, ok := r.tufRepo.Targets[data.CanonicalTargetsRole]
 | 
						|
	if !ok {
 | 
						|
		return nil, store.ErrMetaNotFound{Resource: data.CanonicalTargetsRole.String()}
 | 
						|
	}
 | 
						|
 | 
						|
	// make a copy for traversing nested delegations
 | 
						|
	allDelegations := []data.Role{}
 | 
						|
 | 
						|
	// Define a visitor function to populate the delegations list and translate their key IDs to canonical IDs
 | 
						|
	delegationCanonicalListVisitor := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
 | 
						|
		// For the return list, update with a copy that includes canonicalKeyIDs
 | 
						|
		// These aren't validated by the validRole
 | 
						|
		canonicalDelegations, err := translateDelegationsToCanonicalIDs(tgt.Signed.Delegations)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		allDelegations = append(allDelegations, canonicalDelegations...)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	err := r.tufRepo.WalkTargets("", "", delegationCanonicalListVisitor)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return allDelegations, nil
 | 
						|
}
 |