Skip to content

Commit

Permalink
Output success message for revoke command (#3823)
Browse files Browse the repository at this point in the history
* Output status for `revoke` operation. Fixes #2819.

    - Added method to `certbot.display.ops` to output confirmation of `revoke`.

    - Wrapped call to `acme.client.Client.revoke` in a try to statement to
      handle possible error.

    - Added test for `main.revoke`.

* Added test for failure of certificate revocation.

Moved creation of mocks into RevokeTest setup function.

Stopped mocks in RevokeTest teardown function.

* Fixed lint errors.

* Do not call `unittest.TestCase.assertRaises` as a context manager (to work with py26).

* Fixed spelling error in successful revocation notification.

Added test for the notification.
  • Loading branch information
dashaxiong authored and pde committed Nov 29, 2016
1 parent 7951ba7 commit df5f088
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 1 deletion.
13 changes: 13 additions & 0 deletions certbot/display/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,19 @@ def success_renewal(domains):
os.linesep.join(_gen_ssl_lab_urls(domains))),
pause=False)

def success_revocation(cert_path):
"""Display a box confirming a certificate has been revoked.

:param list cert_path: path to certificate which was revoked.

"""
z_util(interfaces.IDisplay).notification(
"Congratulations! You have successfully revoked the certificate "
"that was located at {0}{1}{1}".format(
cert_path,
os.linesep),
pause=False)


def _gen_ssl_lab_urls(domains):
"""Returns a list of urls.
Expand Down
8 changes: 7 additions & 1 deletion certbot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from acme import jose
from acme import messages
from acme import errors as acme_errors

import certbot

Expand Down Expand Up @@ -503,7 +504,12 @@ def revoke(config, unused_plugins): # TODO: coop with renewal config
key = acc.key
acme = client.acme_from_config_key(config, key)
cert = crypto_util.pyopenssl_load_certificate(config.cert_path[1])[0]
acme.revoke(jose.ComparableX509(cert))
try:
acme.revoke(jose.ComparableX509(cert))
except acme_errors.ClientError as e:
return e.message

display_ops.success_revocation(config.cert_path[0])


def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals
Expand Down
15 changes: 15 additions & 0 deletions certbot/tests/display/ops_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,21 @@ def test_success_renewal(self, mock_util):
for name in names:
self.assertTrue(name in arg)

class SuccessRevocationTest(unittest.TestCase):
# pylint: disable=too-few-public-methods
"""Test the success revocation message."""
@classmethod
def _call(cls, path):
from certbot.display.ops import success_revocation
success_revocation(path)

@mock.patch("certbot.display.ops.z_util")
def test_success_revocation(self, mock_util):
mock_util().notification.return_value = None
path = "/path/to/cert.pem"
self._call(path)
mock_util().notification.assert_called_once()
self.assertTrue(path in mock_util().notification.call_args[0][0])

if __name__ == "__main__":
unittest.main() # pragma: no cover
62 changes: 62 additions & 0 deletions certbot/tests/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import shutil
import tempfile
import unittest
import datetime
import pytz

import mock

Expand All @@ -13,6 +15,11 @@
from certbot import errors
from certbot.plugins import disco as plugins_disco

from certbot.tests import test_util
from acme import jose

CERT_PATH = test_util.vector_path('cert.pem')
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key_2.pem"))

class MainTest(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -110,6 +117,61 @@ def _assert_no_pause(self, message, pause=True):
# pylint: disable=unused-argument
self.assertFalse(pause)

class RevokeTest(unittest.TestCase):
"""Tests for certbot.main.revoke."""

def setUp(self):
self.tempdir_path = tempfile.mkdtemp()
shutil.copy(CERT_PATH, self.tempdir_path)
self.tmp_cert_path = os.path.abspath(os.path.join(self.tempdir_path,
'cert.pem'))

self.patches = [
mock.patch('acme.client.Client'),
mock.patch('certbot.client.Client'),
mock.patch('certbot.main._determine_account'),
mock.patch('certbot.main.display_ops.success_revocation')
]
self.mock_acme_client = self.patches[0].start()
self.patches[1].start()
self.mock_determine_account = self.patches[2].start()
self.mock_success_revoke = self.patches[3].start()

from certbot.account import Account

self.regr = mock.MagicMock()
self.meta = Account.Meta(
creation_host="test.certbot.org",
creation_dt=datetime.datetime(
2015, 7, 4, 14, 4, 10, tzinfo=pytz.UTC))
self.acc = Account(self.regr, KEY, self.meta)

self.mock_determine_account.return_value = (self.acc, None)


def tearDown(self):
shutil.rmtree(self.tempdir_path)
for patch in self.patches:
patch.stop()

def _call(self):
args = 'revoke --cert-path={0}'.format(self.tmp_cert_path).split()
plugins = plugins_disco.PluginsRegistry.find_all()
config = configuration.NamespaceConfig(
cli.prepare_and_parse_args(plugins, args))

from certbot.main import revoke
revoke(config, plugins)

def test_revocation_success(self):
self._call()
self.mock_success_revoke.assert_called_once_with(self.tmp_cert_path)

def test_revocation_error(self):
from acme import errors as acme_errors
self.mock_acme_client.side_effect = acme_errors.ClientError()
self.assertRaises(acme_errors.ClientError, self._call)
self.mock_success_revoke.assert_not_called()

class SetupLogFileHandlerTest(unittest.TestCase):
"""Tests for certbot.main.setup_log_file_handler."""
Expand Down

0 comments on commit df5f088

Please sign in to comment.