This is an automated email from the ASF dual-hosted git repository. clewolff pushed a commit to branch dns-cloudflare-token-auth in repository https://gitbox.apache.org/repos/asf/libcloud.git
commit 39508bff319e3964c6f7b063e63c89371413afe4 Author: Clemens Wolff <[email protected]> AuthorDate: Sun Mar 7 12:46:50 2021 -0500 Enable auth to Cloudflare DNS via API Tokens --- CHANGES.rst | 6 ++++ docs/dns/drivers/cloudflare.rst | 8 ++++- .../dns/cloudflare/instantiate_driver_token.py | 5 +++ libcloud/dns/drivers/cloudflare.py | 36 +++++++++++++++++++--- libcloud/test/dns/test_cloudflare.py | 10 ++++++ 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b0fab73..5455c60 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -41,6 +41,12 @@ Compute (GITHUB-1555) [Rob Juffermans - @robjuffermans] +DNS +~~~ + +- [CloudFlare] Enable authentication via API Tokens. + [Clemens Wolff - @c-w] + Changes in Apache Libcloud 3.3.1 -------------------------------- diff --git a/docs/dns/drivers/cloudflare.rst b/docs/dns/drivers/cloudflare.rst index b55951e..fb814d7 100644 --- a/docs/dns/drivers/cloudflare.rst +++ b/docs/dns/drivers/cloudflare.rst @@ -13,12 +13,18 @@ Instantiating the driver ------------------------ To instantiate the driver you need to pass email address associated with your -account and API key available on the `account page`_ to the driver constructor +account and a Global API key available on the `account page`_ to the driver constructor as shown below. .. literalinclude:: /examples/dns/cloudflare/instantiate_driver.py :language: python +Alternatively, authentication can also be done via an API Token as shown below. +It is recommended that the token at least has the Zone.DNS permissions. + +.. literalinclude:: /examples/dns/cloudflare/instantiate_driver_token.py + :language: python + API Docs -------- diff --git a/docs/examples/dns/cloudflare/instantiate_driver_token.py b/docs/examples/dns/cloudflare/instantiate_driver_token.py new file mode 100644 index 0000000..ebda102 --- /dev/null +++ b/docs/examples/dns/cloudflare/instantiate_driver_token.py @@ -0,0 +1,5 @@ +from libcloud.dns.types import Provider +from libcloud.dns.providers import get_driver + +cls = get_driver(Provider.CLOUDFLARE) +driver = cls('<api token>') diff --git a/libcloud/dns/drivers/cloudflare.py b/libcloud/dns/drivers/cloudflare.py index 84bb5bd..0b91e28 100644 --- a/libcloud/dns/drivers/cloudflare.py +++ b/libcloud/dns/drivers/cloudflare.py @@ -20,7 +20,7 @@ __all__ = [ import itertools import json -from libcloud.common.base import JsonResponse, ConnectionUserAndKey +from libcloud.common.base import JsonResponse, ConnectionKey, ConnectionUserAndKey from libcloud.common.types import InvalidCredsError, LibcloudError from libcloud.dns.base import DNSDriver, Zone, Record from libcloud.dns.types import Provider, RecordType @@ -146,11 +146,16 @@ class CloudFlareDNSResponse(JsonResponse): raise exception_class(**kwargs) -class CloudFlareDNSConnection(ConnectionUserAndKey): +class BaseDNSConnection: host = API_HOST secure = True responseCls = CloudFlareDNSResponse + def encode_data(self, data): + return json.dumps(data) + + +class GlobalAPIKeyDNSConnection(BaseDNSConnection, ConnectionUserAndKey): def add_default_headers(self, headers): headers['Content-Type'] = 'application/json' headers['X-Auth-Email'] = self.user_id @@ -158,15 +163,20 @@ class CloudFlareDNSConnection(ConnectionUserAndKey): return headers - def encode_data(self, data): - return json.dumps(data) + +class TokenDNSConnection(BaseDNSConnection, ConnectionKey): + def add_default_headers(self, headers): + headers['Content-Type'] = 'application/json' + headers['Authorization'] = 'Bearer %s' % self.key + + return headers class CloudFlareDNSDriver(DNSDriver): type = Provider.CLOUDFLARE name = 'CloudFlare DNS' website = 'https://www.cloudflare.com' - connectionCls = CloudFlareDNSConnection + connectionCls = GlobalAPIKeyDNSConnection RECORD_TYPE_MAP = { RecordType.A: 'A', @@ -184,6 +194,22 @@ class CloudFlareDNSDriver(DNSDriver): RECORDS_PAGE_SIZE = 100 MEMBERSHIPS_PAGE_SIZE = 50 + def __init__(self, + key, # type: str + secret=None, # type: Optional[str] + secure=True, # type: bool + host=None, # type: Optional[str] + port=None, # type: Optional[int] + **kwargs # type: Optional[Any] + ): + + if secret is None: + self.connectionCls = TokenDNSConnection + + super(CloudFlareDNSDriver, self).__init__( + key=key, secret=secret, secure=secure, + host=host, port=port, **kwargs) + def iterate_zones(self): def _iterate_zones(params): url = '{}/zones'.format(API_BASE) diff --git a/libcloud/test/dns/test_cloudflare.py b/libcloud/test/dns/test_cloudflare.py index 125edf2..5f97c55 100644 --- a/libcloud/test/dns/test_cloudflare.py +++ b/libcloud/test/dns/test_cloudflare.py @@ -21,6 +21,8 @@ from libcloud.common.types import LibcloudError from libcloud.test import unittest from libcloud.dns.drivers.cloudflare import CloudFlareDNSDriver +from libcloud.dns.drivers.cloudflare import GlobalAPIKeyDNSConnection +from libcloud.dns.drivers.cloudflare import TokenDNSConnection from libcloud.dns.drivers.cloudflare import ZONE_EXTRA_ATTRIBUTES from libcloud.dns.drivers.cloudflare import RECORD_EXTRA_ATTRIBUTES from libcloud.dns.types import RecordType @@ -42,6 +44,14 @@ class CloudFlareDNSDriverTestCase(unittest.TestCase): CloudFlareMockHttp.use_param = 'a' self.driver = CloudFlareDNSDriver(*DNS_PARAMS_CLOUDFLARE) + def test_auth_key(self): + driver = CloudFlareDNSDriver('[email protected]', 'key') + self.assertEqual(driver.connectionCls, GlobalAPIKeyDNSConnection) + + def test_auth_token(self): + driver = CloudFlareDNSDriver('sometoken') + self.assertEqual(driver.connectionCls, TokenDNSConnection) + def test_list_record_types(self): record_types = self.driver.list_record_types() self.assertEqual(len(record_types), 9)
