mirror of
https://github.com/certbot/certbot.git
synced 2026-07-27 16:30:31 +02:00
Merge remote-tracking branch 'github/letsencrypt/master' into network2
This commit is contained in:
@@ -6,3 +6,6 @@ venv/
|
||||
.tox/
|
||||
.coverage
|
||||
m3
|
||||
*~
|
||||
.vagrant
|
||||
*.swp
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<!---
|
||||
|
||||
This file serves as an entry point for GitHub's Contributing
|
||||
Guidelines [1] only.
|
||||
|
||||
GitHub doesn't render rST very well, especially in respect to internal
|
||||
hyperlink targets and cross-references [2]. People also tend to
|
||||
confuse rST and Markdown syntax. Therefore, instead of keeping the
|
||||
contents here (and including from rST documentation under doc/), link
|
||||
to the Sphinx generated docs is provided below.
|
||||
|
||||
|
||||
[1] https://github.com/blog/1184-contributing-guidelines
|
||||
[2] http://docutils.sourceforge.net/docs/user/rst/quickref.html#hyperlink-targets
|
||||
|
||||
-->
|
||||
|
||||
https://letsencrypt.readthedocs.org/en/latest/contributing.html
|
||||
@@ -1,72 +0,0 @@
|
||||
.. _hacking:
|
||||
|
||||
Hacking
|
||||
=======
|
||||
|
||||
In order to start hacking, you will first have to create a development
|
||||
environment:
|
||||
|
||||
::
|
||||
|
||||
./venv/bin/python setup.py dev
|
||||
|
||||
The code base, including your pull requests, **must** have 100% test statement
|
||||
coverage **and** be compliant with the :ref:`coding-style`.
|
||||
|
||||
The following tools are there to help you:
|
||||
|
||||
- ``./venv/bin/tox`` starts a full set of tests. Please make sure you
|
||||
run it before submitting a new pull request.
|
||||
|
||||
- ``./venv/bin/tox -e cover`` checks the test coverage only.
|
||||
|
||||
- ``./venv/bin/tox -e lint`` checks the style of the whole project,
|
||||
while ``./venv/bin/pylint --rcfile=.pylintrc file`` will check a single `file` only.
|
||||
|
||||
|
||||
.. _coding-style:
|
||||
|
||||
Coding style
|
||||
============
|
||||
|
||||
Please:
|
||||
|
||||
1. **Be consistent with the rest of the code**.
|
||||
|
||||
2. Read `PEP 8 - Style Guide for Python Code`_.
|
||||
|
||||
3. Follow the `Google Python Style Guide`_, with the exception that we
|
||||
use `Sphinx-style`_ documentation:
|
||||
|
||||
::
|
||||
|
||||
def foo(arg):
|
||||
"""Short description.
|
||||
|
||||
:param int arg: Some number.
|
||||
|
||||
:returns: Argument
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
return arg
|
||||
|
||||
4. Remember to use ``./venv/bin/pylint``.
|
||||
|
||||
.. _Google Python Style Guide: https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
.. _Sphinx-style: http://sphinx-doc.org/
|
||||
.. _PEP 8 - Style Guide for Python Code: https://www.python.org/dev/peps/pep-0008
|
||||
|
||||
|
||||
Updating the Documentation
|
||||
==========================
|
||||
|
||||
In order to generate the Sphinx documentation, run the following commands.
|
||||
|
||||
::
|
||||
|
||||
cd docs
|
||||
make clean html SPHINXBUILD=../venv/bin/sphinx-build
|
||||
|
||||
|
||||
This should generate documentation in the ``docs/_build/html`` directory.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
include README.rst
|
||||
include CHANGES.rst
|
||||
include CONTRIBUTING.rst
|
||||
include CONTRIBUTING.md
|
||||
include linter_plugin.py
|
||||
include letsencrypt/EULA
|
||||
recursive-include letsencrypt *.json
|
||||
|
||||
@@ -57,6 +57,7 @@ Current Features
|
||||
* web servers supported:
|
||||
|
||||
- apache2.x (tested and working on Ubuntu Linux)
|
||||
- standalone (runs its own webserver to prove you control the domain)
|
||||
|
||||
* the private key is generated locally on your system
|
||||
* can talk to the Let's Encrypt (demo) CA or optionally to other ACME
|
||||
@@ -79,6 +80,8 @@ Documentation: https://letsencrypt.readthedocs.org/
|
||||
|
||||
Software project: https://github.com/letsencrypt/lets-encrypt-preview
|
||||
|
||||
Notes for developers: CONTRIBUTING.rst_
|
||||
|
||||
Main Website: https://letsencrypt.org/
|
||||
|
||||
IRC Channel: #letsencrypt on `Freenode`_
|
||||
@@ -88,3 +91,4 @@ email to client-dev+subscribe@letsencrypt.org)
|
||||
|
||||
.. _Freenode: https://freenode.net
|
||||
.. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev
|
||||
.. _CONTRIBUTING.rst: https://github.com/letsencrypt/lets-encrypt-preview/blob/master/CONTRIBUTING.rst
|
||||
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
# Setup instructions from docs/using.rst
|
||||
$ubuntu_setup_script = <<SETUP_SCRIPT
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python python-setuptools python-virtualenv python-dev gcc swig dialog libaugeas0 libssl-dev libffi-dev ca-certificates
|
||||
|
||||
cd /vagrant
|
||||
if [ ! -d "venv" ]; then
|
||||
virtualenv --no-site-packages -p python2 venv
|
||||
./venv/bin/python setup.py dev
|
||||
fi
|
||||
SETUP_SCRIPT
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.define "ubuntu-trusty", primary: true do |ubuntu_trusty|
|
||||
ubuntu_trusty.vm.box = "ubuntu/trusty64"
|
||||
ubuntu_trusty.vm.provision "shell", inline: $ubuntu_setup_script
|
||||
ubuntu_trusty.vm.provider "virtualbox" do |v|
|
||||
# VM needs more memory to run test suite, got "OSError: [Errno 12]
|
||||
# Cannot allocate memory" when running
|
||||
# letsencrypt.client.tests.display.util_test.NcursesDisplayTest
|
||||
v.memory = 1024
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
+1
-1
@@ -101,7 +101,7 @@ exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
default_role = 'py:obj'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
.. _hacking:
|
||||
|
||||
Hacking
|
||||
=======
|
||||
|
||||
In order to start hacking, you will first have to create a development
|
||||
environment. Start by :doc:`installing dependencies and setting up
|
||||
Let's Encrypt <using>`.
|
||||
|
||||
Now you can install the development packages:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
./venv/bin/python setup.py dev
|
||||
|
||||
The code base, including your pull requests, **must** have 100% test
|
||||
statement coverage **and** be compliant with the :ref:`coding style
|
||||
<coding-style>`.
|
||||
|
||||
The following tools are there to help you:
|
||||
|
||||
- ``./venv/bin/tox`` starts a full set of tests. Please make sure you
|
||||
run it before submitting a new pull request.
|
||||
|
||||
- ``./venv/bin/tox -e cover`` checks the test coverage only.
|
||||
|
||||
- ``./venv/bin/tox -e lint`` checks the style of the whole project,
|
||||
while ``./venv/bin/pylint --rcfile=.pylintrc file`` will check a
|
||||
single ``file`` only.
|
||||
|
||||
.. _installing dependencies and setting up Let's Encrypt:
|
||||
https://letsencrypt.readthedocs.org/en/latest/using.html
|
||||
|
||||
|
||||
Vagrant
|
||||
-------
|
||||
|
||||
If you are a Vagrant user, Let's Encrypt comes with a Vagrantfile that
|
||||
automates setting up a development environment in an Ubuntu 14.04
|
||||
LTS VM. To set it up, simply run ``vagrant up``. The repository is
|
||||
synced to ``/vagrant``, so you can get started with:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
vagrant ssh
|
||||
cd /vagrant
|
||||
./venv/bin/python setup.py install
|
||||
sudo ./venv/bin/letsencrypt
|
||||
|
||||
Support for other Linux distributions coming soon.
|
||||
|
||||
.. note::
|
||||
Unfortunately, Python distutils and, by extension, setup.py and
|
||||
tox, use hard linking quite extensively. Hard linking is not
|
||||
supported by the default sync filesystem in Vagrant. As a result,
|
||||
all actions with these commands are *significantly slower* in
|
||||
Vagrant. One potential fix is to `use NFS`_ (`related issue`_).
|
||||
|
||||
.. _use NFS: http://docs.vagrantup.com/v2/synced-folders/nfs.html
|
||||
.. _related issue: https://github.com/ClusterHQ/flocker/issues/516
|
||||
|
||||
|
||||
Code components and layout
|
||||
==========================
|
||||
|
||||
letsencrypt/acme
|
||||
contains all protocol specific code
|
||||
letsencrypt/client
|
||||
all client code
|
||||
letsencrypt/scripts
|
||||
just the starting point of the code, main.py
|
||||
|
||||
|
||||
Plugin-architecture
|
||||
-------------------
|
||||
|
||||
Let's Encrypt has a plugin architecture to facilitate support for
|
||||
different webservers, other TLS servers, and operating systems.
|
||||
|
||||
The most common kind of plugin is a "Configurator", which is likely to
|
||||
implement the `~letsencrypt.client.interfaces.IAuthenticator` and
|
||||
`~letsencrypt.client.interfaces.IInstaller` interfaces (though some
|
||||
Configurators may implement just one of those).
|
||||
|
||||
There are also `~letsencrypt.client.interfaces.IDisplay` plugins,
|
||||
which implement bindings to alternative UI libraries.
|
||||
|
||||
|
||||
Authenticators
|
||||
--------------
|
||||
|
||||
Authenticators are plugins designed to solve challenges received from
|
||||
the ACME server. From the protocol, there are essentially two
|
||||
different types of challenges. Challenges that must be solved by
|
||||
individual plugins in order to satisfy domain validation (subclasses
|
||||
of `~.DVChallenge`, i.e. `~.challenges.DVSNI`,
|
||||
`~.challenges.SimpleHTTPS`, `~.challenges.DNS`) and client specific
|
||||
challenges (subclasses of `~.ClientChallenge`,
|
||||
i.e. `~.challenges.RecoveryToken`, `~.challenges.RecoveryContact`,
|
||||
`~.challenges.ProofOfPossession`). Client specific challenges are
|
||||
always handled by the `~.ClientAuthenticator`. Right now we have two
|
||||
DV Authenticators, `~.ApacheConfigurator` and the
|
||||
`~.StandaloneAuthenticator`. The Standalone and Apache authenticators
|
||||
only solve the `~.challenges.DVSNI` challenge currently. (You can set
|
||||
which challenges your authenticator can handle through the
|
||||
:meth:`~.IAuthenticator.get_chall_pref`.
|
||||
|
||||
(FYI: We also have a partial implementation for a `~.DNSAuthenticator`
|
||||
in a separate branch).
|
||||
|
||||
|
||||
Installer
|
||||
---------
|
||||
|
||||
Installers classes exist to actually setup the certificate and be able
|
||||
to enhance the configuration. (Turn on HSTS, redirect to HTTPS,
|
||||
etc). You can indicate your abilities through the
|
||||
:meth:`~.IInstaller.supported_enhancements` call. We currently only
|
||||
have one Installer written (still developing), `~.ApacheConfigurator`.
|
||||
|
||||
Installers and Authenticators will oftentimes be the same
|
||||
class/object. Installers and Authenticators are kept separate because
|
||||
it should be possible to use the `~.StandaloneAuthenticator` (it sets
|
||||
up its own Python server to perform challenges) with a program that
|
||||
cannot solve challenges itself. (I am imagining MTA installers).
|
||||
|
||||
|
||||
Display
|
||||
~~~~~~~
|
||||
|
||||
We currently offer a pythondialog and "text" mode for displays. I have
|
||||
rewritten the interface which should be merged within the next day
|
||||
(the rewrite is in the revoker branch of the repo and should be merged
|
||||
within the next day). Display plugins implement
|
||||
`~letsencrypt.client.interfaces.IDisplay` interface.
|
||||
|
||||
|
||||
Augeas
|
||||
------
|
||||
|
||||
Some plugins, especially those designed to reconfigure UNIX servers,
|
||||
can take inherit from `~.AugeasConfigurator` class in order to more
|
||||
efficiently handle common operations on UNIX server configuration
|
||||
files.
|
||||
|
||||
|
||||
.. _coding-style:
|
||||
|
||||
Coding style
|
||||
============
|
||||
|
||||
Please:
|
||||
|
||||
1. **Be consistent with the rest of the code**.
|
||||
|
||||
2. Read `PEP 8 - Style Guide for Python Code`_.
|
||||
|
||||
3. Follow the `Google Python Style Guide`_, with the exception that we
|
||||
use `Sphinx-style`_ documentation::
|
||||
|
||||
def foo(arg):
|
||||
"""Short description.
|
||||
|
||||
:param int arg: Some number.
|
||||
|
||||
:returns: Argument
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
return arg
|
||||
|
||||
4. Remember to use ``./venv/bin/pylint``.
|
||||
|
||||
.. _Google Python Style Guide:
|
||||
https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
.. _Sphinx-style: http://sphinx-doc.org/
|
||||
.. _PEP 8 - Style Guide for Python Code:
|
||||
https://www.python.org/dev/peps/pep-0008
|
||||
|
||||
|
||||
Updating the documentation
|
||||
==========================
|
||||
|
||||
In order to generate the Sphinx documentation, run the following
|
||||
commands:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cd docs
|
||||
make clean html SPHINXBUILD=../venv/bin/sphinx-build
|
||||
|
||||
This should generate documentation in the ``docs/_build/html``
|
||||
directory.
|
||||
+1
-1
@@ -6,7 +6,7 @@ Welcome to the Let's Encrypt client documentation!
|
||||
|
||||
intro
|
||||
using
|
||||
project
|
||||
contributing
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
================================
|
||||
The Let's Encrypt Client Project
|
||||
================================
|
||||
|
||||
.. include:: ../CONTRIBUTING.rst
|
||||
+11
-11
@@ -21,30 +21,30 @@ In general:
|
||||
Ubuntu
|
||||
------
|
||||
|
||||
::
|
||||
.. code-block:: shell
|
||||
|
||||
sudo apt-get install python python-setuptools python-virtualenv python-dev \
|
||||
gcc swig dialog libaugeas0 libssl-dev libffi-dev \
|
||||
ca-certificates
|
||||
sudo apt-get install python python-setuptools python-virtualenv python-dev \
|
||||
gcc swig dialog libaugeas0 libssl-dev libffi-dev \
|
||||
ca-certificates
|
||||
|
||||
.. Please keep the above command in sync with .travis.yml (before_install)
|
||||
|
||||
Mac OSX
|
||||
-------
|
||||
|
||||
::
|
||||
.. code-block:: shell
|
||||
|
||||
sudo brew install augeas swig
|
||||
sudo brew install augeas swig
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
::
|
||||
.. code-block:: shell
|
||||
|
||||
virtualenv --no-site-packages -p python2 venv
|
||||
./venv/bin/python setup.py install
|
||||
sudo ./venv/bin/letsencrypt
|
||||
virtualenv --no-site-packages -p python2 venv
|
||||
./venv/bin/python setup.py install
|
||||
sudo ./venv/bin/letsencrypt
|
||||
|
||||
|
||||
Usage
|
||||
@@ -52,7 +52,7 @@ Usage
|
||||
|
||||
The letsencrypt commandline tool has a builtin help:
|
||||
|
||||
::
|
||||
.. code-block:: shell
|
||||
|
||||
./venv/bin/letsencrypt --help
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
letsencrypt/scripts/main.py
|
||||
@@ -30,13 +30,7 @@ class Field(object):
|
||||
:class:`~letsencrypt.acme.jose.errors.SerializationError`
|
||||
(:class:`~letsencrypt.acme.jose.errors.DeserializationError`).
|
||||
|
||||
For greater flexibility, ``encoder2`` and ``decoder2`` accept two
|
||||
parameters: the whole object ("``self``" in case of encoding, and
|
||||
JSON serialized object ``jobj`` in case of decoding) and the value
|
||||
to be encoded/decoded.
|
||||
|
||||
Note, that ``decoder`` and ``decoder2`` should perform partial
|
||||
serialization only.
|
||||
Note, that ``decoder`` should perform partial serialization only.
|
||||
|
||||
:ivar str json_name: Name of the field when encoded to JSON.
|
||||
:ivar default: Default value (used when not present in JSON object).
|
||||
@@ -48,18 +42,15 @@ class Field(object):
|
||||
deserializing.
|
||||
|
||||
"""
|
||||
__slots__ = ('json_name', 'default', 'omitempty',
|
||||
'fdec', 'fenc', 'fdec2', 'fenc2')
|
||||
__slots__ = ('json_name', 'default', 'omitempty', 'fdec', 'fenc')
|
||||
|
||||
def __init__(self, json_name, default=None, omitempty=False,
|
||||
decoder=None, encoder=None, decoder2=None, encoder2=None):
|
||||
decoder=None, encoder=None):
|
||||
# pylint: disable=too-many-arguments
|
||||
self.json_name = json_name
|
||||
self.default = default
|
||||
self.omitempty = omitempty
|
||||
|
||||
self.fdec2 = decoder2
|
||||
self.fenc2 = encoder2
|
||||
self.fdec = self.default_decoder if decoder is None else decoder
|
||||
self.fenc = self.default_encoder if encoder is None else encoder
|
||||
|
||||
@@ -80,37 +71,24 @@ class Field(object):
|
||||
def _update_params(self, **kwargs):
|
||||
current = dict(json_name=self.json_name, default=self.default,
|
||||
omitempty=self.omitempty,
|
||||
decoder=self.fdec, encoder=self.fenc,
|
||||
decoder2=self.fdec2, encoder2=self.fenc2)
|
||||
decoder=self.fdec, encoder=self.fenc)
|
||||
current.update(kwargs)
|
||||
return type(self)(**current) # pylint: disable=star-args
|
||||
|
||||
def decoder(self, fdec):
|
||||
"""Descriptor to change the decoder on JSON object field."""
|
||||
return self._update_params(decoder=fdec, decoder2=None)
|
||||
return self._update_params(decoder=fdec)
|
||||
|
||||
def encoder(self, fenc):
|
||||
"""Descriptor to change the encoder on JSON object field."""
|
||||
return self._update_params(encoder=fenc, encoder2=None)
|
||||
return self._update_params(encoder=fenc)
|
||||
|
||||
def decoder2(self, fdec2):
|
||||
"""Descriptor to change the decoder2 on JSON object field."""
|
||||
return self._update_params(decoder2=fdec2, decoder=None)
|
||||
|
||||
def encoder2(self, fenc2):
|
||||
"""Descriptor to change the encoder2 on JSON object field."""
|
||||
return self._update_params(encoder2=fenc2, encoder=None)
|
||||
|
||||
def decode(self, value, jobj=None):
|
||||
def decode(self, value):
|
||||
"""Decode a value, optionally with context JSON object."""
|
||||
if self.fdec2 is not None:
|
||||
return self.fdec2(jobj, value)
|
||||
return self.fdec(value)
|
||||
|
||||
def encode(self, value, obj=None):
|
||||
def encode(self, value):
|
||||
"""Encode a value, optionally with context JSON object."""
|
||||
if self.fenc2 is not None:
|
||||
return self.fenc2(obj, value)
|
||||
return self.fenc(value)
|
||||
|
||||
@classmethod
|
||||
@@ -241,7 +219,7 @@ class JSONObjectWithFields(util.ImmutableMap, interfaces.JSONDeSerializable):
|
||||
logging.debug('Ommiting empty field "%s" (%s)', slot, value)
|
||||
else:
|
||||
try:
|
||||
jobj[field.json_name] = field.encode(value, self)
|
||||
jobj[field.json_name] = field.encode(value)
|
||||
except errors.SerializationError as error:
|
||||
raise errors.SerializationError(
|
||||
'Could not encode {0} ({1}): {2}'.format(
|
||||
@@ -274,7 +252,7 @@ class JSONObjectWithFields(util.ImmutableMap, interfaces.JSONDeSerializable):
|
||||
else:
|
||||
value = jobj[field.json_name]
|
||||
try:
|
||||
fields[slot] = field.decode(value, jobj)
|
||||
fields[slot] = field.decode(value)
|
||||
except errors.DeserializationError as error:
|
||||
raise errors.DeserializationError(
|
||||
'Could not decode {0!r} ({1!r}): {2}'.format(
|
||||
|
||||
@@ -21,8 +21,6 @@ class FieldTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.acme.jose.json_util.Field."""
|
||||
|
||||
def test_descriptors(self):
|
||||
mock_jobj = mock.MagicMock()
|
||||
mock_obj = mock.MagicMock()
|
||||
mock_value = mock.MagicMock()
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
@@ -33,36 +31,15 @@ class FieldTest(unittest.TestCase):
|
||||
def encoder(unused_value):
|
||||
return 'e'
|
||||
|
||||
def decoder2(jobj, unused_value):
|
||||
self.assertTrue(jobj is mock_jobj)
|
||||
return 'd2'
|
||||
|
||||
def encoder2(obj, unused_value):
|
||||
self.assertTrue(obj is mock_obj)
|
||||
return 'e2'
|
||||
|
||||
from letsencrypt.acme.jose.json_util import Field
|
||||
field = Field('foo', decoder=decoder, encoder=encoder,
|
||||
decoder2=decoder2, encoder2=encoder2)
|
||||
|
||||
self.assertEqual('e2', field.encode(mock_value, mock_obj))
|
||||
self.assertEqual('d2', field.decode(mock_value, mock_jobj))
|
||||
field = Field('foo')
|
||||
|
||||
field = field.encoder(encoder)
|
||||
self.assertEqual('e', field.encode(mock_value, mock_obj))
|
||||
self.assertEqual('d2', field.decode(mock_value, mock_jobj))
|
||||
self.assertEqual('e', field.encode(mock_value))
|
||||
|
||||
field = field.decoder(decoder)
|
||||
self.assertEqual('e', field.encode(mock_value, mock_obj))
|
||||
self.assertEqual('d', field.decode(mock_value, mock_jobj))
|
||||
|
||||
field = field.encoder2(encoder2)
|
||||
self.assertEqual('e2', field.encode(mock_value, mock_obj))
|
||||
self.assertEqual('d', field.decode(mock_value, mock_jobj))
|
||||
|
||||
field = field.decoder2(decoder2)
|
||||
self.assertEqual('e2', field.encode(mock_value, mock_obj))
|
||||
self.assertEqual('d2', field.decode(mock_value, mock_jobj))
|
||||
self.assertEqual('e', field.encode(mock_value))
|
||||
self.assertEqual('d', field.decode(mock_value))
|
||||
|
||||
def test_default_encoder_is_partial(self):
|
||||
class MockField(interfaces.JSONDeSerializable):
|
||||
|
||||
@@ -71,7 +71,12 @@ class _JWAHS(JWASignature):
|
||||
return HMAC.new(key, msg, self.digestmod).digest()
|
||||
|
||||
def verify(self, key, msg, sig):
|
||||
# TODO: use constant compare to mitigate timing attack?
|
||||
"""Verify the signature.
|
||||
|
||||
.. warning::
|
||||
Does not protect against timing attack (no constant compare).
|
||||
|
||||
"""
|
||||
return self.sign(key, msg) == sig
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import argparse
|
||||
import base64
|
||||
import sys
|
||||
|
||||
import M2Crypto.X509
|
||||
import M2Crypto
|
||||
|
||||
from letsencrypt.acme.jose import b64
|
||||
from letsencrypt.acme.jose import errors
|
||||
|
||||
@@ -5,7 +5,7 @@ import pkg_resources
|
||||
import unittest
|
||||
|
||||
import Crypto.PublicKey.RSA
|
||||
import M2Crypto.X509
|
||||
import M2Crypto
|
||||
import mock
|
||||
|
||||
from letsencrypt.acme.jose import b64
|
||||
@@ -205,29 +205,33 @@ class CLITest(unittest.TestCase):
|
||||
|
||||
def test_unverified(self):
|
||||
from letsencrypt.acme.jose.jws import CLI
|
||||
with mock.patch('sys.stdin') as sin, mock.patch('sys.stdout'):
|
||||
with mock.patch('sys.stdin') as sin:
|
||||
sin.read.return_value = '{"payload": "foo", "signature": "xxx"}'
|
||||
self.assertEqual(-1, CLI.run(['verify']))
|
||||
with mock.patch('sys.stdout'):
|
||||
self.assertEqual(-1, CLI.run(['verify']))
|
||||
|
||||
def test_json(self):
|
||||
from letsencrypt.acme.jose.jws import CLI
|
||||
|
||||
with mock.patch('sys.stdin') as sin, mock.patch('sys.stdout') as sout:
|
||||
with mock.patch('sys.stdin') as sin:
|
||||
sin.read.return_value = 'foo'
|
||||
CLI.run(['sign', '-k', self.key_path, '-a', 'RS256',
|
||||
'-p', 'jwk'])
|
||||
sin.read.return_value = sout.write.mock_calls[0][1][0]
|
||||
self.assertEqual(0, CLI.run(['verify']))
|
||||
with mock.patch('sys.stdout') as sout:
|
||||
CLI.run(['sign', '-k', self.key_path, '-a', 'RS256',
|
||||
'-p', 'jwk'])
|
||||
sin.read.return_value = sout.write.mock_calls[0][1][0]
|
||||
self.assertEqual(0, CLI.run(['verify']))
|
||||
|
||||
def test_compact(self):
|
||||
from letsencrypt.acme.jose.jws import CLI
|
||||
|
||||
with mock.patch('sys.stdin') as sin, mock.patch('sys.stdout') as sout:
|
||||
with mock.patch('sys.stdin') as sin:
|
||||
sin.read.return_value = 'foo'
|
||||
CLI.run(['--compact', 'sign', '-k', self.key_path])
|
||||
sin.read.return_value = sout.write.mock_calls[0][1][0]
|
||||
self.assertEqual(0, CLI.run([
|
||||
'--compact', 'verify', '--kty', 'RSA', '-k', self.key_path]))
|
||||
with mock.patch('sys.stdout') as sout:
|
||||
CLI.run(['--compact', 'sign', '-k', self.key_path])
|
||||
sin.read.return_value = sout.write.mock_calls[0][1][0]
|
||||
self.assertEqual(0, CLI.run([
|
||||
'--compact', 'verify', '--kty', 'RSA',
|
||||
'-k', self.key_path]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -548,8 +548,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
def _enable_redirect(self, ssl_vhost, unused_options):
|
||||
"""Redirect all equivalent HTTP traffic to ssl_vhost.
|
||||
|
||||
.. todo:: This enhancement should be rewritten and will unfortunately
|
||||
require lots of debugging by hand.
|
||||
.. todo:: This enhancement should be rewritten and will
|
||||
unfortunately require lots of debugging by hand.
|
||||
|
||||
Adds Redirect directive to the port 80 equivalent of ssl_vhost
|
||||
First the function attempts to find the vhost with equivalent
|
||||
ip addresses that serves on non-ssl ports
|
||||
|
||||
@@ -50,7 +50,7 @@ class ApacheDvsni(object):
|
||||
def perform(self):
|
||||
"""Peform a DVSNI challenge."""
|
||||
if not self.achalls:
|
||||
return None
|
||||
return []
|
||||
# Save any changes to the configuration as a precaution
|
||||
# About to make temporary changes to the config
|
||||
self.configurator.save()
|
||||
@@ -160,19 +160,19 @@ class ApacheDvsni(object):
|
||||
ips = " ".join(str(i) for i in ip_addrs)
|
||||
document_root = os.path.join(
|
||||
self.configurator.config.config_dir, "dvsni_page/")
|
||||
return ("<VirtualHost " + ips + ">\n"
|
||||
"ServerName " + achall.nonce_domain + "\n"
|
||||
"UseCanonicalName on\n"
|
||||
"SSLStrictSNIVHostCheck on\n"
|
||||
"\n"
|
||||
"LimitRequestBody 1048576\n"
|
||||
"\n"
|
||||
"Include " + self.configurator.parser.loc["ssl_options"] + "\n"
|
||||
"SSLCertificateFile " + self.get_cert_file(achall) + "\n"
|
||||
"SSLCertificateKeyFile " + achall.key.file + "\n"
|
||||
"\n"
|
||||
"DocumentRoot " + document_root + "\n"
|
||||
"</VirtualHost>\n\n")
|
||||
return ("<VirtualHost " + ips + ">{0}"
|
||||
"ServerName " + achall.nonce_domain + "{0}"
|
||||
"UseCanonicalName on{0}"
|
||||
"SSLStrictSNIVHostCheck on{0}"
|
||||
"{0}"
|
||||
"LimitRequestBody 1048576{0}"
|
||||
"{0}"
|
||||
"Include " + self.configurator.parser.loc["ssl_options"] + "{0}"
|
||||
"SSLCertificateFile " + self.get_cert_file(achall) + "{0}"
|
||||
"SSLCertificateKeyFile " + achall.key.file + "{0}"
|
||||
"{0}"
|
||||
"DocumentRoot " + document_root + "{0}"
|
||||
"</VirtualHost>{0}{0}".format(os.linesep))
|
||||
|
||||
def get_cert_file(self, achall):
|
||||
"""Returns standardized name for challenge certificate.
|
||||
|
||||
@@ -203,7 +203,8 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
def _path_satisfied(self, dom):
|
||||
"""Returns whether a path has been completely satisfied."""
|
||||
return all(self.responses[dom][i] is not None for i in self.paths[dom])
|
||||
# Make sure that there are no 'None' or 'False' entries along path.
|
||||
return all(self.responses[dom][i] for i in self.paths[dom])
|
||||
|
||||
def _get_chall_pref(self, domain):
|
||||
"""Return list of challenge preferences.
|
||||
|
||||
@@ -11,6 +11,9 @@ from letsencrypt.acme import messages
|
||||
from letsencrypt.client import errors
|
||||
|
||||
|
||||
# https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
|
||||
requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3()
|
||||
|
||||
logging.getLogger("requests").setLevel(logging.WARNING)
|
||||
|
||||
|
||||
|
||||
Executable → Regular
@@ -481,7 +481,7 @@ class PathSatisfiedTest(unittest.TestCase):
|
||||
self.handler.responses[dom[0]] = [None, "sat", "sat2", None]
|
||||
|
||||
self.handler.paths[dom[1]] = [0]
|
||||
self.handler.responses[dom[1]] = ["sat", None, None, None]
|
||||
self.handler.responses[dom[1]] = ["sat", None, None, False]
|
||||
|
||||
self.handler.paths[dom[2]] = [0]
|
||||
self.handler.responses[dom[2]] = ["sat"]
|
||||
@@ -496,7 +496,7 @@ class PathSatisfiedTest(unittest.TestCase):
|
||||
self.assertTrue(self.handler._path_satisfied(dom[i]))
|
||||
|
||||
def test_not_satisfied(self):
|
||||
dom = ["0", "1", "2"]
|
||||
dom = ["0", "1", "2", "3"]
|
||||
self.handler.paths[dom[0]] = [1, 2]
|
||||
self.handler.responses[dom[0]] = ["sat1", None, "sat2", None]
|
||||
|
||||
@@ -506,6 +506,9 @@ class PathSatisfiedTest(unittest.TestCase):
|
||||
self.handler.paths[dom[2]] = [0]
|
||||
self.handler.responses[dom[2]] = [None]
|
||||
|
||||
self.handler.paths[dom[3]] = [0]
|
||||
self.handler.responses[dom[3]] = [False]
|
||||
|
||||
for i in xrange(3):
|
||||
self.assertFalse(self.handler._path_satisfied(dom[i]))
|
||||
|
||||
|
||||
Executable → Regular
-1
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
"""Parse command line and call the appropriate functions.
|
||||
|
||||
.. todo:: Sanity check all input. Be sure to avoid shell code etc...
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
import codecs
|
||||
import os
|
||||
import re
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
# Workaround for http://bugs.python.org/issue8876, see
|
||||
# http://bugs.python.org/issue8876#msg208792
|
||||
# This can be removed when using Python 2.7.9 or later:
|
||||
# https://hg.python.org/cpython/raw-file/v2.7.9/Misc/NEWS
|
||||
if os.path.abspath(__file__).split(os.path.sep)[1] == 'vagrant':
|
||||
del os.link
|
||||
|
||||
def read_file(filename, encoding='utf8'):
|
||||
"""Read unicode from given file."""
|
||||
@@ -26,7 +31,9 @@ install_requires = [
|
||||
'ConfArgParse',
|
||||
'jsonschema',
|
||||
'mock',
|
||||
'ndg-httpsclient', # urllib3 InsecurePlatformWarning (#304)
|
||||
'psutil>=2.1.0', # net_connections introduced in 2.1.0
|
||||
'pyasn1', # urllib3 InsecurePlatformWarning (#304)
|
||||
'pycrypto',
|
||||
'PyOpenSSL',
|
||||
'python-augeas',
|
||||
@@ -40,7 +47,9 @@ install_requires = [
|
||||
]
|
||||
|
||||
dev_extras = [
|
||||
'pylint>=1.4.0', # upstream #248
|
||||
# Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289
|
||||
'astroid==1.3.5',
|
||||
'pylint==1.4.2', # upstream #248
|
||||
]
|
||||
|
||||
docs_extras = [
|
||||
@@ -85,6 +94,7 @@ setup(
|
||||
packages=[
|
||||
'letsencrypt',
|
||||
'letsencrypt.acme',
|
||||
'letsencrypt.acme.jose',
|
||||
'letsencrypt.client',
|
||||
'letsencrypt.client.apache',
|
||||
'letsencrypt.client.display',
|
||||
|
||||
@@ -19,7 +19,7 @@ setenv =
|
||||
basepython = python2.7
|
||||
commands =
|
||||
pip install -e .[testing]
|
||||
python setup.py nosetests --with-coverage --cover-min-percentage=85
|
||||
python setup.py nosetests --with-coverage --cover-min-percentage=86
|
||||
|
||||
[testenv:lint]
|
||||
# recent versions of pylint do not support Python 2.6 (#97, #187)
|
||||
|
||||
Reference in New Issue
Block a user