mirror of
https://github.com/certbot/certbot.git
synced 2026-08-02 03:22:14 +02:00
Refactor Lexicon-based DNS plugins (#9746)
* Refactor Lexicon-based DNS plugins and upgrade minimal version of Lexicon * Relax filterwarning to comply with envs where boto3 is not installed * Update pinned dependencies * Use our previous method to deprecate part of modules * Safe import internally * Add changelog Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
This commit is contained in:
co-authored by
Brad Warren
parent
694c758db7
commit
732a3ac962
@@ -2,23 +2,19 @@
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import cast
|
||||
from typing import Optional
|
||||
|
||||
from lexicon.providers import dnsmadeeasy
|
||||
from requests import HTTPError
|
||||
|
||||
from certbot import errors
|
||||
from certbot.plugins import dns_common
|
||||
from certbot.plugins import dns_common_lexicon
|
||||
from certbot.plugins.dns_common import CredentialsConfiguration
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ACCOUNT_URL = 'https://cp.dnsmadeeasy.com/account/info'
|
||||
|
||||
|
||||
class Authenticator(dns_common.DNSAuthenticator):
|
||||
class Authenticator(dns_common_lexicon.LexiconDNSAuthenticator):
|
||||
"""DNS Authenticator for DNS Made Easy
|
||||
|
||||
This Authenticator uses the DNS Made Easy API to fulfill a dns-01 challenge.
|
||||
@@ -26,11 +22,17 @@ class Authenticator(dns_common.DNSAuthenticator):
|
||||
|
||||
description = ('Obtain certificates using a DNS TXT record (if you are using DNS Made Easy for '
|
||||
'DNS).')
|
||||
ttl = 60
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
self.credentials: Optional[CredentialsConfiguration] = None
|
||||
self._add_provider_option('api-key',
|
||||
'API key for DNS Made Easy account, '
|
||||
f'obtained from {ACCOUNT_URL}',
|
||||
'auth_username')
|
||||
self._add_provider_option('secret-key',
|
||||
'Secret key for DNS Made Easy account, '
|
||||
f'obtained from {ACCOUNT_URL}',
|
||||
'auth_token')
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, add: Callable[..., None],
|
||||
@@ -42,48 +44,9 @@ class Authenticator(dns_common.DNSAuthenticator):
|
||||
return 'This plugin configures a DNS TXT record to respond to a dns-01 challenge using ' + \
|
||||
'the DNS Made Easy API.'
|
||||
|
||||
def _setup_credentials(self) -> None:
|
||||
self.credentials = self._configure_credentials(
|
||||
'credentials',
|
||||
'DNS Made Easy credentials INI file',
|
||||
{
|
||||
'api-key': 'API key for DNS Made Easy account, obtained from {0}'
|
||||
.format(ACCOUNT_URL),
|
||||
'secret-key': 'Secret key for DNS Made Easy account, obtained from {0}'
|
||||
.format(ACCOUNT_URL)
|
||||
}
|
||||
)
|
||||
|
||||
def _perform(self, domain: str, validation_name: str, validation: str) -> None:
|
||||
self._get_dnsmadeeasy_client().add_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _cleanup(self, domain: str, validation_name: str, validation: str) -> None:
|
||||
self._get_dnsmadeeasy_client().del_txt_record(domain, validation_name, validation)
|
||||
|
||||
def _get_dnsmadeeasy_client(self) -> "_DNSMadeEasyLexiconClient":
|
||||
if not self.credentials: # pragma: no cover
|
||||
raise errors.Error("Plugin has not been prepared.")
|
||||
return _DNSMadeEasyLexiconClient(cast(str, self.credentials.conf('api-key')),
|
||||
cast(str, self.credentials.conf('secret-key')),
|
||||
self.ttl)
|
||||
|
||||
|
||||
class _DNSMadeEasyLexiconClient(dns_common_lexicon.LexiconClient):
|
||||
"""
|
||||
Encapsulates all communication with the DNS Made Easy via Lexicon.
|
||||
"""
|
||||
|
||||
def __init__(self, api_key: str, secret_key: str, ttl: int) -> None:
|
||||
super().__init__()
|
||||
|
||||
config = dns_common_lexicon.build_lexicon_config('dnsmadeeasy', {
|
||||
'ttl': ttl,
|
||||
}, {
|
||||
'auth_username': api_key,
|
||||
'auth_token': secret_key,
|
||||
})
|
||||
|
||||
self.provider = dnsmadeeasy.Provider(config)
|
||||
@property
|
||||
def _provider_name(self) -> str:
|
||||
return 'dnsmadeeasy'
|
||||
|
||||
def _handle_http_error(self, e: HTTPError, domain_name: str) -> Optional[errors.PluginError]:
|
||||
if domain_name in str(e) and str(e).startswith('404 Client Error: Not Found for url:'):
|
||||
|
||||
+4
-20
@@ -1,7 +1,6 @@
|
||||
"""Tests for certbot_dns_dnsmadeeasy._internal.dns_dnsmadeeasy."""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
@@ -18,7 +17,10 @@ SECRET_KEY = 'bar'
|
||||
|
||||
|
||||
class AuthenticatorTest(test_util.TempDirTestCase,
|
||||
dns_test_common_lexicon.BaseLexiconAuthenticatorTest):
|
||||
dns_test_common_lexicon.BaseLexiconDNSAuthenticatorTest):
|
||||
|
||||
DOMAIN_NOT_FOUND = HTTPError(f'404 Client Error: Not Found for url: {DOMAIN}.')
|
||||
LOGIN_ERROR = HTTPError(f'403 Client Error: Forbidden for url: {DOMAIN}.')
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
@@ -35,24 +37,6 @@ class AuthenticatorTest(test_util.TempDirTestCase,
|
||||
|
||||
self.auth = Authenticator(self.config, "dnsmadeeasy")
|
||||
|
||||
self.mock_client = mock.MagicMock()
|
||||
# _get_dnsmadeeasy_client | pylint: disable=protected-access
|
||||
self.auth._get_dnsmadeeasy_client = mock.MagicMock(return_value=self.mock_client)
|
||||
|
||||
|
||||
class DNSMadeEasyLexiconClientTest(unittest.TestCase,
|
||||
dns_test_common_lexicon.BaseLexiconClientTest):
|
||||
DOMAIN_NOT_FOUND = HTTPError('404 Client Error: Not Found for url: {0}.'.format(DOMAIN))
|
||||
LOGIN_ERROR = HTTPError('403 Client Error: Forbidden for url: {0}.'.format(DOMAIN))
|
||||
|
||||
def setUp(self):
|
||||
from certbot_dns_dnsmadeeasy._internal.dns_dnsmadeeasy import _DNSMadeEasyLexiconClient
|
||||
|
||||
self.client = _DNSMadeEasyLexiconClient(API_KEY, SECRET_KEY, 0)
|
||||
|
||||
self.provider_mock = mock.MagicMock()
|
||||
self.client.provider = self.provider_mock
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover
|
||||
|
||||
@@ -7,7 +7,7 @@ from setuptools import setup
|
||||
version = '2.7.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.2.1',
|
||||
'dns-lexicon>=3.14.1',
|
||||
'setuptools>=41.6.0',
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user