Support app-specific user-agents

* application user-agent can be set with UpdaterConfig object
* Setting will affect the default fetcher only
* the application user-agent will be prefixed to the ngclient
  default user-agent

Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
This commit is contained in:
Jussi Kukkonen 2024-04-19 17:53:34 +03:00
parent c6256875f0
commit fe2068697c
3 changed files with 22 additions and 5 deletions

View file

@ -10,7 +10,7 @@
# can be moved out of _internal once sigstore-python 1.0 is not relevant.
import logging
from typing import Dict, Iterator, Tuple
from typing import Dict, Iterator, Optional, Tuple
from urllib import parse
# Imports
@ -35,7 +35,10 @@ class RequestsFetcher(FetcherInterface):
"""
def __init__(
self, socket_timeout: int = 30, chunk_size: int = 400000
self,
socket_timeout: int = 30,
chunk_size: int = 400000,
app_user_agent: Optional[str] = None,
) -> None:
# http://docs.python-requests.org/en/master/user/advanced/#session-objects:
#
@ -56,6 +59,7 @@ def __init__(
# Default settings
self.socket_timeout: int = socket_timeout # seconds
self.chunk_size: int = chunk_size # bytes
self.app_user_agent = app_user_agent
def _fetch(self, url: str) -> Iterator[bytes]:
"""Fetch the contents of HTTP/HTTPS url from a remote server.
@ -138,6 +142,8 @@ def _get_session(self, url: str) -> requests.Session:
self._sessions[session_index] = session
ua = f"tuf/{tuf.__version__} {session.headers['User-Agent']}"
if self.app_user_agent is not None:
ua = f"{self.app_user_agent} {ua}"
session.headers["User-Agent"] = ua
logger.debug("Made new session %s", session_index)

View file

@ -5,6 +5,7 @@
from dataclasses import dataclass
from enum import Flag, unique
from typing import Optional
@unique
@ -39,6 +40,8 @@ class UpdaterConfig:
envelope_type: Configures deserialization and verification mode of TUF
metadata. Per default, it is treated as traditional canonical JSON
-based TUF Metadata.
app_user_agent: Application user agent, e.g. "MyApp/1.0.0". This will be
prefixed to ngclient user agent when the default fetcher is used.
"""
max_root_rotations: int = 32
@ -49,3 +52,4 @@ class UpdaterConfig:
targets_max_length: int = 5000000 # bytes
prefix_targets_with_hash: bool = True
envelope_type: EnvelopeType = EnvelopeType.METADATA
app_user_agent: Optional[str] = None

View file

@ -93,11 +93,15 @@ def __init__(
else:
self._target_base_url = _ensure_trailing_slash(target_base_url)
# Read trusted local root metadata
data = self._load_local_metadata(Root.type)
self._fetcher = fetcher or requests_fetcher.RequestsFetcher()
self.config = config or UpdaterConfig()
if fetcher is not None:
self._fetcher = fetcher
else:
self._fetcher = requests_fetcher.RequestsFetcher(
app_user_agent=self.config.app_user_agent
)
supported_envelopes = [EnvelopeType.METADATA, EnvelopeType.SIMPLE]
if self.config.envelope_type not in supported_envelopes:
raise ValueError(
@ -105,6 +109,9 @@ def __init__(
f"got '{self.config.envelope_type}'"
)
# Read trusted local root metadata
data = self._load_local_metadata(Root.type)
self._trusted_set = trusted_metadata_set.TrustedMetadataSet(
data, self.config.envelope_type
)