From abc8943f7d89d33e34b9b2db26163591bb0aee4a Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 20 Jul 2016 13:35:36 -0700 Subject: [PATCH] adding tests and updating functionality to allow roles with too few keys Signed-off-by: David Lawrence (github: endophage) --- client/client_test.go | 2 +- client/helpers.go | 5 -- cmd/notary/delegations.go | 14 ++-- cmd/notary/delegations_test.go | 89 +++++++++++++-------- cmd/notary/integration_test.go | 3 +- tuf/data/roles_test.go | 89 ++++++++++++--------- tuf/tuf.go | 46 ++++++++--- tuf/tuf_test.go | 141 ++++++++++++++++++++++++++++++++- 8 files changed, 294 insertions(+), 95 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index f45a910f82..249db3b358 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -3022,7 +3022,7 @@ func TestRemoveDelegationChangefileApplicable(t *testing.T) { require.NoError(t, applyTargetsChange(repo.tufRepo, changes[2])) targetRole = repo.tufRepo.Targets[data.CanonicalTargetsRole] - require.Empty(t, targetRole.Signed.Delegations.Roles) + require.Len(t, targetRole.Signed.Delegations.Roles, 1) require.Empty(t, targetRole.Signed.Delegations.Keys) } diff --git a/client/helpers.go b/client/helpers.go index 0fb1a098f6..7f012a0e9c 100644 --- a/client/helpers.go +++ b/client/helpers.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "net/http" - "strings" "time" "github.com/Sirupsen/logrus" @@ -116,10 +115,6 @@ func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error { removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID]) } - // If we specify the only keys left delete the role, else just delete specified keys - if strings.Join(delgRole.ListKeyIDs(), ";") == strings.Join(removeTUFKeyIDs, ";") && len(td.AddKeys) == 0 { - return repo.DeleteDelegation(c.Scope()) - } err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, removeTUFKeyIDs, td.NewThreshold) if err != nil { return err diff --git a/cmd/notary/delegations.go b/cmd/notary/delegations.go index 93322df13c..7514cb18d8 100644 --- a/cmd/notary/delegations.go +++ b/cmd/notary/delegations.go @@ -32,7 +32,7 @@ var cmdDelegationRemoveTemplate = usageTemplate{ Long: "Remove KeyID(s) from the specified Role delegation in a specific Global Unique Name.", } -var cmdDelegationRemoveKeysTemplate = usageTemplate{ +var cmdDelegationPurgeKeysTemplate = usageTemplate{ Use: "purge [ GUN ]", Short: "Remove KeyID(s) from all delegations it is found in.", Long: "Remove KeyID(s) from all delegations it is found in, for which the signing keys are available. Warnings will be printed for delegations that cannot be updated.", @@ -58,9 +58,9 @@ func (d *delegationCommander) GetCommand() *cobra.Command { cmd := cmdDelegationTemplate.ToCommand(nil) cmd.AddCommand(cmdDelegationListTemplate.ToCommand(d.delegationsList)) - cmdRemDelgKeys := cmdDelegationRemoveKeysTemplate.ToCommand(d.delegationRemoveKeys) - cmdRemDelgKeys.Flags().StringSliceVar(&d.keyIDs, "key", nil, "Delegation keys to be removed from the GUN") - cmd.AddCommand(cmdRemDelgKeys) + cmdPurgeDelgKeys := cmdDelegationPurgeKeysTemplate.ToCommand(d.delegationPurgeKeys) + cmdPurgeDelgKeys.Flags().StringSliceVar(&d.keyIDs, "key", nil, "Delegation keys to be removed from the GUN") + cmd.AddCommand(cmdPurgeDelgKeys) cmdRemDelg := cmdDelegationRemoveTemplate.ToCommand(d.delegationRemove) cmdRemDelg.Flags().StringSliceVar(&d.paths, "paths", nil, "List of paths to remove") @@ -75,10 +75,10 @@ func (d *delegationCommander) GetCommand() *cobra.Command { return cmd } -func (d *delegationCommander) delegationRemoveKeys(cmd *cobra.Command, args []string) error { - if len(args) > 1 { +func (d *delegationCommander) delegationPurgeKeys(cmd *cobra.Command, args []string) error { + if len(args) != 1 { cmd.Usage() - return fmt.Errorf("Please provide a Global Unique Name as an argument to remove") + return fmt.Errorf("Please provide a single Global Unique Name as an argument to remove") } if len(d.keyIDs) == 0 { diff --git a/cmd/notary/delegations_test.go b/cmd/notary/delegations_test.go index 399d9dcfbd..d0ce432593 100644 --- a/cmd/notary/delegations_test.go +++ b/cmd/notary/delegations_test.go @@ -14,23 +14,36 @@ import ( "github.com/stretchr/testify/require" ) -var testTrustDir = "trust_dir" - -func setup() *delegationCommander { +func setup(trustDir string) *delegationCommander { return &delegationCommander{ configGetter: func() (*viper.Viper, error) { mainViper := viper.New() - mainViper.Set("trust_dir", testTrustDir) + mainViper.Set("trust_dir", trustDir) return mainViper, nil }, retriever: nil, } } -func TestAddInvalidDelegationName(t *testing.T) { - // Cleanup after test - defer os.RemoveAll(testTrustDir) +func TestPurgeDelegationKeys(t *testing.T) { + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + + cmdr := setup(tmpDir) + cmd := cmdr.GetCommand() + err = cmdr.delegationPurgeKeys(cmd, []string{}) + require.Error(t, err) + + err = cmdr.delegationPurgeKeys(cmd, []string{"gun"}) + require.Error(t, err) + + cmdr.keyIDs = []string{"abc"} + err = cmdr.delegationPurgeKeys(cmd, []string{"gun"}) + require.NoError(t, err) +} +func TestAddInvalidDelegationName(t *testing.T) { // Setup certificate tempFile, err := ioutil.TempFile("/tmp", "pemfile") require.NoError(t, err) @@ -41,7 +54,10 @@ func TestAddInvalidDelegationName(t *testing.T) { defer os.Remove(tempFile.Name()) // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to invalid delegation name (should be prefixed by "targets/") err = commander.delegationAdd(commander.GetCommand(), []string{"gun", "INVALID_NAME", tempFile.Name()}) @@ -49,9 +65,6 @@ func TestAddInvalidDelegationName(t *testing.T) { } func TestAddInvalidDelegationCert(t *testing.T) { - // Cleanup after test - defer os.RemoveAll(testTrustDir) - // Setup certificate tempFile, err := ioutil.TempFile("/tmp", "pemfile") require.NoError(t, err) @@ -62,7 +75,10 @@ func TestAddInvalidDelegationCert(t *testing.T) { defer os.Remove(tempFile.Name()) // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to expired cert err = commander.delegationAdd(commander.GetCommand(), []string{"gun", "targets/delegation", tempFile.Name(), "--paths", "path"}) @@ -70,9 +86,6 @@ func TestAddInvalidDelegationCert(t *testing.T) { } func TestAddInvalidShortPubkeyCert(t *testing.T) { - // Cleanup after test - defer os.RemoveAll(testTrustDir) - // Setup certificate tempFile, err := ioutil.TempFile("/tmp", "pemfile") require.NoError(t, err) @@ -83,7 +96,10 @@ func TestAddInvalidShortPubkeyCert(t *testing.T) { defer os.Remove(tempFile.Name()) // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to short RSA key err = commander.delegationAdd(commander.GetCommand(), []string{"gun", "targets/delegation", tempFile.Name(), "--paths", "path"}) @@ -91,53 +107,62 @@ func TestAddInvalidShortPubkeyCert(t *testing.T) { } func TestRemoveInvalidDelegationName(t *testing.T) { - // Cleanup after test - defer os.RemoveAll(testTrustDir) - // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to invalid delegation name (should be prefixed by "targets/") - err := commander.delegationRemove(commander.GetCommand(), []string{"gun", "INVALID_NAME", "fake_key_id1", "fake_key_id2"}) + err = commander.delegationRemove(commander.GetCommand(), []string{"gun", "INVALID_NAME", "fake_key_id1", "fake_key_id2"}) require.Error(t, err) } func TestRemoveAllInvalidDelegationName(t *testing.T) { - // Cleanup after test - defer os.RemoveAll(testTrustDir) - // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to invalid delegation name (should be prefixed by "targets/") - err := commander.delegationRemove(commander.GetCommand(), []string{"gun", "INVALID_NAME"}) + err = commander.delegationRemove(commander.GetCommand(), []string{"gun", "INVALID_NAME"}) require.Error(t, err) } func TestAddInvalidNumArgs(t *testing.T) { // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to invalid number of args (2 instead of 3) - err := commander.delegationAdd(commander.GetCommand(), []string{"not", "enough"}) + err = commander.delegationAdd(commander.GetCommand(), []string{"not", "enough"}) require.Error(t, err) } func TestListInvalidNumArgs(t *testing.T) { // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to invalid number of args (0 instead of 1) - err := commander.delegationsList(commander.GetCommand(), []string{}) + err = commander.delegationsList(commander.GetCommand(), []string{}) require.Error(t, err) } func TestRemoveInvalidNumArgs(t *testing.T) { // Setup commander - commander := setup() + tmpDir, err := ioutil.TempDir("/tmp", "notary-cmd-test-") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + commander := setup(tmpDir) // Should error due to invalid number of args (1 instead of 2) - err := commander.delegationRemove(commander.GetCommand(), []string{"notenough"}) + err = commander.delegationRemove(commander.GetCommand(), []string{"notenough"}) require.Error(t, err) } diff --git a/cmd/notary/integration_test.go b/cmd/notary/integration_test.go index 3b6761b847..c4e251dfc5 100644 --- a/cmd/notary/integration_test.go +++ b/cmd/notary/integration_test.go @@ -555,7 +555,8 @@ func TestClientDelegationsInteraction(t *testing.T) { // list delegations - we should see no delegations output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") require.NoError(t, err) - require.Contains(t, output, "No delegations present in this repository.") + require.NotContains(t, output, keyID) + require.NotContains(t, output, keyID2) // add delegation with multiple certs and multiple paths output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", tempFile.Name(), tempFile2.Name(), "--paths", "path1,path2") diff --git a/tuf/data/roles_test.go b/tuf/data/roles_test.go index 594060c4e3..e697ae0e31 100644 --- a/tuf/data/roles_test.go +++ b/tuf/data/roles_test.go @@ -105,44 +105,61 @@ func TestErrInvalidRole(t *testing.T) { } func TestIsDelegation(t *testing.T) { - require.True(t, IsDelegation(path.Join(CanonicalTargetsRole, "level1"))) - require.True(t, IsDelegation( - path.Join(CanonicalTargetsRole, "level1", "level2", "level3"))) - require.True(t, IsDelegation(path.Join(CanonicalTargetsRole, "under_score"))) - require.True(t, IsDelegation(path.Join(CanonicalTargetsRole, "hyphen-hyphen"))) - require.False(t, IsDelegation( - path.Join(CanonicalTargetsRole, strings.Repeat("x", 255-len(CanonicalTargetsRole))))) - - require.False(t, IsDelegation("")) - require.False(t, IsDelegation(CanonicalRootRole)) - require.False(t, IsDelegation(path.Join(CanonicalRootRole, "level1"))) - - require.False(t, IsDelegation(CanonicalTargetsRole)) - require.False(t, IsDelegation(CanonicalTargetsRole+"/")) - require.False(t, IsDelegation(path.Join(CanonicalTargetsRole, "level1")+"/")) - require.False(t, IsDelegation(path.Join(CanonicalTargetsRole, "UpperCase"))) - - require.False(t, IsDelegation( - path.Join(CanonicalTargetsRole, "directory")+"/../../traversal")) - - require.False(t, IsDelegation(CanonicalTargetsRole+"///test/middle/slashes")) - - require.False(t, IsDelegation(CanonicalTargetsRole+"/./././")) - - require.False(t, IsDelegation( - path.Join(" ", CanonicalTargetsRole, "level1"))) - - require.False(t, IsDelegation( - path.Join(" "+CanonicalTargetsRole, "level1"))) - - require.False(t, IsDelegation( - path.Join(CanonicalTargetsRole, "level1"+" "))) + f := require.False + tr := require.True + for val, check := range map[string]func(require.TestingT, bool, ...interface{}){ + // false checks + path.Join(CanonicalTargetsRole, strings.Repeat("x", 255-len(CanonicalTargetsRole))): f, + "": f, + CanonicalRootRole: f, + path.Join(CanonicalRootRole, "level1"): f, + CanonicalTargetsRole: f, + CanonicalTargetsRole + "/": f, + path.Join(CanonicalTargetsRole, "level1") + "/": f, + path.Join(CanonicalTargetsRole, "UpperCase"): f, + path.Join(CanonicalTargetsRole, "directory") + "/../../traversal": f, + CanonicalTargetsRole + "///test/middle/slashes": f, + CanonicalTargetsRole + "/./././": f, + path.Join(" ", CanonicalTargetsRole, "level1"): f, + path.Join(" "+CanonicalTargetsRole, "level1"): f, + path.Join(CanonicalTargetsRole, "level1"+" "): f, + path.Join(CanonicalTargetsRole, "white space"+"level2"): f, + path.Join(CanonicalTargetsRole, strings.Repeat("x", 256-len(CanonicalTargetsRole))): f, + + // true checks + path.Join(CanonicalTargetsRole, "level1"): tr, + path.Join(CanonicalTargetsRole, "level1", "level2", "level3"): tr, + path.Join(CanonicalTargetsRole, "under_score"): tr, + path.Join(CanonicalTargetsRole, "hyphen-hyphen"): tr, + } { + check(t, IsDelegation(val)) + } - require.False(t, IsDelegation( - path.Join(CanonicalTargetsRole, "white space"+"level2"))) +} - require.False(t, IsDelegation( - path.Join(CanonicalTargetsRole, strings.Repeat("x", 256-len(CanonicalTargetsRole))))) +func TestIsWildDelegation(t *testing.T) { + f := require.False + tr := require.True + for val, check := range map[string]func(require.TestingT, bool, ...interface{}){ + // false checks + CanonicalRootRole: f, + CanonicalTargetsRole: f, + CanonicalSnapshotRole: f, + CanonicalTimestampRole: f, + "foo": f, + "foo/*": f, + path.Join(CanonicalRootRole, "*"): f, + path.Join(CanonicalSnapshotRole, "*"): f, + path.Join(CanonicalTimestampRole, "*"): f, + path.Join(CanonicalTargetsRole, "*", "foo"): f, + path.Join(CanonicalTargetsRole, "*", "*"): f, + + // true checks + path.Join(CanonicalTargetsRole, "*"): tr, + path.Join(CanonicalTargetsRole, "foo", "*"): tr, + } { + check(t, IsWildDelegation(val)) + } } func TestValidRoleFunction(t *testing.T) { diff --git a/tuf/tuf.go b/tuf/tuf.go index e64b35615d..42bad9a155 100644 --- a/tuf/tuf.go +++ b/tuf/tuf.go @@ -326,16 +326,16 @@ func delegationUpdateVisitor(roleName string, addKeys data.KeyList, removeKeys, } } // We didn't find the role earlier, so create it only if we have keys to add + // that are also sufficient to meet the threshold. + if addKeys == nil { + addKeys = data.KeyList{} // initialize to empty list if necessary so calling .IDs() below won't panic + } if delgRole == nil { - if len(addKeys) > 0 { - delgRole, err = data.NewRole(roleName, newThreshold, addKeys.IDs(), addPaths) - if err != nil { - return err - } - } else { - // If we can't find the role and didn't specify keys to add, this is an error - return data.ErrInvalidRole{Role: roleName, Reason: "cannot create new delegation without keys"} + delgRole, err = data.NewRole(roleName, newThreshold, addKeys.IDs(), addPaths) + if err != nil { + return err } + } // Add the key IDs to the role and the keys themselves to the parent for _, k := range addKeys { @@ -345,7 +345,7 @@ func delegationUpdateVisitor(roleName string, addKeys data.KeyList, removeKeys, } // Make sure we have a valid role still if len(delgRole.KeyIDs) < delgRole.Threshold { - return data.ErrInvalidRole{Role: roleName, Reason: "insufficient keys to meet threshold"} + logrus.Warnf("role %s has fewer keys than its threshold of %d; it will not be usable until keys are added to it", delgRole.Name, delgRole.Threshold) } // NOTE: this closure CANNOT error after this point, as we've committed to editing the SignedTargets metadata in the repo object. // Any errors related to updating this delegation must occur before this point. @@ -416,9 +416,13 @@ func (tr *Repo) PurgeDelegationKeys(role string, removeKeys []string) error { purgeKeys := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} { var ( - deleteCandidates []string - err error + deleteCandidates []string + err error + canSign bool + needsUpdateCantSign bool ) + err = tr.VerifyCanSign(validRole.Name) + canSign = err == nil for id, key := range tgt.Signed.Delegations.Keys { var ( canonID string @@ -433,12 +437,32 @@ func (tr *Repo) PurgeDelegationKeys(role string, removeKeys []string) error { tufIDToCanon[id] = canonID } if _, ok := removeIDs[canonID]; ok { + if !canSign { + needsUpdateCantSign = true + break + } delete(tgt.Signed.Delegations.Keys, id) deleteCandidates = append(deleteCandidates, id) } } + if needsUpdateCantSign { + logrus.Warnf( + "role %s contains keys being purged but you do not have the necessary keys present to sign it; keys will not be purged from %s or its immediate children", + validRole.Name, + validRole.Name, + ) + return nil + } + if len(deleteCandidates) == 0 { + // none of the interesting keys were present + return nil + } + // delete candidate keys from all roles. Keep only roles that still have keys for _, role := range tgt.Signed.Delegations.Roles { role.RemoveKeys(deleteCandidates) + if len(role.KeyIDs) < role.Threshold { + logrus.Warnf("role %s has fewer keys than its threshold of %d; it will not be usable until keys are added to it", role.Name, role.Threshold) + } } return nil } diff --git a/tuf/tuf_test.go b/tuf/tuf_test.go index 69a3363be3..96b1379865 100644 --- a/tuf/tuf_test.go +++ b/tuf/tuf_test.go @@ -166,6 +166,144 @@ func TestUpdateDelegations(t *testing.T) { require.False(t, ok, "no empty targets file should be created for deepest delegation") } +func TestPurgeDelegationsKeyFromTop(t *testing.T) { + ed25519 := signed.NewEd25519() + repo := initRepo(t, ed25519) + + vetinari := path.Join(data.CanonicalTargetsRole, "vetinari") + sybil := path.Join(data.CanonicalTargetsRole, "sybil") + vimes := path.Join(data.CanonicalTargetsRole, "vimes") + carrot := path.Join(vimes, "carrot") + targetsWild := path.Join(data.CanonicalTargetsRole, "*") + + // create 2 keys, we'll purge one of them + testKey1, err := ed25519.Create(vetinari, testGUN, data.ED25519Key) + require.NoError(t, err) + testKey2, err := ed25519.Create(vetinari, testGUN, data.ED25519Key) + require.NoError(t, err) + + // create some delegations + err = repo.UpdateDelegationKeys(vetinari, []data.PublicKey{testKey1, testKey2}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(vetinari, []string{""}, []string{}, false) + require.NoError(t, err) + + err = repo.UpdateDelegationKeys(sybil, []data.PublicKey{testKey1}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(sybil, []string{""}, []string{}, false) + require.NoError(t, err) + + err = repo.UpdateDelegationKeys(vimes, []data.PublicKey{testKey2}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(vimes, []string{""}, []string{}, false) + require.NoError(t, err) + + err = repo.UpdateDelegationKeys(carrot, []data.PublicKey{testKey1}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(carrot, []string{""}, []string{}, false) + require.NoError(t, err) + + id1, err := utils.CanonicalKeyID(testKey1) + require.NoError(t, err) + err = repo.PurgeDelegationKeys(targetsWild, []string{id1}) + require.NoError(t, err) + + role, err := repo.GetDelegationRole(vetinari) + require.NoError(t, err) + require.Len(t, role.Keys, 1) + _, ok := role.Keys[testKey2.ID()] + require.True(t, ok) + + role, err = repo.GetDelegationRole(sybil) + require.NoError(t, err) + require.Len(t, role.Keys, 0) + + role, err = repo.GetDelegationRole(vimes) + require.NoError(t, err) + require.Len(t, role.Keys, 1) + _, ok = role.Keys[testKey2.ID()] + require.True(t, ok) + + role, err = repo.GetDelegationRole(carrot) + require.NoError(t, err) + require.Len(t, role.Keys, 0) +} + +func TestPurgeDelegationsKeyFromDeep(t *testing.T) { + ed25519 := signed.NewEd25519() + repo := initRepo(t, ed25519) + + vetinari := path.Join(data.CanonicalTargetsRole, "vetinari") + sybil := path.Join(data.CanonicalTargetsRole, "sybil") + vimes := path.Join(data.CanonicalTargetsRole, "vimes") + carrot := path.Join(vimes, "carrot") + vimesWild := path.Join(vimes, "*") + + // create 2 keys, we'll purge one of them + testKey1, err := ed25519.Create(vetinari, testGUN, data.ED25519Key) + require.NoError(t, err) + testKey2, err := ed25519.Create(vetinari, testGUN, data.ED25519Key) + require.NoError(t, err) + + // create some delegations + err = repo.UpdateDelegationKeys(vetinari, []data.PublicKey{testKey1, testKey2}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(vetinari, []string{""}, []string{}, false) + require.NoError(t, err) + + err = repo.UpdateDelegationKeys(sybil, []data.PublicKey{testKey1}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(sybil, []string{""}, []string{}, false) + require.NoError(t, err) + + err = repo.UpdateDelegationKeys(vimes, []data.PublicKey{testKey2}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(vimes, []string{""}, []string{}, false) + require.NoError(t, err) + + err = repo.UpdateDelegationKeys(carrot, []data.PublicKey{testKey1}, []string{}, 1) + require.NoError(t, err) + err = repo.UpdateDelegationPaths(carrot, []string{""}, []string{}, false) + require.NoError(t, err) + + id1, err := utils.CanonicalKeyID(testKey1) + require.NoError(t, err) + err = repo.PurgeDelegationKeys(vimesWild, []string{id1}) + require.NoError(t, err) + + role, err := repo.GetDelegationRole(vetinari) + require.NoError(t, err) + require.Len(t, role.Keys, 2) + _, ok := role.Keys[testKey1.ID()] + require.True(t, ok) + _, ok = role.Keys[testKey2.ID()] + require.True(t, ok) + + role, err = repo.GetDelegationRole(sybil) + require.NoError(t, err) + require.Len(t, role.Keys, 1) + _, ok = role.Keys[testKey1.ID()] + require.True(t, ok) + + role, err = repo.GetDelegationRole(vimes) + require.NoError(t, err) + require.Len(t, role.Keys, 1) + _, ok = role.Keys[testKey2.ID()] + require.True(t, ok) + + role, err = repo.GetDelegationRole(carrot) + require.NoError(t, err) + require.Len(t, role.Keys, 0) +} + +func TestPurgeDelegationsKeyBadWildRole(t *testing.T) { + ed25519 := signed.NewEd25519() + repo := initRepo(t, ed25519) + + err := repo.PurgeDelegationKeys("targets/foo", nil) + require.Error(t, err) + require.IsType(t, data.ErrInvalidRole{}, err) +} func TestUpdateDelegationsParentMissing(t *testing.T) { ed25519 := signed.NewEd25519() repo := initRepo(t, ed25519) @@ -262,8 +400,7 @@ func TestUpdateDelegationsNotEnoughKeys(t *testing.T) { require.NoError(t, err) err = repo.UpdateDelegationKeys("targets/role", []data.PublicKey{roleKey}, []string{}, 2) - require.Error(t, err) - require.IsType(t, data.ErrInvalidRole{}, err) + require.NoError(t, err) // no delegation metadata created for failed delegation _, ok := repo.Targets["targets/role"]