mirror of
https://github.com/theupdateframework/python-tuf
synced 2026-05-24 10:08:28 +00:00
Merge pull request #599 from vladimir-v-diaz/address_review_pr#580
Address review of pr#580 (Revise API for delegating paths)
This commit is contained in:
commit
fec88be847
4 changed files with 62 additions and 57 deletions
|
|
@ -1460,17 +1460,17 @@ def test_add_paths(self):
|
|||
self.targets_object.delegate(rolename, public_keys, [], threshold,
|
||||
list_of_targets=None, path_hash_prefixes=None)
|
||||
|
||||
# Delegate an extra role for test coverage (i.e., check that restricted
|
||||
# paths are not added to a child role not requested.)
|
||||
# Delegate an extra role for test coverage (i.e., to later verify that
|
||||
# delegated paths are not added to a child role that was not requested).
|
||||
self.targets_object.delegate('junk_role', public_keys, [])
|
||||
|
||||
restricted_path = os.path.join(self.targets_directory, 'tuf_files')
|
||||
os.mkdir(restricted_path)
|
||||
paths = [restricted_path + '/*']
|
||||
delegated_path = os.path.join(self.targets_directory, 'tuf_files')
|
||||
os.mkdir(delegated_path)
|
||||
paths = [delegated_path + '/*']
|
||||
self.targets_object.add_paths(paths, 'tuf')
|
||||
|
||||
# Retrieve 'targets_object' roleinfo, and verify the roleinfo contains
|
||||
# the expected restricted paths of the delegated role. Only
|
||||
# Retrieve 'targets_object' roleinfo, and verify the roleinfo contains the
|
||||
# expected delegated paths of the delegated role.
|
||||
targets_object_roleinfo = tuf.roledb.get_roleinfo(self.targets_object.rolename,
|
||||
'test_repository')
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
- [Managing Targets](#managing_targets)
|
||||
|
||||
<a name="overview">
|
||||
## Overview
|
||||
## Overview
|
||||
The Update Framework (TUF) is a Python-based security system for software
|
||||
updates. In order to prevent your users from downloading vulnerable or malicious
|
||||
code disguised as updates to your software, TUF requires that each update you
|
||||
|
|
@ -56,7 +56,7 @@ is the private key.
|
|||
>>> generate_and_write_rsa_keypair("path/to/key")
|
||||
Enter a password for the RSA key:
|
||||
Confirm:
|
||||
>>>
|
||||
>>>
|
||||
```
|
||||
|
||||
We can also use the bits parameter to set a different key length (the default
|
||||
|
|
@ -131,7 +131,7 @@ public key by the server (upon uploading) and by the clients (when updating).
|
|||
|
||||
<a name="signing_and_writing_the_metadata">
|
||||
### Signing and Writing the Metadata ###
|
||||
In order to sign the metadata, we need to import the private key corresponding
|
||||
In order to sign the metadata, we need to import the private key corresponding
|
||||
to the public key we added to the project. One the key is loaded to the project,
|
||||
it will automatically be used to sign the metadata whenever it is written.
|
||||
|
||||
|
|
@ -160,14 +160,14 @@ several contributors, you may want to consider adding
|
|||
|
||||
<a name="loading_an_existing_project">
|
||||
## Loading an Existing Project
|
||||
To make changes to existing metadata, we will need the Project again. We can
|
||||
restore it with the load_project() function.
|
||||
To make changes to existing metadata, we will need the Project again. We can
|
||||
restore it with the load_project() function.
|
||||
|
||||
```
|
||||
>>> from tuf.developer_tool import *
|
||||
>>> project = load_project("local/path/to/metadata")
|
||||
```
|
||||
Each time the project is loaded anew, the necessary private keys must also be
|
||||
Each time the project is loaded anew, the necessary private keys must also be
|
||||
loaded in order to sign metadata.
|
||||
|
||||
```
|
||||
|
|
@ -205,21 +205,21 @@ contain any number of public keys. We can also add keys to the role after
|
|||
creating it using the [add\_verification\_key()](#adding_a_key_to_a_delegation)
|
||||
method.
|
||||
|
||||
### Restricted Paths
|
||||
### Delegated Paths
|
||||
|
||||
By default, a delegated role is permitted to add and modify targets anywhere in
|
||||
the Project's targets directory. We can assign restricted paths to a delegated
|
||||
role to limit this permission.
|
||||
the Project's targets directory. We can delegate trust of paths to a role to
|
||||
limit this permission.
|
||||
|
||||
```
|
||||
>>> project.add_restricted_paths(["restricted/filepath"], "newrole")
|
||||
>>> project.add_paths(["delegated/filepath"], "newrole")
|
||||
```
|
||||
|
||||
This will prevent the delegated role from signing targets whose local filepaths
|
||||
do not begin with "restricted/filepath". We can assign several restricted
|
||||
filepaths to a role by adding them to the list in the first parameter, or by
|
||||
invoking the method again. A role with multiple restricted paths can add
|
||||
targets to any of them.
|
||||
do not begin with "delegated/filepath". We can delegate several filepaths to a
|
||||
role by adding them to the list in the first parameter, or by invoking the
|
||||
method again. A role with multiple delegated paths can add targets to any of
|
||||
them.
|
||||
|
||||
Note that this method is invoked from the parent role (in this case, the Project)
|
||||
and takes the delegated role name as an argument.
|
||||
|
|
@ -248,16 +248,16 @@ Delegations can be revoked, removing the delegated role from the project.
|
|||
```
|
||||
|
||||
<a name="managing_keys">
|
||||
## Managing Keys
|
||||
## Managing Keys
|
||||
This section describes the key-related functions and parameters not covered in
|
||||
the [Creating a Simple Project](#creating_a_simple_project) section.
|
||||
|
||||
### Additional Parameters for Key Generation
|
||||
When generating keys, it is possible to specify the length of the key in bits
|
||||
When generating keys, it is possible to specify the length of the key in bits
|
||||
and its password as parameters:
|
||||
|
||||
```
|
||||
>>> generate_and_write_rsa_keypair("path/to/key",bits=2048, password="pw")
|
||||
>>> generate_and_write_rsa_keypair("path/to/key", bits=2048, password="pw")
|
||||
```
|
||||
The bits parameter defaults to 3072, and values below 2048 will raise an error.
|
||||
The password parameter is only intended to be used in scripts.
|
||||
|
|
|
|||
|
|
@ -585,9 +585,9 @@ The `delegate_hashed_bins()` method has the following form:
|
|||
delegate_hashed_bins(list_of_targets, keys_of_hashed_bins, number_of_bins)
|
||||
```
|
||||
|
||||
A complete example of retrieving target paths to add to hashed bins,
|
||||
performing the hashed bin delegations, signing them, and finally adding
|
||||
restricted paths for some role is provided next.
|
||||
We next provide a complete example of retrieving target paths to add to hashed
|
||||
bins, performing the hashed bin delegations, signing them, and delegating paths
|
||||
to some role.
|
||||
```Python
|
||||
# Get a list of target paths for the hashed bins.
|
||||
>>> targets = \
|
||||
|
|
|
|||
|
|
@ -1765,12 +1765,12 @@ def target_files(self):
|
|||
def add_paths(self, paths, child_rolename):
|
||||
"""
|
||||
<Purpose>
|
||||
Add 'paths' to the delegated paths for 'child_rolename'. 'paths' can be
|
||||
a list of either file paths or glob patterns. The updater client
|
||||
verifies the target paths specified by child roles, and searches for
|
||||
targets by visiting these delegated paths. A child role may only provide
|
||||
targets specifically listed in the delegations field of the parent role,
|
||||
or a target that matches a delegated path.
|
||||
Add 'paths' to the delegated paths of 'child_rolename'. 'paths' can be a
|
||||
list of either file paths or glob patterns. The updater client verifies
|
||||
the target paths specified by child roles, and searches for targets by
|
||||
visiting these delegated paths. A child role may only provide targets
|
||||
specifically listed in the delegations field of the delegating role, or a
|
||||
target that matches a delegated path.
|
||||
|
||||
>>>
|
||||
>>>
|
||||
|
|
@ -1787,8 +1787,12 @@ def add_paths(self, paths, child_rolename):
|
|||
'Django' in 'unclaimed').
|
||||
|
||||
<Exceptions>
|
||||
securesystemslib.exceptions.Error, if a path or glob pattern in 'paths'
|
||||
is not a string, or if 'child_rolename' has not been delegated yet.
|
||||
securesystemslib.exceptions.FormatError, if a path or glob pattern in
|
||||
'paths' is not a string, or if 'child_rolename' is not a formatted
|
||||
rolename.
|
||||
|
||||
securesystemslib.exceptions.Error, if 'child_rolename' has not been
|
||||
delegated yet.
|
||||
|
||||
<Side Effects>
|
||||
Modifies this Targets' delegations field.
|
||||
|
|
@ -1797,7 +1801,7 @@ def add_paths(self, paths, child_rolename):
|
|||
None.
|
||||
"""
|
||||
|
||||
# Does 'filepath' have the correct format?
|
||||
# Do the argument have the correct format?
|
||||
# Ensure the arguments have the appropriate number of objects and object
|
||||
# types, and that all dict keys are properly named.
|
||||
# Raise 'securesystemslib.exceptions.FormatError' if there is a mismatch.
|
||||
|
|
@ -1817,9 +1821,8 @@ def add_paths(self, paths, child_rolename):
|
|||
for path in paths:
|
||||
# Are the delegated paths or glob patterns located in the repository's
|
||||
# targets directory? If so, log it - the paths don't necessarily need to
|
||||
# be located in the repository's directory.
|
||||
# Append a trailing path separator with
|
||||
# os.path.join(path, '').
|
||||
# be located in the repository's directory. Append a trailing path
|
||||
# separator with os.path.join(path, '').
|
||||
targets_directory = os.path.join(self._targets_directory, '')
|
||||
if not path.startswith(targets_directory):
|
||||
logger.debug(repr(path) + ' is not located in the'
|
||||
|
|
@ -1835,14 +1838,14 @@ def add_paths(self, paths, child_rolename):
|
|||
# Update the delegated paths of 'child_rolename' to add relative paths.
|
||||
for role in roleinfo['delegations']['roles']:
|
||||
if role['name'] == child_rolename:
|
||||
delegated_paths = role['paths']
|
||||
|
||||
for relative_path in relative_paths:
|
||||
if relative_path not in delegated_paths:
|
||||
delegated_paths.append(relative_path)
|
||||
for relative_path in relative_paths:
|
||||
if relative_path not in role['paths']:
|
||||
role['paths'].append(relative_path)
|
||||
|
||||
else:
|
||||
logger.debug(repr(relative_path) + ' is already a delegated path.')
|
||||
else:
|
||||
logger.debug(repr(relative_path) + ' is already a delegated path.')
|
||||
logger.debug(repr(role['name']) + ' does not match child rolename.')
|
||||
|
||||
tuf.roledb.update_roleinfo(self._rolename, roleinfo,
|
||||
repository_name=self._repository_name)
|
||||
|
|
@ -2157,8 +2160,11 @@ def delegate(self, rolename, public_keys, paths, threshold=1,
|
|||
|
||||
paths:
|
||||
The paths, or glob patterns, delegated to 'rolename'. Any targets
|
||||
added to 'rolename' must match one of the paths or glob patterns in
|
||||
'paths'.
|
||||
added to 'rolename', via add_targets() or 'list_of_targets', must
|
||||
match one of the paths or glob patterns in 'paths'. Apart from the
|
||||
public keys of 'rolename', the delegated 'paths' is often known and
|
||||
specified when a delegation is first performed. If the delegator
|
||||
is unsure of which 'paths' to delegate, 'paths' can be set to [''].
|
||||
|
||||
threshold:
|
||||
The threshold number of keys of 'rolename'.
|
||||
|
|
@ -2175,7 +2181,7 @@ def delegate(self, rolename, public_keys, paths, threshold=1,
|
|||
file specified by 'target/other_role'.
|
||||
|
||||
list_of_targets:
|
||||
A list of target filepaths that are added to the paths of 'rolename'.
|
||||
A list of target filepaths that are added to 'rolename'.
|
||||
'list_of_targets' is a list of target filepaths, can be empty, and each
|
||||
filepath must be located in the repository's targets directory. The
|
||||
list of targets should also exist at the specified paths, otherwise
|
||||
|
|
@ -2261,14 +2267,13 @@ def delegate(self, rolename, public_keys, paths, threshold=1,
|
|||
# targets directory.
|
||||
relative_paths = []
|
||||
|
||||
if paths is not None:
|
||||
for path in paths:
|
||||
if not path.startswith(self._targets_directory + os.sep):
|
||||
logger.debug(repr(path) + ' is not loated in the repository\'s'
|
||||
' targets directory: ' + repr(self._targets_directory))
|
||||
for path in paths:
|
||||
if not path.startswith(self._targets_directory + os.sep):
|
||||
logger.debug(repr(path) + ' is not loated in the repository\'s'
|
||||
' targets directory: ' + repr(self._targets_directory))
|
||||
|
||||
# Append a trailing path separator with os.path.join(path, '').
|
||||
relative_paths.append(path[targets_directory_length:])
|
||||
# Append a trailing path separator with os.path.join(path, '').
|
||||
relative_paths.append(path[targets_directory_length:])
|
||||
|
||||
# Create a new Targets object for the 'rolename' delegation. An initial
|
||||
# expiration is set (3 months from the current time).
|
||||
|
|
@ -2299,10 +2304,10 @@ def delegate(self, rolename, public_keys, paths, threshold=1,
|
|||
'terminating': terminating,
|
||||
'paths': list(roleinfo['paths'].keys())}
|
||||
|
||||
if paths is not None:
|
||||
if paths:
|
||||
roleinfo['paths'] = relative_paths
|
||||
|
||||
if path_hash_prefixes is not None:
|
||||
if path_hash_prefixes:
|
||||
roleinfo['path_hash_prefixes'] = path_hash_prefixes
|
||||
# A role in a delegations must list either 'path_hash_prefixes'
|
||||
# or 'paths'.
|
||||
|
|
|
|||
Loading…
Reference in a new issue