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