Add an account deactivate utility script. (#4254)

* Add an account deactivate utility script.

This is handy if you created an account with a tool other than Certbot, and want
to deactivate the account.

* Move deactivate.py to tools.

* Add test for ConflictError.

* Fix lint error.

* Document how to set server.
This commit is contained in:
Jacob Hoffman-Andrews 2017-05-17 14:24:59 -07:00 committed by ohemorange
parent 686f5d6c81
commit 10bac107ee
4 changed files with 69 additions and 0 deletions

View file

@ -564,6 +564,9 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
except ValueError:
jobj = None
if response.status_code == 409:
raise errors.ConflictError(response.headers.get('Location'))
if not response.ok:
if jobj is not None:
if response_ct != cls.JSON_ERROR_CONTENT_TYPE:

View file

@ -513,6 +513,12 @@ class ClientNetworkTest(unittest.TestCase):
self.assertEqual(
self.response, self.net._check_response(self.response))
def test_check_response_conflict(self):
self.response.ok = False
self.response.status_code = 409
# pylint: disable=protected-access
self.assertRaises(errors.ConflictError, self.net._check_response, self.response)
def test_check_response_jobj(self):
self.response.json.return_value = {}
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:

View file

@ -82,3 +82,14 @@ class PollError(ClientError):
def __repr__(self):
return '{0}(exhausted={1!r}, updated={2!r})'.format(
self.__class__.__name__, self.exhausted, self.updated)
class ConflictError(ClientError):
"""Error for when the server returns a 409 (Conflict) HTTP status.
In the version of ACME implemented by Boulder, this is used to find an
account if you only have the private key, but don't know the account URL.
"""
def __init__(self, location):
self.location = location
super(ConflictError, self).__init__()

49
tools/deactivate.py Normal file
View file

@ -0,0 +1,49 @@
"""
Given an ACME account key as input, deactivate the account.
This can be useful if you created an account with a non-Certbot client and now
want to deactivate it.
Private key should be in PKCS#8 PEM form.
To provide the URL for the ACME server you want to use, set it in the $DIRECTORY
environment variable, e.g.:
DIRECTORY=https://acme-staging.api.letsencrypt.org/directory python \
deactivate.py private_key.pem
"""
import os
import sys
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from acme import client as acme_client
from acme import errors as acme_errors
from acme import jose
from acme import messages
DIRECTORY = os.getenv('DIRECTORY', 'http://localhost:4000/directory')
if len(sys.argv) != 2:
print("Usage: python deactivate.py private_key.pem")
sys.exit(1)
data = open(sys.argv[1], "r").read()
key = jose.JWKRSA(key=serialization.load_pem_private_key(
data, None, default_backend()))
net = acme_client.ClientNetwork(key, verify_ssl=False,
user_agent="acme account deactivator")
client = acme_client.Client(DIRECTORY, key=key, net=net)
try:
# We expect this to fail and give us a Conflict response with a Location
# header pointing at the account's URL.
client.register()
except acme_errors.ConflictError as e:
location = e.location
if location is None:
raise "Key was not previously registered (but now is)."
client.deactivate_registration(messages.RegistrationResource(uri=location))