func TestApplyTargetsDelegationCreateDelete(t *testing.T) { _, repo, cs := testutils.EmptyRepo() newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) tgts := repo.Targets[data.CanonicalTargetsRole] assert.Len(t, tgts.Signed.Delegations.Roles, 1) assert.Len(t, tgts.Signed.Delegations.Keys, 1) _, ok := tgts.Signed.Delegations.Keys[newKey.ID()] assert.True(t, ok) role := tgts.Signed.Delegations.Roles[0] assert.Len(t, role.KeyIDs, 1) assert.Equal(t, newKey.ID(), role.KeyIDs[0]) assert.Equal(t, "targets/level1", role.Name) assert.Equal(t, "level1", role.Paths[0]) // delete delegation ch = changelist.NewTufChange( changelist.ActionDelete, "targets/level1", changelist.TypeTargetsDelegation, "", nil, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) assert.Len(t, tgts.Signed.Delegations.Roles, 0) assert.Len(t, tgts.Signed.Delegations.Keys, 0) }
func TestApplyTargetsDelegationAlreadyExistingMergePaths(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) // we have sufficient checks elsewhere we don't need to confirm that // creating fresh works here via more asserts. // Use different path for this changelist td.AddPaths = []string{"level2"} tdJSON, err = json.Marshal(td) assert.NoError(t, err) ch = changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) // when attempting to create the same role again, check that we // merged with previous details err = applyTargetsChange(repo, ch) assert.NoError(t, err) delegation, _, err := repo.GetDelegation("targets/level1") assert.NoError(t, err) // Assert we have both paths assert.Contains(t, delegation.Paths, "level2") assert.Contains(t, delegation.Paths, "level1") }
// adds a TUF Change template to the given roles func addChange(cl *changelist.FileChangelist, c changelist.Change, roles ...string) error { if len(roles) == 0 { roles = []string{data.CanonicalTargetsRole} } var changes []changelist.Change for _, role := range roles { role = strings.ToLower(role) // Ensure we can only add targets to the CanonicalTargetsRole, // or a Delegation role (which is <CanonicalTargetsRole>/something else) if role != data.CanonicalTargetsRole && !data.IsDelegation(role) { return data.ErrInvalidRole{ Role: role, Reason: "cannot add targets to this role", } } changes = append(changes, changelist.NewTufChange( c.Action(), role, c.Type(), c.Path(), c.Content(), )) } for _, c := range changes { if err := cl.Add(c); err != nil { return err } } return nil }
// Applying a delegation whose parent doesn't exist fails. func TestApplyTargetsDelegationParentDoesntExist(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) // make sure a key exists for the previous level, so it's not a missing // key error, but we don't care about this key _, err = cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) newKey, err := cs.Create("targets/level1/level2", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1/level2", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.Error(t, err) assert.IsType(t, data.ErrInvalidRole{}, err) }
func TestApplyTargetsDelegationCreateInvalid(t *testing.T) { _, repo, cs := testutils.EmptyRepo() newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, AddPathHashPrefixes: []string{"abc"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.Error(t, err) }
// AddDelegation creates a new changelist entry to add a delegation to the repository // when the changelist gets applied at publish time. This does not do any validation // other than checking the name of the delegation to add - all that will happen // at publish time. func (r *NotaryRepository) AddDelegation(name string, threshold int, delegationKeys []data.PublicKey, paths []string) error { if !data.IsDelegation(name) { return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"} } cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() logrus.Debugf(`Adding delegation "%s" with threshold %d, and %d keys\n`, name, threshold, len(delegationKeys)) tdJSON, err := json.Marshal(&changelist.TufDelegation{ NewThreshold: threshold, AddKeys: data.KeyList(delegationKeys), AddPaths: paths, }) if err != nil { return err } template := changelist.NewTufChange( changelist.ActionCreate, name, changelist.TypeTargetsDelegation, "", // no path tdJSON, ) return addChange(cl, template, name) }
func (r *NotaryRepository) rootFileKeyChange(role, action string, key data.PublicKey) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() kl := make(data.KeyList, 0, 1) kl = append(kl, key) meta := changelist.TufRootData{ RoleName: role, Keys: kl, } metaJSON, err := json.Marshal(meta) if err != nil { return err } c := changelist.NewTufChange( action, changelist.ScopeRoot, changelist.TypeRootRole, role, metaJSON, ) err = cl.Add(c) if err != nil { return err } return nil }
func TestApplyTargetsDelegationInvalidJSONContent(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON[1:], ) err = applyTargetsChange(repo, ch) assert.Error(t, err) }
func (r *NotaryRepository) rootFileKeyChange(role, action string, key data.PublicKey) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() k, ok := key.(*data.TUFKey) if !ok { return errors.New("Invalid key type found during rotation.") } meta := changelist.TufRootData{ RoleName: role, Keys: []data.TUFKey{*k}, } metaJSON, err := json.Marshal(meta) if err != nil { return err } c := changelist.NewTufChange( action, changelist.ScopeRoot, changelist.TypeRootRole, role, metaJSON, ) err = cl.Add(c) if err != nil { return err } return nil }
// RemoveDelegation creates a new changelist entry to remove a delegation from // the repository when the changelist gets applied at publish time. // This does not validate that the delegation exists, since one might exist // after applying all changes. func (r *NotaryRepository) RemoveDelegation(name string, keyIDs, paths []string, removeAll bool) error { if !data.IsDelegation(name) { return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"} } cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() logrus.Debugf(`Removing delegation "%s"\n`, name) var template *changelist.TufChange // We use the Delete action only for force removal, Update is used for removing individual keys and paths if removeAll { template = changelist.NewTufChange( changelist.ActionDelete, name, changelist.TypeTargetsDelegation, "", // no path nil, // deleting role, no data needed ) } else { tdJSON, err := json.Marshal(&changelist.TufDelegation{ RemoveKeys: keyIDs, RemovePaths: paths, }) if err != nil { return err } template = changelist.NewTufChange( changelist.ActionUpdate, name, changelist.TypeTargetsDelegation, "", // no path tdJSON, ) } return addChange(cl, template, name) }
func newDeleteDelegationChange(name string, content []byte) *changelist.TufChange { return changelist.NewTufChange( changelist.ActionDelete, name, changelist.TypeTargetsDelegation, "", // no path for delegations content, ) }
// RemoveTarget creates new changelist entries to remove a target from the given // roles in the repository when the changelist gets applied at publish time. // If roles are unspecified, the default role is "target". func (r *NotaryRepository) RemoveTarget(targetName string, roles ...string) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } logrus.Debugf("Removing target \"%s\"", targetName) template := changelist.NewTufChange(changelist.ActionDelete, "", changelist.TypeTargetsTarget, targetName, nil) return addChange(cl, template, roles...) }
// RemoveTarget creates a new changelist entry to remove a target from the repository // when the changelist gets applied at publish time func (r *NotaryRepository) RemoveTarget(targetName string) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } logrus.Debugf("Removing target \"%s\"", targetName) c := changelist.NewTufChange(changelist.ActionDelete, changelist.ScopeTargets, "target", targetName, nil) err = cl.Add(c) if err != nil { return err } return nil }
func TestApplyTargetsDelegationInvalidAction(t *testing.T) { _, repo, _ := testutils.EmptyRepo() ch := changelist.NewTufChange( "bad action", "targets/level1", changelist.TypeTargetsDelegation, "", nil, ) err := applyTargetsChange(repo, ch) assert.Error(t, err) }
func TestApplyTargetsChangeInvalidType(t *testing.T) { _, repo, _ := testutils.EmptyRepo() ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", "badType", "", nil, ) err := applyTargetsChange(repo, ch) assert.Error(t, err) }
// AddTarget creates new changelist entries to add a target to the given roles // in the repository when the changelist gets appied at publish time. // If roles are unspecified, the default role is "targets". func (r *NotaryRepository) AddTarget(target *Target, roles ...string) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() logrus.Debugf("Adding target \"%s\" with sha256 \"%x\" and size %d bytes.\n", target.Name, target.Hashes["sha256"], target.Length) meta := data.FileMeta{Length: target.Length, Hashes: target.Hashes} metaJSON, err := json.Marshal(meta) if err != nil { return err } template := changelist.NewTufChange( changelist.ActionCreate, "", changelist.TypeTargetsTarget, target.Name, metaJSON) return addChange(cl, template, roles...) }
// AddTarget adds a new target to the repository, forcing a timestamps check from TUF func (r *NotaryRepository) AddTarget(target *Target) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } logrus.Debugf("Adding target \"%s\" with sha256 \"%x\" and size %d bytes.\n", target.Name, target.Hashes["sha256"], target.Length) meta := data.FileMeta{Length: target.Length, Hashes: target.Hashes} metaJSON, err := json.Marshal(meta) if err != nil { return err } c := changelist.NewTufChange(changelist.ActionCreate, "targets", "target", target.Name, metaJSON) err = cl.Add(c) if err != nil { return err } return cl.Close() }
func (r *NotaryRepository) rootFileKeyChange(cl changelist.Changelist, role, action string, key data.PublicKey) error { kl := make(data.KeyList, 0, 1) kl = append(kl, key) meta := changelist.TufRootData{ RoleName: role, Keys: kl, } metaJSON, err := json.Marshal(meta) if err != nil { return err } c := changelist.NewTufChange( action, changelist.ScopeRoot, changelist.TypeRootRole, role, metaJSON, ) return cl.Add(c) }
// RemoveDelegation creates a new changelist entry to remove a delegation from // the repository when the changelist gets applied at publish time. // This does not validate that the delegation exists, since one might exist // after applying all changes. func (r *NotaryRepository) RemoveDelegation(name string) error { if !data.IsDelegation(name) { return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"} } cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() logrus.Debugf(`Removing delegation "%s"\n`, name) template := changelist.NewTufChange( changelist.ActionDelete, name, changelist.TypeTargetsDelegation, "", // no path nil, ) return addChange(cl, template, name) }
func TestApplyTargetsDelegationCreateAlreadyExisting(t *testing.T) { _, repo, cs := testutils.EmptyRepo() newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) // we have sufficient checks elsewhere we don't need to confirm that // creating fresh works here via more asserts. // when attempting to create the same role again, assert we receive // an ErrInvalidRole because an existing role can't be "created" err = applyTargetsChange(repo, ch) assert.Error(t, err) assert.IsType(t, data.ErrInvalidRole{}, err) }
func TestApplyTargetsDelegationCreate2Deep(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) tgts := repo.Targets[data.CanonicalTargetsRole] assert.Len(t, tgts.Signed.Delegations.Roles, 1) assert.Len(t, tgts.Signed.Delegations.Keys, 1) _, ok := tgts.Signed.Delegations.Keys[newKey.ID()] assert.True(t, ok) role := tgts.Signed.Delegations.Roles[0] assert.Len(t, role.KeyIDs, 1) assert.Equal(t, newKey.ID(), role.KeyIDs[0]) assert.Equal(t, "targets/level1", role.Name) assert.Equal(t, "level1", role.Paths[0]) // init delegations targets file. This would be done as part of a publish // operation repo.InitTargets("targets/level1") td = &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1/level2"}, } tdJSON, err = json.Marshal(td) assert.NoError(t, err) ch = changelist.NewTufChange( changelist.ActionCreate, "targets/level1/level2", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) tgts = repo.Targets["targets/level1"] assert.Len(t, tgts.Signed.Delegations.Roles, 1) assert.Len(t, tgts.Signed.Delegations.Keys, 1) _, ok = tgts.Signed.Delegations.Keys[newKey.ID()] assert.True(t, ok) role = tgts.Signed.Delegations.Roles[0] assert.Len(t, role.KeyIDs, 1) assert.Equal(t, newKey.ID(), role.KeyIDs[0]) assert.Equal(t, "targets/level1/level2", role.Name) assert.Equal(t, "level1/level2", role.Paths[0]) }
func TestApplyTargetsDelegationCreateAlreadyExisting(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) // we have sufficient checks elsewhere we don't need to confirm that // creating fresh works here via more asserts. extraKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create delegation kl = data.KeyList{extraKey} td = &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err = json.Marshal(td) assert.NoError(t, err) ch = changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) // when attempting to create the same role again, check that we added a key err = applyTargetsChange(repo, ch) assert.NoError(t, err) delegation, keys, err := repo.GetDelegation("targets/level1") assert.NoError(t, err) assert.Contains(t, delegation.Paths, "level1") assert.Equal(t, len(delegation.KeyIDs), 2) for _, keyID := range delegation.KeyIDs { _, ok := keys[keyID] assert.True(t, ok) } }
func TestApplyTargetsDelegationCreate2SharedKey(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) // create first delegation kl := data.KeyList{newKey} td := &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level1"}, } tdJSON, err := json.Marshal(td) assert.NoError(t, err) ch := changelist.NewTufChange( changelist.ActionCreate, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) // create second delegation kl = data.KeyList{newKey} td = &changelist.TufDelegation{ NewThreshold: 1, AddKeys: kl, AddPaths: []string{"level2"}, } tdJSON, err = json.Marshal(td) assert.NoError(t, err) ch = changelist.NewTufChange( changelist.ActionCreate, "targets/level2", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) tgts := repo.Targets[data.CanonicalTargetsRole] assert.Len(t, tgts.Signed.Delegations.Roles, 2) assert.Len(t, tgts.Signed.Delegations.Keys, 1) role1 := tgts.Signed.Delegations.Roles[0] assert.Len(t, role1.KeyIDs, 1) assert.Equal(t, newKey.ID(), role1.KeyIDs[0]) assert.Equal(t, "targets/level1", role1.Name) assert.Equal(t, "level1", role1.Paths[0]) role2 := tgts.Signed.Delegations.Roles[1] assert.Len(t, role2.KeyIDs, 1) assert.Equal(t, newKey.ID(), role2.KeyIDs[0]) assert.Equal(t, "targets/level2", role2.Name) assert.Equal(t, "level2", role2.Paths[0]) // delete one delegation, ensure shared key remains td = &changelist.TufDelegation{ RemoveKeys: []string{newKey.ID()}, } tdJSON, err = json.Marshal(td) assert.NoError(t, err) ch = changelist.NewTufChange( changelist.ActionDelete, "targets/level1", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) assert.Len(t, tgts.Signed.Delegations.Roles, 1) assert.Len(t, tgts.Signed.Delegations.Keys, 1) // delete other delegation, ensure key cleaned up ch = changelist.NewTufChange( changelist.ActionDelete, "targets/level2", changelist.TypeTargetsDelegation, "", tdJSON, ) err = applyTargetsChange(repo, ch) assert.NoError(t, err) assert.Len(t, tgts.Signed.Delegations.Roles, 0) assert.Len(t, tgts.Signed.Delegations.Keys, 0) }