mirror of https://github.com/docker/docs.git
client library for retrieving keys and signatures for all roles
Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
This commit is contained in:
parent
60e6d254b3
commit
a052d9e105
|
|
@ -169,7 +169,7 @@ func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...st
|
|||
// currently we only support server managing timestamps and snapshots, and
|
||||
// nothing else - timestamps are always managed by the server, and implicit
|
||||
// (do not have to be passed in as part of `serverManagedRoles`, so that
|
||||
// the API of Initialize doens't change).
|
||||
// the API of Initialize doesn't change).
|
||||
var serverManagesSnapshot bool
|
||||
locallyManagedKeys := []string{
|
||||
data.CanonicalTargetsRole,
|
||||
|
|
@ -396,7 +396,7 @@ func (r *NotaryRepository) RemoveDelegation(name string, keyIDs, paths []string,
|
|||
}
|
||||
|
||||
// AddTarget creates new changelist entries to add a target to the given roles
|
||||
// in the repository when the changelist gets appied at publish time.
|
||||
// in the repository when the changelist gets applied at publish time.
|
||||
// If roles are unspecified, the default role is "targets".
|
||||
func (r *NotaryRepository) AddTarget(target *Target, roles ...string) error {
|
||||
|
||||
|
|
@ -454,7 +454,7 @@ func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, erro
|
|||
for _, role := range roles {
|
||||
// we don't need to do anything special with removing role from
|
||||
// roles because listSubtree always processes role and only excludes
|
||||
// descendent delegations that appear in roles.
|
||||
// descendant delegations that appear in roles.
|
||||
r.listSubtree(targets, role, roles...)
|
||||
}
|
||||
|
||||
|
|
@ -571,6 +571,56 @@ func (r *NotaryRepository) GetDelegationRoles() ([]*data.Role, error) {
|
|||
return allDelegations, nil
|
||||
}
|
||||
|
||||
// RoleWithSignatures is a Role with its associated signatures
|
||||
type RoleWithSignatures struct {
|
||||
Signatures []data.Signature
|
||||
data.Role
|
||||
}
|
||||
|
||||
// GetRepoRoleMetaInfo returns a list of RoleWithSignatures objects for this repo
|
||||
// This represents the latest metadata for each role in this repo
|
||||
func (r *NotaryRepository) GetRepoRoleMetaInfo() ([]RoleWithSignatures, error) {
|
||||
// Update to latest repo state
|
||||
_, err := r.Update(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get all role info from our updated keysDB
|
||||
roles, err := r.tufRepo.GetAllRoles()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 the role isn't a delegation, we should error -- this is only possible if we have invalid keyDB state
|
||||
if !data.IsDelegation(role.Name) {
|
||||
return nil, data.ErrInvalidRole{Role: role.Name, Reason: "invalid role name"}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// Publish pushes the local changes in signed material to the remote notary-server
|
||||
// Conceptually it performs an operation similar to a `git rebase`
|
||||
func (r *NotaryRepository) Publish() error {
|
||||
|
|
|
|||
|
|
@ -1115,7 +1115,7 @@ func testListTarget(t *testing.T, rootType string) {
|
|||
repo, _ := initializeRepo(t, rootType, "docker.com/notary", ts.URL, false)
|
||||
defer os.RemoveAll(repo.baseDir)
|
||||
|
||||
// tests need to manually boostrap timestamp as client doesn't generate it
|
||||
// tests need to manually bootstrap timestamp as client doesn't generate it
|
||||
err := repo.tufRepo.InitTimestamp()
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
|
|
@ -1171,7 +1171,7 @@ func testListTargetWithDelegates(t *testing.T, rootType string) {
|
|||
repo, _ := initializeRepo(t, rootType, "docker.com/notary", ts.URL, false)
|
||||
defer os.RemoveAll(repo.baseDir)
|
||||
|
||||
// tests need to manually boostrap timestamp as client doesn't generate it
|
||||
// tests need to manually bootstrap timestamp as client doesn't generate it
|
||||
err := repo.tufRepo.InitTimestamp()
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
|
|
@ -2949,3 +2949,102 @@ func TestDeleteRepoNoCerts(t *testing.T) {
|
|||
// Assert keys for this repo exist locally
|
||||
assertRepoHasExpectedKeys(t, repo, rootKeyID, true)
|
||||
}
|
||||
|
||||
// Test that we get a correct map of key IDs
|
||||
func TestGetRepoRoleMetaInfo(t *testing.T) {
|
||||
ts := fullTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
repo, _ := initializeRepo(t, data.ECDSAKey, "docker.com/notary", ts.URL, false)
|
||||
defer os.RemoveAll(repo.baseDir)
|
||||
|
||||
assert.NoError(t, repo.Publish())
|
||||
|
||||
rolesWithSigs, err := repo.GetRepoRoleMetaInfo()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Should only have base roles at this point
|
||||
assert.Len(t, rolesWithSigs, len(data.BaseRoles))
|
||||
// Each base role should only have one key, one signature, and its key should match the signature's key
|
||||
for _, role := range rolesWithSigs {
|
||||
assert.Len(t, role.Signatures, 1)
|
||||
assert.Len(t, role.KeyIDs, 1)
|
||||
assert.Equal(t, role.Signatures[0].KeyID, role.KeyIDs[0])
|
||||
}
|
||||
|
||||
// Create a delegation on the top level
|
||||
aKey := createKey(t, repo, "user", true)
|
||||
assert.NoError(t,
|
||||
repo.AddDelegation("targets/a", 1, []data.PublicKey{aKey}, []string{""}),
|
||||
"error creating delegation")
|
||||
|
||||
assert.NoError(t, repo.Publish())
|
||||
|
||||
rolesWithSigs, err = repo.GetRepoRoleMetaInfo()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, rolesWithSigs, len(data.BaseRoles)+1)
|
||||
// The delegation hasn't published any targets or metadata so it won't have a signature yet
|
||||
for _, role := range rolesWithSigs {
|
||||
if role.Name == "targets/a" {
|
||||
assert.Nil(t, role.Signatures)
|
||||
} else {
|
||||
assert.Len(t, role.Signatures, 1)
|
||||
assert.Equal(t, role.Signatures[0].KeyID, role.KeyIDs[0])
|
||||
}
|
||||
assert.Len(t, role.KeyIDs, 1)
|
||||
}
|
||||
|
||||
addTarget(t, repo, "current", "../fixtures/intermediate-ca.crt", "targets/a")
|
||||
assert.NoError(t, repo.Publish())
|
||||
|
||||
rolesWithSigs, err = repo.GetRepoRoleMetaInfo()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, rolesWithSigs, len(data.BaseRoles)+1)
|
||||
// The delegation should have a signature now
|
||||
for _, role := range rolesWithSigs {
|
||||
assert.Len(t, role.Signatures, 1)
|
||||
assert.Equal(t, role.Signatures[0].KeyID, role.KeyIDs[0])
|
||||
assert.Len(t, role.KeyIDs, 1)
|
||||
}
|
||||
|
||||
// Create another delegation, one level further
|
||||
bKey := createKey(t, repo, "user", true)
|
||||
assert.NoError(t,
|
||||
repo.AddDelegation("targets/a/b", 1, []data.PublicKey{bKey}, []string{""}),
|
||||
"error creating delegation")
|
||||
|
||||
assert.NoError(t, repo.Publish())
|
||||
|
||||
rolesWithSigs, err = repo.GetRepoRoleMetaInfo()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, rolesWithSigs, len(data.BaseRoles)+2)
|
||||
// The nested delegation hasn't published any targets or metadata so it won't have a signature yet
|
||||
for _, role := range rolesWithSigs {
|
||||
if role.Name == "targets/a/b" {
|
||||
assert.Nil(t, role.Signatures)
|
||||
} else {
|
||||
assert.Len(t, role.Signatures, 1)
|
||||
assert.Equal(t, role.Signatures[0].KeyID, role.KeyIDs[0])
|
||||
}
|
||||
assert.Len(t, role.KeyIDs, 1)
|
||||
}
|
||||
|
||||
// Now make another repo and check that we don't pick up its roles
|
||||
repo2, _ := initializeRepo(t, data.ECDSAKey, "docker.com/notary2", ts.URL, false)
|
||||
defer os.RemoveAll(repo2.baseDir)
|
||||
|
||||
assert.NoError(t, repo2.Publish())
|
||||
|
||||
// repo2 only has the base roles
|
||||
rolesWithSigs2, err := repo2.GetRepoRoleMetaInfo()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, rolesWithSigs2, len(data.BaseRoles))
|
||||
|
||||
// original repo stays in same state (base roles + 2 delegations)
|
||||
rolesWithSigs, err = repo.GetRepoRoleMetaInfo()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, rolesWithSigs, len(data.BaseRoles)+2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ func (e ErrNoSuchRole) Error() string {
|
|||
return fmt.Sprintf("role does not exist: %s", e.Role)
|
||||
}
|
||||
|
||||
// ErrNoRoles indicates no roles exist for this repo
|
||||
type ErrNoRoles struct{}
|
||||
|
||||
func (e ErrNoRoles) Error() string {
|
||||
return fmt.Sprintf("no roles exist")
|
||||
}
|
||||
|
||||
// ErrInvalidRole represents an error regarding a role. Typically
|
||||
// something like a role for which sone of the public keys were
|
||||
// not found in the TUF repo.
|
||||
|
|
|
|||
|
|
@ -58,6 +58,15 @@ func (db *KeyDB) AddRole(r *data.Role) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetAllRoles gets all roles from the database
|
||||
func (db *KeyDB) GetAllRoles() []*data.Role {
|
||||
roles := []*data.Role{}
|
||||
for _, role := range db.roles {
|
||||
roles = append(roles, role)
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
// GetKey pulls a key out of the database by its ID
|
||||
func (db *KeyDB) GetKey(id string) data.PublicKey {
|
||||
return db.keys[id]
|
||||
|
|
|
|||
|
|
@ -173,6 +173,15 @@ func (tr *Repo) RemoveBaseKeys(role string, keyIDs ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetAllRoles returns a list of all role entries for this TUF repo
|
||||
func (tr *Repo) GetAllRoles() ([]*data.Role, error) {
|
||||
roles := tr.keysDB.GetAllRoles()
|
||||
if len(roles) == 0 {
|
||||
return nil, data.ErrNoRoles{}
|
||||
}
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
// GetDelegation finds the role entry representing the provided
|
||||
// role name or ErrInvalidRole
|
||||
func (tr *Repo) GetDelegation(role string) (*data.Role, error) {
|
||||
|
|
|
|||
|
|
@ -936,3 +936,19 @@ func TestAddBaseKeysToRoot(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllRoles(t *testing.T) {
|
||||
ed25519 := signed.NewEd25519()
|
||||
keyDB := keys.NewDB()
|
||||
repo := initRepo(t, ed25519, keyDB)
|
||||
|
||||
// After we init, we get the base roles
|
||||
roles, err := repo.GetAllRoles()
|
||||
assert.Len(t, roles, len(data.BaseRoles))
|
||||
|
||||
// Clear the keysDB, check that we error
|
||||
repo.keysDB = keys.NewDB()
|
||||
roles, err = repo.GetAllRoles()
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, data.ErrNoRoles{}, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue