Skip to content

Commit

Permalink
added ruff formatter and format code
Browse files Browse the repository at this point in the history
  • Loading branch information
infinityofspace committed Nov 13, 2024
1 parent 2ff93ed commit 336c84a
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 59 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/formatting_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: formatting check

on:
push:
pull_request:

jobs:
formatting-check:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.13" ]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/[email protected]
with:
python-version: ${{ matrix.python-version }}

- name: Install requirements
run: pip install -r requirements.txt

- name: Check formatting
run: ruff format --check
44 changes: 29 additions & 15 deletions certbot_dns_duckdns/cert/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
from certbot.plugins import dns_common
from dns import resolver

from certbot_dns_duckdns.duckdns.client import DuckDNSClient, NotValidDuckdnsDomainError, \
is_valid_full_duckdns_domain
from certbot_dns_duckdns.duckdns.client import (
DuckDNSClient,
NotValidDuckdnsDomainError,
is_valid_full_duckdns_domain,
)

DEFAULT_PROPAGATION_SECONDS = 30
TXT_MAX_LEN = 255
Expand Down Expand Up @@ -33,14 +36,22 @@ def add_parser_arguments(cls, add: callable) -> None:
:param add: method handling the argument adding to the cli
"""

super(Authenticator, cls).add_parser_arguments(add, default_propagation_seconds=DEFAULT_PROPAGATION_SECONDS)
super(Authenticator, cls).add_parser_arguments(
add, default_propagation_seconds=DEFAULT_PROPAGATION_SECONDS
)
add("credentials", help="DuckDNS credentials INI file.")
add("token", help="DuckDNS token (overwrites credentials file)")
add("token-env", default=TOKEN_ENV_NAME, help="Environment variable name for the DuckDNS token")
add("no-txt-restore",
add(
"token-env",
default=TOKEN_ENV_NAME,
help="Environment variable name for the DuckDNS token",
)
add(
"no-txt-restore",
default=False,
action="store_true",
help="Do not restore the original TXT record")
help="Do not restore the original TXT record",
)

def more_info(self) -> str:
"""
Expand All @@ -58,8 +69,7 @@ def _setup_credentials(self) -> None:

credentials_file = self.conf("credentials")
if credentials_file:
self._configure_file("credentials",
"DuckDNS credentials INI file")
self._configure_file("credentials", "DuckDNS credentials INI file")
dns_common.validate_file_permissions(credentials_file)
self.credentials = self._configure_credentials(
"credentials",
Expand Down Expand Up @@ -126,7 +136,9 @@ def _cleanup(self, domain: str, validation_name: str, validation: str) -> None:
# setting an empty TXT value does not work with the DuckDNS API
self._get_duckdns_client().clear_txt_record(duckdns_domain)
else:
self._get_duckdns_client().set_txt_record(duckdns_domain, self.old_txt_value)
self._get_duckdns_client().set_txt_record(
duckdns_domain, self.old_txt_value
)
except Exception as e:
raise errors.PluginError(e)

Expand Down Expand Up @@ -155,8 +167,8 @@ def _get_duckdns_domain(self, domain: str) -> str:

# delegated acme challenge (ipv4)
try:
result = resolver.resolve(f"{ACME_CHALLENGE_TXT_PREFIX}.{domain}", 'A')
delegated_domain = result.canonical_name.to_text().rstrip('.')
result = resolver.resolve(f"{ACME_CHALLENGE_TXT_PREFIX}.{domain}", "A")
delegated_domain = result.canonical_name.to_text().rstrip(".")

# check if the delegated domain is a valid duckdns.org domain
if is_valid_full_duckdns_domain(delegated_domain):
Expand All @@ -168,8 +180,8 @@ def _get_duckdns_domain(self, domain: str) -> str:

# delegated acme challenge (ipv6)
try:
result = resolver.resolve(f"{ACME_CHALLENGE_TXT_PREFIX}.{domain}", 'AAAA')
delegated_domain = result.canonical_name.to_text().rstrip('.')
result = resolver.resolve(f"{ACME_CHALLENGE_TXT_PREFIX}.{domain}", "AAAA")
delegated_domain = result.canonical_name.to_text().rstrip(".")

# check if the delegated domain is a valid duckdns.org domain
if is_valid_full_duckdns_domain(delegated_domain):
Expand All @@ -180,6 +192,8 @@ def _get_duckdns_domain(self, domain: str) -> str:
pass

# invalid domain
e = Exception(f"The given domain \"{domain}\" is neither a duckdns subdomain nor "
f" delegates _acme-challenge.{domain} to a duckdns subdomain.")
e = Exception(
f'The given domain "{domain}" is neither a duckdns subdomain nor '
f" delegates _acme-challenge.{domain} to a duckdns subdomain."
)
raise errors.PluginError(e)
35 changes: 20 additions & 15 deletions certbot_dns_duckdns/duckdns/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
logging.getLogger("urllib3").setLevel(logging.WARNING)

BASE_URL = "https://www.duckdns.org/update"
VALID_DUCKDNS_DOMAIN_REGEX = re.compile(r"^([a-z\d\\-]+\.)*[a-z\d\\-]+(\.duckdns\.org)?$")
VALID_FULL_DUCKDNS_DOMAIN_REGEX = re.compile(r"^([a-z\d\\-]+\.)*[a-z\d\\-]+\.duckdns\.org$")
VALID_DUCKDNS_DOMAIN_REGEX = re.compile(
r"^([a-z\d\\-]+\.)*[a-z\d\\-]+(\.duckdns\.org)?$"
)
VALID_FULL_DUCKDNS_DOMAIN_REGEX = re.compile(
r"^([a-z\d\\-]+\.)*[a-z\d\\-]+\.duckdns\.org$"
)


def is_valid_duckdns_domain(domain):
Expand All @@ -23,6 +27,7 @@ class TXTUpdateError(Exception):
"""
Exception if during the TXT record changing something goes wrong.
"""

pass


Expand All @@ -33,7 +38,7 @@ class NotValidDuckdnsDomainError(Exception):

def __init__(self, domain):
self.domain = domain
self.message = f"The domain \"{domain}\" is not valid a duckdns subdomain."
self.message = f'The domain "{domain}" is not valid a duckdns subdomain.'
super().__init__(self.message)


Expand Down Expand Up @@ -81,17 +86,15 @@ def set_txt_record(self, domain: str, txt: str) -> None:

root_domain = self.__get_validated_root_domain__(domain)

params = {
"token": self._token,
"domains": root_domain,
"txt": txt
}
params = {"token": self._token, "domains": root_domain, "txt": txt}
r = requests.get(url=BASE_URL, params=params)

if r.text != "OK":
raise TXTUpdateError("The TXT update \"{}\" for domain \"{}\" could not be set.\n"
"Request status code: {}\n"
"Request response text: {}".format(txt, domain, r.status_code, r.text))
raise TXTUpdateError(
'The TXT update "{}" for domain "{}" could not be set.\n'
"Request status code: {}\n"
"Request response text: {}".format(txt, domain, r.status_code, r.text)
)

@staticmethod
def __get_validated_root_domain__(domain):
Expand Down Expand Up @@ -131,11 +134,13 @@ def clear_txt_record(self, domain: str) -> None:
"token": self._token,
"domains": root_domain,
"txt": "",
"clear": "true"
"clear": "true",
}
r = requests.get(url=BASE_URL, params=params)

if r.text != "OK":
raise TXTUpdateError("The clearing of the TXT record for domain \"{}\" was not successful.\n"
"Request status code: {}\n"
"Request response text: {}".format(domain, r.status_code, r.text))
raise TXTUpdateError(
'The clearing of the TXT record for domain "{}" was not successful.\n'
"Request status code: {}\n"
"Request response text: {}".format(domain, r.status_code, r.text)
)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ requests>=2.20.0,<3.0
certbot>=1.18.0,<4.0
dnspython>=2.0.0,<3.0
responses~=0.25
ruff~=0.7
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@
"Topic :: Security",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Utilities",
"Topic :: System :: Systems Administration"
"Topic :: System :: Systems Administration",
],
packages=find_packages(),
python_requires=">=3.9",
install_requires=[
"certbot>=1.18.0,<4.0",
"requests>=2.20.0,<3.0",
"dnspython>=2.0.0,<3.0"
"dnspython>=2.0.0,<3.0",
],
entry_points={
"certbot.plugins": [
"dns-duckdns = certbot_dns_duckdns.cert.client:Authenticator",
]
}
},
)
8 changes: 2 additions & 6 deletions tests/cert_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ def test_valid_auth(self):

authenticator = Authenticator(config, name="duckdns")

authenticator._perform(
domain=domain, validation_name="", validation=txt_value
)
authenticator._perform(domain=domain, validation_name="", validation=txt_value)

@responses.activate
def test_invalid_auth(self):
Expand Down Expand Up @@ -127,6 +125,4 @@ def test_cleanup(self):

authenticator = Authenticator(config, name="duckdns")

authenticator._cleanup(
domain=domain, validation_name="", validation=txt_value
)
authenticator._cleanup(domain=domain, validation_name="", validation=txt_value)
76 changes: 56 additions & 20 deletions tests/duckdns_tests.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,105 @@
import unittest

from certbot_dns_duckdns.duckdns.client import DuckDNSClient, is_valid_duckdns_domain, is_valid_full_duckdns_domain
from certbot_dns_duckdns.duckdns.client import (
DuckDNSClient,
is_valid_duckdns_domain,
is_valid_full_duckdns_domain,
)

TEST_DOMAIN = "example.duckdns.org"
TEST_DUCKDNS_TOKEN = "1234567890abcdef"


class DuckDNSTests(unittest.TestCase):

def test_get_validated_root_domain_hyphen_domain(self):
domain = "test-example.duckdns.org"
root_domain = DuckDNSClient.__get_validated_root_domain__(domain)
self.assertEqual(root_domain, domain)

def test_get_validated_root_domain_multiple_subdomains(self):
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("test1.test2." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"test1.test2." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("test1.test2.test3." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"test1.test2.test3." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)

def test_get_validated_root_domain_special_subdomains(self):
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("test-." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"test-." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("test--." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"test--." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("-test." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"-test." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("--test." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"--test." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("-." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"-." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("--." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"--." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("-test-." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"-test-." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("test--test." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"test--test." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("12345." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"12345." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("12345-." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"12345-." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("12345--." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"12345--." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("-12345." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"-12345." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("--12345." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"--12345." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)
with self.subTest():
root_domain = DuckDNSClient.__get_validated_root_domain__("-123ad-sdas--45-as-." + TEST_DOMAIN)
root_domain = DuckDNSClient.__get_validated_root_domain__(
"-123ad-sdas--45-as-." + TEST_DOMAIN
)
self.assertEqual(root_domain, TEST_DOMAIN)


def test_is_valid_duckdns_domain(self):
with self.subTest():
self.assertTrue(is_valid_duckdns_domain(TEST_DOMAIN))
Expand All @@ -92,7 +126,9 @@ def test_is_valid_full_duckdns_domain(self):
self.assertTrue(is_valid_full_duckdns_domain("test.abc." + TEST_DOMAIN))
self.assertTrue(is_valid_full_duckdns_domain("test.abc.efg." + TEST_DOMAIN))
self.assertTrue(is_valid_full_duckdns_domain("test-abc." + TEST_DOMAIN))
self.assertTrue(is_valid_full_duckdns_domain("test-abc.test-def." + TEST_DOMAIN))
self.assertTrue(
is_valid_full_duckdns_domain("test-abc.test-def." + TEST_DOMAIN)
)

with self.subTest():
self.assertFalse(is_valid_full_duckdns_domain("abc.def.ghi"))
Expand Down

0 comments on commit 336c84a

Please sign in to comment.