Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate Signer API docs for RTD #622

Merged
merged 3 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ env/*
tests/htmlcov/*
.DS_Store
.python-version

# Sphinx documentation
docs/_build/
21 changes: 21 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2
build:
os: ubuntu-22.04
apt_packages:
- swig
- softhsm2
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swig and softhsm2 are needed for pykcs11, which we install in requirements-docs.txt via requirements-pinned.txt.

We can probably shuffle requirements* files so that we don't need to install any optional requirements.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion makes sense but if rtd is fine with us installing random software... I suppose this is ok

tools:
python: "3.11"

sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true

python:
install:
- requirements: requirements-docs.txt
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
65 changes: 65 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys

sys.path.insert(0, os.path.abspath(os.path.join("..")))

import securesystemslib

# -- Project information -----------------------------------------------------

project = "securesystemslib"
copyright = "2023, New York University and the securesystemslib contributors"
author = "New York University and the securesystemslib contributors"


# -- General configuration ---------------------------------------------------

master_doc = "index"

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.napoleon",
"sphinx.ext.autosummary",
"sphinx.ext.autosectionlabel",
]

autosectionlabel_prefix_document = True


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = "sphinx_rtd_theme"

# -- Autodoc configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html

# Shorten paths
add_module_names = False
python_use_unqualified_type_names = True

# Show typehints in argument doc lines, but not in signatures
autodoc_typehints = "description"

autodoc_default_options = {
"members": True,
"inherited-members": "Exception", # excl. members inherited from 'Exception'
}

# Version
version = securesystemslib.__version__
17 changes: 17 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Welcome to ``securesystemslib``
===============================

A cryptography interface to sign and verify `TUF
<https://theupdateframework.io>`_ and `in-toto <https://in-toto.io>`_ metadata.

.. note::
This documentation is built for
`securesystemslib (Python) <https://github.com/secure-systems-lab/securesystemslib>`_, used by
`python-tuf <https://github.com/theupdateframework/python-tuf/>`_ and
`in-toto (Python) <https://github.com/in-toto/in-toto>`_ reference implementations.

.. toctree::
:maxdepth: 2
:caption: Contents

signer
35 changes: 35 additions & 0 deletions docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
21 changes: 21 additions & 0 deletions docs/signer.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Signer API
==========

.. currentmodule:: securesystemslib.signer

.. warning::
The API is experimental and may change without warning in versions ``<1.0.0``.

See `'New Signer API' <https://theupdateframework.github.io/python-tuf/2023/01/24/securesystemslib-signer-api.html>`_ blog post
for background infos.


.. Autodoc cannot resolve docs for imported globals (sphinx-doc/sphinx#6495)
.. As workaround we reference their original internal definition.
.. autodata:: securesystemslib.signer._signer.SIGNER_FOR_URI_SCHEME
:no-value:
.. autodata:: securesystemslib.signer._key.KEY_FOR_TYPE_AND_SCHEME
:no-value:
.. autoclass:: securesystemslib.signer.Signer
.. autoclass:: securesystemslib.signer.Key
.. autoclass:: securesystemslib.signer.Signature
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ tox
-r requirements.txt
-r requirements-test.txt
-r requirements-lint.txt
-r requirements-docs.txt
-e .
5 changes: 5 additions & 0 deletions requirements-docs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-r requirements-pinned.txt

# install sphinx and its extensions
sphinx
sphinx-rtd-theme
10 changes: 8 additions & 2 deletions securesystemslib/signer/_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
# NOTE Key dispatch table is defined here so it's usable by Key,
# but is populated in __init__.py (and can be appended by users).
KEY_FOR_TYPE_AND_SCHEME: Dict[Tuple[str, str], Type] = {}
"""Key dispatch table for ``Key.from_dict()``

See ``securesystemslib.signer.KEY_FOR_TYPE_AND_SCHEME`` for default key types
and schemes, and how to register custom implementations.
"""


class Key(metaclass=ABCMeta):
Expand Down Expand Up @@ -121,8 +126,9 @@ def from_dict(cls, keyid: str, key_dict: Dict[str, Any]) -> "Key":
Key implementations must override this factory constructor that is used
as a deserialization helper.

Users should call Key.from_dict(): it dispatches to the actual subclass
implementation based on supported keys in KEY_FOR_TYPE_AND_SCHEME.
Users should call ``Key.from_dict()``: it dispatches to the actual
subclass implementation based on supported keys in
``KEY_FOR_TYPE_AND_SCHEME``.

Raises:
KeyError, TypeError: Invalid arguments.
Expand Down
6 changes: 6 additions & 0 deletions securesystemslib/signer/_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ class Signature:
Provides utility methods to easily create an object from a dictionary
and return the dictionary representation of the object.

Args:
keyid: HEX string used as a unique identifier of the key.
sig: HEX string representing the signature.
unrecognized_fields: Dictionary of all attributes that are not managed
by securesystemslib.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeating Args and Attributes seems silly. I'd prefer to only use "Args" here and say "All parameters named below are not just constructor arguments but also instance attributes." as we do in the Key class.

The problem is we use sig as arg name and signature as attribute name. I think we should consolidate them now. Changing the (positional) arg name seems less disrupting.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, consolidate now sounds good: No-one outside of securesystemslib should should be calling Signature() so the fallout should be minimal.

This doesn't have to be in this PR though


Attributes:
keyid: HEX string used as a unique identifier of the key.
signature: HEX string representing the signature.
Expand Down
26 changes: 16 additions & 10 deletions securesystemslib/signer/_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
# NOTE Signer dispatch table is defined here so it's usable by Signer,
# but is populated in __init__.py (and can be appended by users).
SIGNER_FOR_URI_SCHEME: Dict[str, Type] = {}
"""Signer dispatch table for ``Signer.from_priv_key()``

See ``securesystemslib.signer.SIGNER_FOR_URI_SCHEME`` for default URI schemes,
and how to register custom implementations.
"""

# SecretsHandler is a function the calling code can provide to Signer:
# SecretsHandler will be called if Signer needs additional secrets.
Expand All @@ -24,23 +28,25 @@
class Signer(metaclass=ABCMeta):
"""Signer interface that supports multiple signing implementations.

Usage example:
Usage example::

signer = Signer.from_priv_key_uri("envvar:MYPRIVKEY", pub_key)
sig = signer.sign(b"data")

Note that signer implementations may raise errors (during both
Signer.from_priv_key_uri() and Signer.sign()) that are not documented here:
examples could include network errors or file read errors. Applications
should use generic try-except here if unexpected raises are not an option.
``Signer.from_priv_key_uri()`` and ``Signer.sign()``) that are not
documented here: examples could include network errors or file read errors.
Applications should use generic try-except here if unexpected raises are
not an option.

See SIGNER_FOR_URI_SCHEME for supported private key URI schemes. The
See ``SIGNER_FOR_URI_SCHEME`` for supported private key URI schemes. The
currently supported default schemes are:
* envvar: see SSlibSigner for details
* file: see SSlibSigner for details

* envvar: see ``SSlibSigner`` for details
* file: see ``SSlibSigner`` for details

Interactive applications may also define a secrets handler that allows
asking for user secrets if they are needed:
asking for user secrets if they are needed::

from getpass import getpass

Expand All @@ -55,7 +61,7 @@ def sec_handler(secret_name:str) -> str:
uri2 = "file:keys/myenckey?encrypted=true"
signer2 = Signer.from_priv_key_uri(uri2, pub_key2, sec_handler)

Applications can provide their own Signer and Key implementations:
Applications can provide their own Signer and Key implementations::

from securesystemslib.signer import Signer, SIGNER_FOR_URI_SCHEME
from mylib import MySigner
Expand Down Expand Up @@ -90,7 +96,7 @@ def from_priv_key_uri(
"""Factory constructor for a given private key URI

Returns a specific Signer instance based on the private key URI and the
supported uri schemes listed in SIGNER_FOR_URI_SCHEME.
supported uri schemes listed in ``SIGNER_FOR_URI_SCHEME``.

Args:
priv_key_uri: URI that identifies the private key
Expand Down