Codebase list python-certbot-dns-google / d49085d
New upstream version 2.0.0 Harlan Lieberman-Berg 1 year, 5 months ago
13 changed file(s) with 54 addition(s) and 53 deletion(s). Raw diff Collapse all Expand all
22 recursive-include docs *
33 recursive-include certbot_dns_google/testdata *
44 recursive-include tests *
5 include certbot_dns_google/py.typed
56 global-exclude __pycache__
67 global-exclude *.py[cod]
00 Metadata-Version: 2.1
11 Name: certbot-dns-google
2 Version: 1.21.0
2 Version: 2.0.0
33 Summary: Google Cloud DNS Authenticator plugin for Certbot
44 Home-page: https://github.com/certbot/certbot
55 Author: Certbot Project
66 Author-email: certbot-dev@eff.org
77 License: Apache License 2.0
8 Platform: UNKNOWN
98 Classifier: Development Status :: 5 - Production/Stable
109 Classifier: Environment :: Plugins
1110 Classifier: Intended Audience :: System Administrators
1312 Classifier: Operating System :: POSIX :: Linux
1413 Classifier: Programming Language :: Python
1514 Classifier: Programming Language :: Python :: 3
16 Classifier: Programming Language :: Python :: 3.6
1715 Classifier: Programming Language :: Python :: 3.7
1816 Classifier: Programming Language :: Python :: 3.8
1917 Classifier: Programming Language :: Python :: 3.9
18 Classifier: Programming Language :: Python :: 3.10
19 Classifier: Programming Language :: Python :: 3.11
2020 Classifier: Topic :: Internet :: WWW/HTTP
2121 Classifier: Topic :: Security
2222 Classifier: Topic :: System :: Installation/Setup
2323 Classifier: Topic :: System :: Networking
2424 Classifier: Topic :: System :: Systems Administration
2525 Classifier: Topic :: Utilities
26 Requires-Python: >=3.6
26 Requires-Python: >=3.7
2727 Provides-Extra: docs
2828 License-File: LICENSE.txt
29
30 UNKNOWN
31
2929
3030 * ``dns.changes.create``
3131 * ``dns.changes.get``
32 * ``dns.changes.list``
33 * ``dns.managedZones.get``
3234 * ``dns.managedZones.list``
3335 * ``dns.resourceRecordSets.create``
3436 * ``dns.resourceRecordSets.delete``
00 """DNS Authenticator for Google Cloud DNS."""
11 import json
22 import logging
3 from typing import Any
4 from typing import Callable
5 from typing import Dict
6 from typing import Optional
37
48 from googleapiclient import discovery
59 from googleapiclient import errors as googleapiclient_errors
2832 ttl = 60
2933
3034 @classmethod
31 def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
35 def add_parser_arguments(cls, add: Callable[..., None],
36 default_propagation_seconds: int = 60) -> None:
3237 super().add_parser_arguments(add, default_propagation_seconds=60)
3338 add('credentials',
3439 help=('Path to Google Cloud DNS service account JSON file. (See {0} for' +
3641 'required permissions.)').format(ACCT_URL, PERMISSIONS_URL),
3742 default=None)
3843
39 def more_info(self): # pylint: disable=missing-function-docstring
44 def more_info(self) -> str:
4045 return 'This plugin configures a DNS TXT record to respond to a dns-01 challenge using ' + \
4146 'the Google Cloud DNS API.'
4247
43 def _setup_credentials(self):
48 def _setup_credentials(self) -> None:
4449 if self.conf('credentials') is None:
4550 try:
4651 # use project_id query to check for availability of google metadata server
5762
5863 dns_common.validate_file_permissions(self.conf('credentials'))
5964
60 def _perform(self, domain, validation_name, validation):
65 def _perform(self, domain: str, validation_name: str, validation: str) -> None:
6166 self._get_google_client().add_txt_record(domain, validation_name, validation, self.ttl)
6267
63 def _cleanup(self, domain, validation_name, validation):
68 def _cleanup(self, domain: str, validation_name: str, validation: str) -> None:
6469 self._get_google_client().del_txt_record(domain, validation_name, validation, self.ttl)
6570
66 def _get_google_client(self):
71 def _get_google_client(self) -> '_GoogleClient':
6772 return _GoogleClient(self.conf('credentials'))
6873
6974
7277 Encapsulates all communication with the Google Cloud DNS API.
7378 """
7479
75 def __init__(self, account_json=None, dns_api=None):
80 def __init__(self, account_json: Optional[str] = None,
81 dns_api: Optional[discovery.Resource] = None) -> None:
7682
7783 scopes = ['https://www.googleapis.com/auth/ndev.clouddns.readwrite']
7884 if account_json is not None:
94100 else:
95101 self.dns = dns_api
96102
97 def add_txt_record(self, domain, record_name, record_content, record_ttl):
103 def add_txt_record(self, domain: str, record_name: str, record_content: str,
104 record_ttl: int) -> None:
98105 """
99106 Add a TXT record using the supplied information.
100107
109116
110117 record_contents = self.get_existing_txt_rrset(zone_id, record_name)
111118 if record_contents is None:
112 # If it wasn't possible to fetch the records at this label (missing .list permission),
113 # assume there aren't any (#5678). If there are actually records here, this will fail
114 # with HTTP 409/412 API errors.
119 # If it wasn't possible to fetch the records at this label (missing .list permission),
120 # assume there aren't any (#5678). If there are actually records here, this will fail
121 # with HTTP 409/412 API errors.
115122 record_contents = {"rrdatas": []}
116123 add_records = record_contents["rrdatas"][:]
117124
163170 raise errors.PluginError('Error communicating with the Google Cloud DNS API: {0}'
164171 .format(e))
165172
166 def del_txt_record(self, domain, record_name, record_content, record_ttl):
173 def del_txt_record(self, domain: str, record_name: str, record_content: str,
174 record_ttl: int) -> None:
167175 """
168176 Delete a TXT record using the supplied information.
169177
223231 except googleapiclient_errors.Error as e:
224232 logger.warning('Encountered error deleting TXT record: %s', e)
225233
226 def get_existing_txt_rrset(self, zone_id, record_name):
234 def get_existing_txt_rrset(self, zone_id: str, record_name: str) -> Optional[Dict[str, Any]]:
227235 """
228236 Get existing TXT records from the RRset for the record name.
229237
253261 return response["rrsets"][0]
254262 return None
255263
256 def _find_managed_zone_id(self, domain):
264 def _find_managed_zone_id(self, domain: str) -> str:
257265 """
258266 Find the managed zone for a given domain.
259267
285293 .format(domain, zone_dns_name_guesses))
286294
287295 @staticmethod
288 def get_project_id():
296 def get_project_id() -> str:
289297 """
290298 Query the google metadata service for the current project ID
291299
(New empty file)
00 Metadata-Version: 2.1
11 Name: certbot-dns-google
2 Version: 1.21.0
2 Version: 2.0.0
33 Summary: Google Cloud DNS Authenticator plugin for Certbot
44 Home-page: https://github.com/certbot/certbot
55 Author: Certbot Project
66 Author-email: certbot-dev@eff.org
77 License: Apache License 2.0
8 Platform: UNKNOWN
98 Classifier: Development Status :: 5 - Production/Stable
109 Classifier: Environment :: Plugins
1110 Classifier: Intended Audience :: System Administrators
1312 Classifier: Operating System :: POSIX :: Linux
1413 Classifier: Programming Language :: Python
1514 Classifier: Programming Language :: Python :: 3
16 Classifier: Programming Language :: Python :: 3.6
1715 Classifier: Programming Language :: Python :: 3.7
1816 Classifier: Programming Language :: Python :: 3.8
1917 Classifier: Programming Language :: Python :: 3.9
18 Classifier: Programming Language :: Python :: 3.10
19 Classifier: Programming Language :: Python :: 3.11
2020 Classifier: Topic :: Internet :: WWW/HTTP
2121 Classifier: Topic :: Security
2222 Classifier: Topic :: System :: Installation/Setup
2323 Classifier: Topic :: System :: Networking
2424 Classifier: Topic :: System :: Systems Administration
2525 Classifier: Topic :: Utilities
26 Requires-Python: >=3.6
26 Requires-Python: >=3.7
2727 Provides-Extra: docs
2828 License-File: LICENSE.txt
29
30 UNKNOWN
31
00 LICENSE.txt
11 MANIFEST.in
22 README.rst
3 setup.cfg
43 setup.py
54 certbot_dns_google/__init__.py
5 certbot_dns_google/py.typed
66 certbot_dns_google.egg-info/PKG-INFO
77 certbot_dns_google.egg-info/SOURCES.txt
88 certbot_dns_google.egg-info/dependency_links.txt
00 [certbot.plugins]
11 dns-google = certbot_dns_google._internal.dns_google:Authenticator
2
00 google-api-python-client>=1.5.5
11 oauth2client>=4.0
2 setuptools>=39.0.1
2 setuptools>=41.6.0
33 httplib2
4 acme>=1.21.0
5 certbot>=1.21.0
4 acme>=2.0.0
5 certbot>=2.0.0
66
77 [docs]
88 Sphinx>=1.0
176176 intersphinx_mapping = {
177177 'python': ('https://docs.python.org/', None),
178178 'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
179 'certbot': ('https://certbot.eff.org/docs/', None),
179 'certbot': ('https://eff-certbot.readthedocs.io/en/stable/', None),
180180 }
0 [bdist_wheel]
1 universal = 1
2
30 [egg_info]
41 tag_build =
52 tag_date = 0
33 from setuptools import find_packages
44 from setuptools import setup
55
6 version = '1.21.0'
6 version = '2.0.0'
77
88 install_requires = [
99 'google-api-python-client>=1.5.5',
1010 'oauth2client>=4.0',
11 'setuptools>=39.0.1',
11 'setuptools>=41.6.0',
1212 # already a dependency of google-api-python-client, but added for consistency
1313 'httplib2'
1414 ]
4040 author="Certbot Project",
4141 author_email='certbot-dev@eff.org',
4242 license='Apache License 2.0',
43 python_requires='>=3.6',
43 python_requires='>=3.7',
4444 classifiers=[
4545 'Development Status :: 5 - Production/Stable',
4646 'Environment :: Plugins',
4949 'Operating System :: POSIX :: Linux',
5050 'Programming Language :: Python',
5151 'Programming Language :: Python :: 3',
52 'Programming Language :: Python :: 3.6',
5352 'Programming Language :: Python :: 3.7',
5453 'Programming Language :: Python :: 3.8',
5554 'Programming Language :: Python :: 3.9',
55 'Programming Language :: Python :: 3.10',
56 'Programming Language :: Python :: 3.11',
5657 'Topic :: Internet :: WWW/HTTP',
5758 'Topic :: Security',
5859 'Topic :: System :: Installation/Setup',
55 from googleapiclient.errors import Error
66 from googleapiclient.http import HttpMock
77 from httplib2 import ServerNotFoundError
8 try:
9 import mock
10 except ImportError: # pragma: no cover
11 from unittest import mock # type: ignore
8
9 from unittest import mock
1210
1311 from certbot import errors
1412 from certbot.compat import os
183181 with mock.patch(mock_get_rrs) as mock_rrs:
184182 mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": self.record_ttl}
185183 client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
186 self.assertTrue(changes.create.called)
184 self.assertIs(changes.create.called, True)
187185 deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
188 self.assertTrue("sample-txt-contents" in deletions["rrdatas"])
186 self.assertIn("sample-txt-contents", deletions["rrdatas"])
189187 self.assertEqual(self.record_ttl, deletions["ttl"])
190188
191189 @mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
200198 custom_ttl = 300
201199 mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": custom_ttl}
202200 client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
203 self.assertTrue(changes.create.called)
201 self.assertIs(changes.create.called, True)
204202 deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
205 self.assertTrue("sample-txt-contents" in deletions["rrdatas"])
203 self.assertIn("sample-txt-contents", deletions["rrdatas"])
206204 self.assertEqual(custom_ttl, deletions["ttl"]) #otherwise HTTP 412
207205
208206 @mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
213211 [{'managedZones': [{'id': self.zone}]}])
214212 client.add_txt_record(DOMAIN, "_acme-challenge.example.org",
215213 "example-txt-contents", self.record_ttl)
216 self.assertFalse(changes.create.called)
214 self.assertIs(changes.create.called, False)
217215
218216 @mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
219217 @mock.patch('certbot_dns_google._internal.dns_google.open',
356354 client, unused_changes = self._setUp_client_with_mock(
357355 [{'managedZones': [{'id': self.zone}]}])
358356 not_found = client.get_existing_txt_rrset(self.zone, "nonexistent.tld")
359 self.assertEqual(not_found, None)
357 self.assertIsNone(not_found)
360358
361359 @mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
362360 @mock.patch('certbot_dns_google._internal.dns_google.open',
366364 [{'managedZones': [{'id': self.zone}]}], API_ERROR)
367365 # Record name mocked in setUp
368366 found = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
369 self.assertEqual(found, None)
367 self.assertIsNone(found)
370368
371369 @mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
372370 @mock.patch('certbot_dns_google._internal.dns_google.open',
410408 def __init__(self):
411409 self.status = 200
412410
411
413412 if __name__ == "__main__":
414413 unittest.main() # pragma: no cover