Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-certbot-dns-rfc2136 for 
openSUSE:Factory checked in at 2023-06-07 23:08:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-certbot-dns-rfc2136 (Old)
 and      /work/SRC/openSUSE:Factory/.python-certbot-dns-rfc2136.new.15902 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-certbot-dns-rfc2136"

Wed Jun  7 23:08:13 2023 rev:39 rq:1091316 version:2.6.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-certbot-dns-rfc2136/python-certbot-dns-rfc2136.changes
    2022-10-27 13:55:21.032928105 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-certbot-dns-rfc2136.new.15902/python-certbot-dns-rfc2136.changes
 2023-06-07 23:08:52.135847468 +0200
@@ -1,0 +2,11 @@
+Wed Jun  7 16:05:55 UTC 2023 - Markéta Machová <[email protected]>
+
+- update to version 2.6.0
+  * Support for Python 3.11 was added to Certbot and all of its components.
+  * All Certbot components now require pytest to run tests.
+  * Packaged tests for all Certbot components besides josepy were moved inside 
the _internal/tests module.
+  * There is now a new Other annotated challenge object to allow plugins to 
support entirely novel challenges.
+  * DNS RFC2136 module now uses the TSIG key to check for an authoritative SOA 
record. Helps the use of 
+    split-horizon and multiple views in BIND9 using the key in an ACL to 
determine which view to use.
+
+-------------------------------------------------------------------

Old:
----
  certbot-dns-rfc2136-1.31.0.tar.gz

New:
----
  certbot-dns-rfc2136-2.6.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-certbot-dns-rfc2136.spec ++++++
--- /var/tmp/diff_new_pack.09dtEC/_old  2023-06-07 23:08:52.659850510 +0200
+++ /var/tmp/diff_new_pack.09dtEC/_new  2023-06-07 23:08:52.663850534 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-certbot-dns-rfc2136
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python3-%{**}}
 %define skip_python2 1
 Name:           python-certbot-dns-rfc2136
-Version:        1.31.0
+Version:        2.6.0
 Release:        0
 Summary:        RFC 2136 DNS Authenticator plugin for Certbot
 License:        Apache-2.0

++++++ certbot-dns-rfc2136-1.31.0.tar.gz -> certbot-dns-rfc2136-2.6.0.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-dns-rfc2136-1.31.0/MANIFEST.in 
new/certbot-dns-rfc2136-2.6.0/MANIFEST.in
--- old/certbot-dns-rfc2136-1.31.0/MANIFEST.in  2022-10-04 16:40:41.000000000 
+0200
+++ new/certbot-dns-rfc2136-2.6.0/MANIFEST.in   2023-05-09 21:44:36.000000000 
+0200
@@ -1,7 +1,6 @@
 include LICENSE.txt
 include README.rst
 recursive-include docs *
-recursive-include tests *
 include certbot_dns_rfc2136/py.typed
 global-exclude __pycache__
 global-exclude *.py[cod]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-dns-rfc2136-1.31.0/PKG-INFO 
new/certbot-dns-rfc2136-2.6.0/PKG-INFO
--- old/certbot-dns-rfc2136-1.31.0/PKG-INFO     2022-10-04 16:41:15.918922700 
+0200
+++ new/certbot-dns-rfc2136-2.6.0/PKG-INFO      2023-05-09 21:44:59.850839100 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: certbot-dns-rfc2136
-Version: 1.31.0
+Version: 2.6.0
 Summary: RFC 2136 DNS Authenticator plugin for Certbot
 Home-page: https://github.com/certbot/certbot
 Author: Certbot Project
@@ -17,6 +17,7 @@
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Security
 Classifier: Topic :: System :: Installation/Setup
@@ -25,4 +26,5 @@
 Classifier: Topic :: Utilities
 Requires-Python: >=3.7
 Provides-Extra: docs
+Provides-Extra: test
 License-File: LICENSE.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/__init__.py 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/__init__.py
--- old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/__init__.py      
2022-10-04 16:40:41.000000000 +0200
+++ new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/__init__.py       
2023-05-09 21:44:36.000000000 +0200
@@ -26,8 +26,8 @@
 
 Use of this plugin requires a configuration file containing the target DNS
 server and optional port that supports RFC 2136 Dynamic Updates, the name
-of the TSIG key, the TSIG key secret itself and the algorithm used if it's
-different to HMAC-MD5.
+of the TSIG key, the TSIG key secret itself, the algorithm used if it's
+different to HMAC-MD5, and optionally whether to sign the initial SOA query.
 
 .. code-block:: ini
    :name: credentials.ini
@@ -44,6 +44,9 @@
 AmKd7ak51vWKgSl12ib86oQRPkpDjg==
    # TSIG key algorithm
    dns_rfc2136_algorithm = HMAC-SHA512
+   # TSIG sign SOA query (optional, default: false)
+   dns_rfc2136_sign_query = false
+
 
 The path to this file can be provided interactively or using the
 ``--dns-rfc2136-credentials`` command-line argument. Certbot records the
@@ -107,12 +110,11 @@
 .. code-block:: bash
    :caption: Generate a new SHA512 TSIG key
 
-   dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST keyname.
+   tsig-keygen -a HMAC-SHA512 keyname.
 
 .. note::
-   There are a few tools shipped with BIND that can all generate TSIG keys;
-   ``dnssec-keygen``, ``rndc-confgen``, and ``ddns-confgen``. Try and use the
-   most secure algorithm supported by your DNS server.
+   Prior to BIND version 9.10.0, you will need to use ``dnssec-keygen`` to 
generate
+   TSIG keys. Try and use the most secure algorithm supported by your DNS 
server.
 
 .. code-block:: none
    :caption: Sample BIND configuration
@@ -162,8 +164,8 @@
 zone option.  The zone will be then visible in both zones with exactly the 
same content.
 
 .. note::
-   Order matters in BIND views, the ``in-view`` zone option must refer to a
-   view defined preceeding it, it cannot refer to a view defined later in the 
configuration file.
+   Order matters in BIND views: the ``in-view`` zone option must refer to a
+   view defined preceeding it.  It cannot refer to a view defined later in the 
configuration file.
 
 .. code-block:: none
    :caption: Split-view BIND configuration
@@ -195,4 +197,34 @@
      };
    };
 
+Another solution is to add `dns_rfc2136_sign_query = true` to the 
configuration file and then
+add the key to the `match-clients` list within the external zone view. All 
queries signed with
+this key should then be directed to this view, regardless of source IP.
+
+.. code-block:: none
+   :caption: Split-view BIND configuration with key-based ACLs
+
+   key "keyname." {
+     algorithm hmac-sha512;
+     secret "4q4wM/2I180UXoMyN4INVhJNi8V9BCV+jMw2mXgZw/CSuxUT8C7NKKFs \
+AmKd7ak51vWKgSl12ib86oQRPkpDjg==";
+   };
+
+   // adjust internal-addresses to suit your needs
+   acl internal-address { 127.0.0.0/8; 10.0.0.0/8; 192.168.0.0/16; 
172.16.0.0/12; };
+
+   acl certbot-keys { key keyname.; }
+
+   view "external" {
+     match-clients { acl certbot-keys; !internal-addresses; any; };
+
+     zone "example.com." IN {
+       type master;
+       file "named.example.com";
+       update-policy {
+         grant keyname. name _acme-challenge.example.com. txt;
+       };
+     };
+   };
+
 """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/_internal/dns_rfc2136.py 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/_internal/dns_rfc2136.py
--- old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/_internal/dns_rfc2136.py 
2022-10-04 16:40:41.000000000 +0200
+++ new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/_internal/dns_rfc2136.py  
2023-05-09 21:44:36.000000000 +0200
@@ -2,6 +2,7 @@
 import logging
 from typing import Any
 from typing import Callable
+from typing import cast
 from typing import Optional
 
 import dns.flags
@@ -59,7 +60,7 @@
                'RFC 2136 Dynamic Updates.'
 
     def _validate_credentials(self, credentials: CredentialsConfiguration) -> 
None:
-        server = credentials.conf('server')
+        server = cast(str, credentials.conf('server'))
         if not is_ipaddress(server):
             raise errors.PluginError("The configured target DNS server ({0}) 
is not a valid IPv4 "
                                      "or IPv6 address. A hostname is not 
allowed.".format(server))
@@ -89,12 +90,14 @@
     def _get_rfc2136_client(self) -> "_RFC2136Client":
         if not self.credentials:  # pragma: no cover
             raise errors.Error("Plugin has not been prepared.")
-        return _RFC2136Client(self.credentials.conf('server'),
-                              int(self.credentials.conf('port') or self.PORT),
-                              self.credentials.conf('name'),
-                              self.credentials.conf('secret'),
-                              
self.ALGORITHMS.get(self.credentials.conf('algorithm'),
-                                                  dns.tsig.HMAC_MD5))
+
+        return _RFC2136Client(cast(str, self.credentials.conf('server')),
+                              int(cast(str, self.credentials.conf('port')) or 
self.PORT),
+                              cast(str, self.credentials.conf('name')),
+                              cast(str, self.credentials.conf('secret')),
+                              
self.ALGORITHMS.get(self.credentials.conf('algorithm') or '',
+                                                  dns.tsig.HMAC_MD5),
+                              (self.credentials.conf('sign_query') or 
'').upper() == "TRUE")
 
 
 class _RFC2136Client:
@@ -102,13 +105,15 @@
     Encapsulates all communication with the target DNS server.
     """
     def __init__(self, server: str, port: int, key_name: str, key_secret: str,
-                 key_algorithm: dns.name.Name, timeout: int = 
DEFAULT_NETWORK_TIMEOUT) -> None:
+                 key_algorithm: dns.name.Name, sign_query: bool,
+                 timeout: int = DEFAULT_NETWORK_TIMEOUT) -> None:
         self.server = server
         self.port = port
         self.keyring = dns.tsigkeyring.from_text({
             key_name: key_secret
         })
         self.algorithm = key_algorithm
+        self.sign_query = sign_query
         self._default_timeout = timeout
 
     def add_txt_record(self, record_name: str, record_content: str, 
record_ttl: int) -> None:
@@ -138,7 +143,7 @@
         except Exception as e:
             raise errors.PluginError('Encountered error adding TXT record: {0}'
                                      .format(e))
-        rcode = response.rcode()  # type: ignore[attr-defined]
+        rcode = response.rcode()
 
         if rcode == dns.rcode.NOERROR:
             logger.debug('Successfully added TXT record %s', record_name)
@@ -173,7 +178,7 @@
         except Exception as e:
             raise errors.PluginError('Encountered error deleting TXT record: 
{0}'
                                      .format(e))
-        rcode = response.rcode()  # type: ignore[attr-defined]
+        rcode = response.rcode()
 
         if rcode == dns.rcode.NOERROR:
             logger.debug('Successfully deleted TXT record %s', record_name)
@@ -216,6 +221,9 @@
         request = dns.message.make_query(domain, dns.rdatatype.SOA, 
dns.rdataclass.IN)
         # Turn off Recursion Desired bit in query
         request.flags ^= dns.flags.RD
+        # Use our TSIG keyring if configured
+        if self.sign_query:
+            request.use_tsig(self.keyring, algorithm=self.algorithm)
 
         try:
             try:
@@ -223,11 +231,11 @@
             except (OSError, dns.exception.Timeout) as e:
                 logger.debug('TCP query failed, fallback to UDP: %s', e)
                 response = dns.query.udp(request, self.server, 
self._default_timeout, self.port)
-            rcode = response.rcode()  # type: ignore[attr-defined]
+            rcode = response.rcode()
 
             # Authoritative Answer bit should be set
             if (rcode == dns.rcode.NOERROR
-                    and response.get_rrset(response.answer,  # type: 
ignore[attr-defined]
+                    and response.get_rrset(response.answer,
                                            domain, dns.rdataclass.IN, 
dns.rdatatype.SOA)
                     and response.flags & dns.flags.AA):
                 logger.debug('Received authoritative SOA response for %s', 
domain_name)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/_internal/tests/__init__.py 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/_internal/tests/__init__.py
--- 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/_internal/tests/__init__.py  
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/_internal/tests/__init__.py   
    2023-05-09 21:44:36.000000000 +0200
@@ -0,0 +1 @@
+"""certbot-dns-rfc2136 tests"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/_internal/tests/dns_rfc2136_test.py
 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/_internal/tests/dns_rfc2136_test.py
--- 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136/_internal/tests/dns_rfc2136_test.py
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136/_internal/tests/dns_rfc2136_test.py
       2023-05-09 21:44:36.000000000 +0200
@@ -0,0 +1,248 @@
+"""Tests for certbot_dns_rfc2136._internal.dns_rfc2136."""
+
+import sys
+import unittest
+from unittest import mock
+
+import dns.flags
+import dns.rcode
+import dns.tsig
+import pytest
+
+from certbot import errors
+from certbot.compat import os
+from certbot.plugins import dns_test_common
+from certbot.plugins.dns_test_common import DOMAIN
+from certbot.tests import util as test_util
+
+SERVER = '192.0.2.1'
+PORT = 53
+NAME = 'a-tsig-key.'
+SECRET = 'SSB3b25kZXIgd2hvIHdpbGwgYm90aGVyIHRvIGRlY29kZSB0aGlzIHRleHQK'
+VALID_CONFIG = {"rfc2136_server": SERVER, "rfc2136_name": NAME, 
"rfc2136_secret": SECRET}
+TIMEOUT = 45
+
+
+class AuthenticatorTest(test_util.TempDirTestCase, 
dns_test_common.BaseAuthenticatorTest):
+
+    def setUp(self):
+        from certbot_dns_rfc2136._internal.dns_rfc2136 import Authenticator
+
+        super().setUp()
+
+        path = os.path.join(self.tempdir, 'file.ini')
+        dns_test_common.write(VALID_CONFIG, path)
+
+        self.config = mock.MagicMock(rfc2136_credentials=path,
+                                     rfc2136_propagation_seconds=0)  # don't 
wait during tests
+
+        self.auth = Authenticator(self.config, "rfc2136")
+
+        self.mock_client = mock.MagicMock()
+        # _get_rfc2136_client | pylint: disable=protected-access
+        self.orig_get_client = self.auth._get_rfc2136_client
+        self.auth._get_rfc2136_client = 
mock.MagicMock(return_value=self.mock_client)
+
+    def test_get_client_default_conf_values(self):
+        # algorithm and sign_query are intentionally absent to test that the 
default (None)
+        # value does not crash Certbot.
+        creds = { "server": SERVER, "port": PORT, "name": NAME, "secret": 
SECRET }
+        self.auth.credentials = mock.MagicMock()
+        self.auth.credentials.conf = lambda key: creds.get(key, None)
+        client = self.orig_get_client()
+        assert client.algorithm == self.auth.ALGORITHMS["HMAC-MD5"]
+        assert client.sign_query == False
+
+    @test_util.patch_display_util()
+    def test_perform(self, unused_mock_get_utility):
+        self.auth.perform([self.achall])
+
+        expected = [mock.call.add_txt_record('_acme-challenge.'+DOMAIN, 
mock.ANY, mock.ANY)]
+        assert expected == self.mock_client.mock_calls
+
+    def test_cleanup(self):
+        # _attempt_cleanup | pylint: disable=protected-access
+        self.auth._attempt_cleanup = True
+        self.auth.cleanup([self.achall])
+
+        expected = [mock.call.del_txt_record('_acme-challenge.'+DOMAIN, 
mock.ANY)]
+        assert expected == self.mock_client.mock_calls
+
+    def test_invalid_algorithm_raises(self):
+        config = VALID_CONFIG.copy()
+        config["rfc2136_algorithm"] = "INVALID"
+        dns_test_common.write(config, self.config.rfc2136_credentials)
+
+        with pytest.raises(errors.PluginError):
+            self.auth.perform([self.achall])
+
+    @test_util.patch_display_util()
+    def test_valid_algorithm_passes(self, unused_mock_get_utility):
+        config = VALID_CONFIG.copy()
+        config["rfc2136_algorithm"] = "HMAC-sha512"
+        dns_test_common.write(config, self.config.rfc2136_credentials)
+
+        self.auth.perform([self.achall])
+
+    def test_invalid_server_raises(self):
+        config = VALID_CONFIG.copy()
+        config["rfc2136_server"] = "example.com"
+        dns_test_common.write(config, self.config.rfc2136_credentials)
+
+        with pytest.raises(errors.PluginError):
+            self.auth.perform([self.achall])
+
+    @test_util.patch_display_util()
+    def test_valid_server_passes(self, unused_mock_get_utility):
+        config = VALID_CONFIG.copy()
+        dns_test_common.write(config, self.config.rfc2136_credentials)
+
+        self.auth.perform([self.achall])
+
+        config["rfc2136_server"] = "2001:db8:3333:4444:cccc:dddd:eeee:ffff"
+        dns_test_common.write(config, self.config.rfc2136_credentials)
+
+        self.auth.perform([self.achall])
+
+
+class RFC2136ClientTest(unittest.TestCase):
+
+    def setUp(self):
+        from certbot_dns_rfc2136._internal.dns_rfc2136 import _RFC2136Client
+
+        self.rfc2136_client = _RFC2136Client(SERVER, PORT, NAME, SECRET, 
dns.tsig.HMAC_MD5,
+        False, TIMEOUT)
+
+    @mock.patch("dns.query.tcp")
+    def test_add_txt_record(self, query_mock):
+        query_mock.return_value.rcode.return_value = dns.rcode.NOERROR
+        # _find_domain | pylint: disable=protected-access
+        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
+
+        self.rfc2136_client.add_txt_record("bar", "baz", 42)
+
+        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
+        assert 'bar. 42 IN TXT "baz"' in str(query_mock.call_args[0][0])
+
+    @mock.patch("dns.query.tcp")
+    def test_add_txt_record_wraps_errors(self, query_mock):
+        query_mock.side_effect = Exception
+        # _find_domain | pylint: disable=protected-access
+        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
+
+        with pytest.raises(errors.PluginError):
+            self.rfc2136_client.add_txt_record("bar", "baz", 42)
+
+    @mock.patch("dns.query.tcp")
+    def test_add_txt_record_server_error(self, query_mock):
+        query_mock.return_value.rcode.return_value = dns.rcode.NXDOMAIN
+        # _find_domain | pylint: disable=protected-access
+        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
+
+        with pytest.raises(errors.PluginError):
+            self.rfc2136_client.add_txt_record("bar", "baz", 42)
+
+    @mock.patch("dns.query.tcp")
+    def test_del_txt_record(self, query_mock):
+        query_mock.return_value.rcode.return_value = dns.rcode.NOERROR
+        # _find_domain | pylint: disable=protected-access
+        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
+
+        self.rfc2136_client.del_txt_record("bar", "baz")
+
+        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
+        assert 'bar. 0 NONE TXT "baz"' in str(query_mock.call_args[0][0])
+
+    @mock.patch("dns.query.tcp")
+    def test_del_txt_record_wraps_errors(self, query_mock):
+        query_mock.side_effect = Exception
+        # _find_domain | pylint: disable=protected-access
+        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
+
+        with pytest.raises(errors.PluginError):
+            self.rfc2136_client.del_txt_record("bar", "baz")
+
+    @mock.patch("dns.query.tcp")
+    def test_del_txt_record_server_error(self, query_mock):
+        query_mock.return_value.rcode.return_value = dns.rcode.NXDOMAIN
+        # _find_domain | pylint: disable=protected-access
+        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
+
+        with pytest.raises(errors.PluginError):
+            self.rfc2136_client.del_txt_record("bar", "baz")
+
+    def test_find_domain(self):
+        # _query_soa | pylint: disable=protected-access
+        self.rfc2136_client._query_soa = mock.MagicMock(side_effect=[False, 
False, True])
+
+        # _find_domain | pylint: disable=protected-access
+        domain = self.rfc2136_client._find_domain('foo.bar.'+DOMAIN)
+
+        assert domain == DOMAIN
+
+    def test_find_domain_wraps_errors(self):
+        # _query_soa | pylint: disable=protected-access
+        self.rfc2136_client._query_soa = mock.MagicMock(return_value=False)
+
+        with pytest.raises(errors.PluginError):
+            self.rfc2136_client._find_domain('foo.bar.'+DOMAIN)
+
+    @mock.patch("dns.query.tcp")
+    @mock.patch("dns.message.make_query")
+    def test_query_soa_found(self, mock_make_query, query_mock):
+        query_mock.return_value = mock.MagicMock(answer=[mock.MagicMock()], 
flags=dns.flags.AA)
+        query_mock.return_value.rcode.return_value = dns.rcode.NOERROR
+        mock_make_query.return_value = mock.MagicMock()
+
+        # _query_soa | pylint: disable=protected-access
+        result = self.rfc2136_client._query_soa(DOMAIN)
+
+        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
+        mock_make_query.return_value.use_tsig.assert_not_called()
+        assert result
+
+    @mock.patch("dns.query.tcp")
+    def test_query_soa_not_found(self, query_mock):
+        query_mock.return_value.rcode.return_value = dns.rcode.NXDOMAIN
+
+        # _query_soa | pylint: disable=protected-access
+        result = self.rfc2136_client._query_soa(DOMAIN)
+
+        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
+        assert not result
+
+    @mock.patch("dns.query.tcp")
+    def test_query_soa_wraps_errors(self, query_mock):
+        query_mock.side_effect = Exception
+
+        with pytest.raises(errors.PluginError):
+            self.rfc2136_client._query_soa(DOMAIN)
+
+    @mock.patch("dns.query.udp")
+    @mock.patch("dns.query.tcp")
+    def test_query_soa_fallback_to_udp(self, tcp_mock, udp_mock):
+        tcp_mock.side_effect = OSError
+        udp_mock.return_value = mock.MagicMock(answer=[mock.MagicMock()], 
flags=dns.flags.AA)
+        udp_mock.return_value.rcode.return_value = dns.rcode.NOERROR
+
+        # _query_soa | pylint: disable=protected-access
+        result = self.rfc2136_client._query_soa(DOMAIN)
+
+        tcp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
+        udp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
+        assert result
+
+    @mock.patch("dns.query.tcp")
+    @mock.patch("dns.message.make_query")
+    def test_query_soa_signed(self, mock_make_query, unused_mock_query):
+        mock_make_query.return_value = mock.MagicMock()
+        self.rfc2136_client.sign_query = True
+        self.rfc2136_client.algorithm = "alg0"
+
+        self.rfc2136_client._query_soa(DOMAIN)
+
+        mock_make_query.return_value.use_tsig.assert_called_with(mock.ANY, 
algorithm="alg0")
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main(sys.argv[1:] + [__file__]))  # pragma: no cover
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136.egg-info/PKG-INFO 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136.egg-info/PKG-INFO
--- old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136.egg-info/PKG-INFO        
2022-10-04 16:41:15.000000000 +0200
+++ new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136.egg-info/PKG-INFO 
2023-05-09 21:44:59.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: certbot-dns-rfc2136
-Version: 1.31.0
+Version: 2.6.0
 Summary: RFC 2136 DNS Authenticator plugin for Certbot
 Home-page: https://github.com/certbot/certbot
 Author: Certbot Project
@@ -17,6 +17,7 @@
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Security
 Classifier: Topic :: System :: Installation/Setup
@@ -25,4 +26,5 @@
 Classifier: Topic :: Utilities
 Requires-Python: >=3.7
 Provides-Extra: docs
+Provides-Extra: test
 License-File: LICENSE.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136.egg-info/SOURCES.txt 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136.egg-info/SOURCES.txt
--- old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136.egg-info/SOURCES.txt     
2022-10-04 16:41:15.000000000 +0200
+++ new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136.egg-info/SOURCES.txt      
2023-05-09 21:44:59.000000000 +0200
@@ -12,10 +12,11 @@
 certbot_dns_rfc2136.egg-info/top_level.txt
 certbot_dns_rfc2136/_internal/__init__.py
 certbot_dns_rfc2136/_internal/dns_rfc2136.py
+certbot_dns_rfc2136/_internal/tests/__init__.py
+certbot_dns_rfc2136/_internal/tests/dns_rfc2136_test.py
 docs/.gitignore
 docs/Makefile
 docs/api.rst
 docs/conf.py
 docs/index.rst
-docs/make.bat
-tests/dns_rfc2136_test.py
\ No newline at end of file
+docs/make.bat
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136.egg-info/requires.txt 
new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136.egg-info/requires.txt
--- old/certbot-dns-rfc2136-1.31.0/certbot_dns_rfc2136.egg-info/requires.txt    
2022-10-04 16:41:15.000000000 +0200
+++ new/certbot-dns-rfc2136-2.6.0/certbot_dns_rfc2136.egg-info/requires.txt     
2023-05-09 21:44:59.000000000 +0200
@@ -1,8 +1,11 @@
 dnspython>=1.15.0
 setuptools>=41.6.0
-acme>=1.31.0
-certbot>=1.31.0
+acme>=2.6.0
+certbot>=2.6.0
 
 [docs]
 Sphinx>=1.0
 sphinx_rtd_theme
+
+[test]
+pytest
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-dns-rfc2136-1.31.0/setup.py 
new/certbot-dns-rfc2136-2.6.0/setup.py
--- old/certbot-dns-rfc2136-1.31.0/setup.py     2022-10-04 16:40:42.000000000 
+0200
+++ new/certbot-dns-rfc2136-2.6.0/setup.py      2023-05-09 21:44:37.000000000 
+0200
@@ -4,7 +4,7 @@
 from setuptools import find_packages
 from setuptools import setup
 
-version = '1.31.0'
+version = '2.6.0'
 
 install_requires = [
     'dnspython>=1.15.0',
@@ -30,6 +30,10 @@
     'sphinx_rtd_theme',
 ]
 
+test_extras = [
+    'pytest',
+]
+
 setup(
     name='certbot-dns-rfc2136',
     version=version,
@@ -51,6 +55,7 @@
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
         'Topic :: Internet :: WWW/HTTP',
         'Topic :: Security',
         'Topic :: System :: Installation/Setup',
@@ -64,6 +69,7 @@
     install_requires=install_requires,
     extras_require={
         'docs': docs_extras,
+        'test': test_extras,
     },
     entry_points={
         'certbot.plugins': [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-dns-rfc2136-1.31.0/tests/dns_rfc2136_test.py 
new/certbot-dns-rfc2136-2.6.0/tests/dns_rfc2136_test.py
--- old/certbot-dns-rfc2136-1.31.0/tests/dns_rfc2136_test.py    2022-10-04 
16:40:41.000000000 +0200
+++ new/certbot-dns-rfc2136-2.6.0/tests/dns_rfc2136_test.py     1970-01-01 
01:00:00.000000000 +0100
@@ -1,240 +0,0 @@
-"""Tests for certbot_dns_rfc2136._internal.dns_rfc2136."""
-
-import unittest
-
-import dns.flags
-import dns.rcode
-import dns.tsig
-try:
-    import mock
-except ImportError: # pragma: no cover
-    from unittest import mock # type: ignore
-
-from certbot import errors
-from certbot.compat import os
-from certbot.plugins import dns_test_common
-from certbot.plugins.dns_test_common import DOMAIN
-from certbot.tests import util as test_util
-
-SERVER = '192.0.2.1'
-PORT = 53
-NAME = 'a-tsig-key.'
-SECRET = 'SSB3b25kZXIgd2hvIHdpbGwgYm90aGVyIHRvIGRlY29kZSB0aGlzIHRleHQK'
-VALID_CONFIG = {"rfc2136_server": SERVER, "rfc2136_name": NAME, 
"rfc2136_secret": SECRET}
-TIMEOUT = 45
-
-
-class AuthenticatorTest(test_util.TempDirTestCase, 
dns_test_common.BaseAuthenticatorTest):
-
-    def setUp(self):
-        from certbot_dns_rfc2136._internal.dns_rfc2136 import Authenticator
-
-        super().setUp()
-
-        path = os.path.join(self.tempdir, 'file.ini')
-        dns_test_common.write(VALID_CONFIG, path)
-
-        self.config = mock.MagicMock(rfc2136_credentials=path,
-                                     rfc2136_propagation_seconds=0)  # don't 
wait during tests
-
-        self.auth = Authenticator(self.config, "rfc2136")
-
-        self.mock_client = mock.MagicMock()
-        # _get_rfc2136_client | pylint: disable=protected-access
-        self.auth._get_rfc2136_client = 
mock.MagicMock(return_value=self.mock_client)
-
-    @test_util.patch_display_util()
-    def test_perform(self, unused_mock_get_utility):
-        self.auth.perform([self.achall])
-
-        expected = [mock.call.add_txt_record('_acme-challenge.'+DOMAIN, 
mock.ANY, mock.ANY)]
-        self.assertEqual(expected, self.mock_client.mock_calls)
-
-    def test_cleanup(self):
-        # _attempt_cleanup | pylint: disable=protected-access
-        self.auth._attempt_cleanup = True
-        self.auth.cleanup([self.achall])
-
-        expected = [mock.call.del_txt_record('_acme-challenge.'+DOMAIN, 
mock.ANY)]
-        self.assertEqual(expected, self.mock_client.mock_calls)
-
-    def test_invalid_algorithm_raises(self):
-        config = VALID_CONFIG.copy()
-        config["rfc2136_algorithm"] = "INVALID"
-        dns_test_common.write(config, self.config.rfc2136_credentials)
-
-        self.assertRaises(errors.PluginError,
-                          self.auth.perform,
-                          [self.achall])
-
-    @test_util.patch_display_util()
-    def test_valid_algorithm_passes(self, unused_mock_get_utility):
-        config = VALID_CONFIG.copy()
-        config["rfc2136_algorithm"] = "HMAC-sha512"
-        dns_test_common.write(config, self.config.rfc2136_credentials)
-
-        self.auth.perform([self.achall])
-
-    def test_invalid_server_raises(self):
-        config = VALID_CONFIG.copy()
-        config["rfc2136_server"] = "example.com"
-        dns_test_common.write(config, self.config.rfc2136_credentials)
-
-        self.assertRaises(errors.PluginError,
-                          self.auth.perform,
-                          [self.achall])
-
-    @test_util.patch_display_util()
-    def test_valid_server_passes(self, unused_mock_get_utility):
-        config = VALID_CONFIG.copy()
-        dns_test_common.write(config, self.config.rfc2136_credentials)
-
-        self.auth.perform([self.achall])
-
-        config["rfc2136_server"] = "2001:db8:3333:4444:cccc:dddd:eeee:ffff"
-        dns_test_common.write(config, self.config.rfc2136_credentials)
-
-        self.auth.perform([self.achall])
-
-
-class RFC2136ClientTest(unittest.TestCase):
-
-    def setUp(self):
-        from certbot_dns_rfc2136._internal.dns_rfc2136 import _RFC2136Client
-
-        self.rfc2136_client = _RFC2136Client(SERVER, PORT, NAME, SECRET, 
dns.tsig.HMAC_MD5,
-        TIMEOUT)
-
-    @mock.patch("dns.query.tcp")
-    def test_add_txt_record(self, query_mock):
-        query_mock.return_value.rcode.return_value = dns.rcode.NOERROR
-        # _find_domain | pylint: disable=protected-access
-        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
-
-        self.rfc2136_client.add_txt_record("bar", "baz", 42)
-
-        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
-        self.assertIn('bar. 42 IN TXT "baz"', str(query_mock.call_args[0][0]))
-
-    @mock.patch("dns.query.tcp")
-    def test_add_txt_record_wraps_errors(self, query_mock):
-        query_mock.side_effect = Exception
-        # _find_domain | pylint: disable=protected-access
-        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
-
-        self.assertRaises(
-            errors.PluginError,
-            self.rfc2136_client.add_txt_record,
-             "bar", "baz", 42)
-
-    @mock.patch("dns.query.tcp")
-    def test_add_txt_record_server_error(self, query_mock):
-        query_mock.return_value.rcode.return_value = dns.rcode.NXDOMAIN
-        # _find_domain | pylint: disable=protected-access
-        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
-
-        self.assertRaises(
-            errors.PluginError,
-            self.rfc2136_client.add_txt_record,
-             "bar", "baz", 42)
-
-    @mock.patch("dns.query.tcp")
-    def test_del_txt_record(self, query_mock):
-        query_mock.return_value.rcode.return_value = dns.rcode.NOERROR
-        # _find_domain | pylint: disable=protected-access
-        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
-
-        self.rfc2136_client.del_txt_record("bar", "baz")
-
-        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
-        self.assertIn('bar. 0 NONE TXT "baz"', str(query_mock.call_args[0][0]))
-
-    @mock.patch("dns.query.tcp")
-    def test_del_txt_record_wraps_errors(self, query_mock):
-        query_mock.side_effect = Exception
-        # _find_domain | pylint: disable=protected-access
-        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
-
-        self.assertRaises(
-            errors.PluginError,
-            self.rfc2136_client.del_txt_record,
-             "bar", "baz")
-
-    @mock.patch("dns.query.tcp")
-    def test_del_txt_record_server_error(self, query_mock):
-        query_mock.return_value.rcode.return_value = dns.rcode.NXDOMAIN
-        # _find_domain | pylint: disable=protected-access
-        self.rfc2136_client._find_domain = 
mock.MagicMock(return_value="example.com")
-
-        self.assertRaises(
-            errors.PluginError,
-            self.rfc2136_client.del_txt_record,
-             "bar", "baz")
-
-    def test_find_domain(self):
-        # _query_soa | pylint: disable=protected-access
-        self.rfc2136_client._query_soa = mock.MagicMock(side_effect=[False, 
False, True])
-
-        # _find_domain | pylint: disable=protected-access
-        domain = self.rfc2136_client._find_domain('foo.bar.'+DOMAIN)
-
-        self.assertEqual(domain, DOMAIN)
-
-    def test_find_domain_wraps_errors(self):
-        # _query_soa | pylint: disable=protected-access
-        self.rfc2136_client._query_soa = mock.MagicMock(return_value=False)
-
-        self.assertRaises(
-            errors.PluginError,
-            # _find_domain | pylint: disable=protected-access
-            self.rfc2136_client._find_domain,
-            'foo.bar.'+DOMAIN)
-
-    @mock.patch("dns.query.tcp")
-    def test_query_soa_found(self, query_mock):
-        query_mock.return_value = mock.MagicMock(answer=[mock.MagicMock()], 
flags=dns.flags.AA)
-        query_mock.return_value.rcode.return_value = dns.rcode.NOERROR
-
-        # _query_soa | pylint: disable=protected-access
-        result = self.rfc2136_client._query_soa(DOMAIN)
-
-        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
-        self.assertTrue(result)
-
-    @mock.patch("dns.query.tcp")
-    def test_query_soa_not_found(self, query_mock):
-        query_mock.return_value.rcode.return_value = dns.rcode.NXDOMAIN
-
-        # _query_soa | pylint: disable=protected-access
-        result = self.rfc2136_client._query_soa(DOMAIN)
-
-        query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
-        self.assertFalse(result)
-
-    @mock.patch("dns.query.tcp")
-    def test_query_soa_wraps_errors(self, query_mock):
-        query_mock.side_effect = Exception
-
-        self.assertRaises(
-            errors.PluginError,
-            # _query_soa | pylint: disable=protected-access
-            self.rfc2136_client._query_soa,
-            DOMAIN)
-
-    @mock.patch("dns.query.udp")
-    @mock.patch("dns.query.tcp")
-    def test_query_soa_fallback_to_udp(self, tcp_mock, udp_mock):
-        tcp_mock.side_effect = OSError
-        udp_mock.return_value = mock.MagicMock(answer=[mock.MagicMock()], 
flags=dns.flags.AA)
-        udp_mock.return_value.rcode.return_value = dns.rcode.NOERROR
-
-        # _query_soa | pylint: disable=protected-access
-        result = self.rfc2136_client._query_soa(DOMAIN)
-
-        tcp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
-        udp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
-        self.assertTrue(result)
-
-
-if __name__ == "__main__":
-    unittest.main()  # pragma: no cover

Reply via email to