Skip to content

Commit

Permalink
Metadata API: Add key helpers in Targets
Browse files Browse the repository at this point in the history
Root class has the functionality to add and remove keys for delegated
metadata (add_key()/remove_key()) but the other delegator Targets does
not.
It should provide the same/similar functionality.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
  • Loading branch information
MVrachev committed Sep 3, 2021
1 parent 30da6b7 commit 886c5d3
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 1 deletion.
74 changes: 73 additions & 1 deletion tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
Key,
MetaFile,
TargetFile,
Delegations,
DelegatedRole,
)

Expand Down Expand Up @@ -536,6 +535,79 @@ def test_metadata_targets(self):
targets.signed.targets[filename].to_dict(), fileinfo.to_dict()
)

def test_targets_key_api(self):
targets_path = os.path.join(
self.repo_dir, 'metadata', 'targets.json')
targets: Targets = Metadata[Targets].from_file(targets_path).signed

# Add a new delegated role "role2" in targets
delegated_role = DelegatedRole.from_dict({
"keyids": [],
"name": "role2",
"paths": ["fn3", "fn4"],
"terminating": False,
"threshold": 1
})
targets.delegations.roles["role2"] = delegated_role

key_dict = {
"keytype": "ed25519",
"keyval": {
"public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd"
},
"scheme": "ed25519"
}
key = Key.from_dict("id2", key_dict)

# Assert that delegated role "role1" does not contain the new key
self.assertNotIn(key.keyid, targets.delegations.roles["role1"].keyids)
targets.add_key("role1", key)

# Assert that the new key is added to the delegated role "role1"
self.assertIn(key.keyid, targets.delegations.roles["role1"].keyids)

# Confirm that the newly added key does not break the obj serialization
targets.to_dict()

# Try adding the same key again and assert its ignored.
past_keyid = targets.delegations.roles["role1"].keyids.copy()
targets.add_key("role1", key)
self.assertEqual(past_keyid, targets.delegations.roles["role1"].keyids)

# Try adding a key to a delegated role that doesn't exists
with self.assertRaises(KeyError):
targets.add_key("abc", key)

# Add the same key to "role2" as well
targets.add_key("role2", key)

# Remove the key from "role1" role ("role2" still uses it)
targets.remove_key("role1", key.keyid)

# Assert that delegated role "role1" doesn't contain the key.
self.assertNotIn(key.keyid, targets.delegations.roles["role1"].keyids)
self.assertIn(key.keyid, targets.delegations.roles["role2"].keyids)

# Remove the key from "role2" as well
targets.remove_key("role2", key.keyid)
self.assertNotIn(key.keyid, targets.delegations.roles["role2"].keyids)

# Try removing a key from delegated role that doesn't exists
with self.assertRaises(KeyError):
targets.remove_key("abc", key.keyid)

# Remove delegations as a whole
targets.delegations = None

# Test that calling add_key and remove_key throws an error
# and that delegations is still None after each of the api calls
with self.assertRaises(exceptions.UnknownRoleError):
targets.add_key("role1", key)
self.assertTrue(targets.delegations is None)
with self.assertRaises(exceptions.UnknownRoleError):
targets.remove_key("role1", key.keyid)
self.assertTrue(targets.delegations is None)


def test_length_and_hash_validation(self):

Expand Down
29 changes: 29 additions & 0 deletions tuf/api/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -1301,3 +1301,32 @@ def to_dict(self) -> Dict[str, Any]:
def update(self, fileinfo: TargetFile) -> None:
"""Assigns passed target file info to meta dict."""
self.targets[fileinfo.path] = fileinfo

def add_key(self, role: str, key: Key) -> None:
"""Adds new signing key for delegated role 'role'.
Raises:
UnknownRoleError: If there are no 'delegations' in the Targets obj.
KeyError: If there is no role 'role' in the Delegations object.
"""
if self.delegations is None:
raise exceptions.UnknownRoleError("Delegations doesn't exists!")
self.delegations.roles[role].keyids.add(key.keyid)
self.delegations.keys[key.keyid] = key

def remove_key(self, role: str, keyid: str) -> None:
"""Removes key from delegated role 'role' and updates the delegations
key store.
Raises:
UnknownRoleError: If there are no 'delegations' in the Targets obj.
KeyError: If there is no role 'role' in the Delegations object.
"""
if self.delegations is None:
raise exceptions.UnknownRoleError("Delegations doesn't exists!")
self.delegations.roles[role].keyids.remove(keyid)
for keyinfo in self.delegations.roles.values():
if keyid in keyinfo.keyids:
return

del self.delegations.keys[keyid]

0 comments on commit 886c5d3

Please sign in to comment.