Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3785722
D2249.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
D2249.diff
View Options
diff --git a/_tests/Makefile b/_tests/Makefile
--- a/_tests/Makefile
+++ b/_tests/Makefile
@@ -1,4 +1,9 @@
-test:
+test: test-python test-bats
+
+test-python:
python -m unittest discover modules
python -m unittest discover pillar
python -m unittest discover scripts/python
+
+test-bats:
+ bats scripts/bats/test_edit_acme_dns_accounts.sh
diff --git a/_tests/data/acmedns-merged.json b/_tests/data/acmedns-merged.json
new file mode 100644
--- /dev/null
+++ b/_tests/data/acmedns-merged.json
@@ -0,0 +1,29 @@
+{
+ "nasqueron.org": {
+ "username": "082e20b6-83e2-4844-9dc8-95d625c6c8bb",
+ "password": "password1",
+ "fulldomain": "f65af502-9d34-402d-9a7e-c11529509b84.acme.nasqueron.org",
+ "subdomain": "f65af502-9d34-402d-9a7e-c11529509b84",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ },
+ "foo.tld": {
+ "username": "9ec73be3-5556-11ea-9c51-0007cb03f249",
+ "password": "password2",
+ "fulldomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ },
+ "bar.tld": {
+ "username": "c60f2111-5556-11ea-9c51-0007cb03f249",
+ "password": "password3",
+ "fulldomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ }
+}
diff --git a/_tests/data/acmedns-toimport.json b/_tests/data/acmedns-toimport.json
new file mode 100644
--- /dev/null
+++ b/_tests/data/acmedns-toimport.json
@@ -0,0 +1,11 @@
+{
+ "bar.tld": {
+ "username": "c60f2111-5556-11ea-9c51-0007cb03f249",
+ "password": "password3",
+ "fulldomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ }
+}
diff --git a/_tests/data/acmedns.json b/_tests/data/acmedns.json
new file mode 100644
--- /dev/null
+++ b/_tests/data/acmedns.json
@@ -0,0 +1,20 @@
+{
+ "nasqueron.org": {
+ "username": "082e20b6-83e2-4844-9dc8-95d625c6c8bb",
+ "password": "password1",
+ "fulldomain": "f65af502-9d34-402d-9a7e-c11529509b84.acme.nasqueron.org",
+ "subdomain": "f65af502-9d34-402d-9a7e-c11529509b84",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ },
+ "foo.tld": {
+ "username": "9ec73be3-5556-11ea-9c51-0007cb03f249",
+ "password": "password2",
+ "fulldomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ }
+}
diff --git a/_tests/scripts/bats/test_edit_acme_dns_accounts.sh b/_tests/scripts/bats/test_edit_acme_dns_accounts.sh
new file mode 100755
--- /dev/null
+++ b/_tests/scripts/bats/test_edit_acme_dns_accounts.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bats
+
+SCRIPT="../roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py"
+
+# -------------------------------------------------------------
+# Arguments parsing
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+@test "exit with error code if no arg provided" {
+ run $SCRIPT
+ [ "$status" -ne 0 ]
+}
+
+@test "exit with error code if command doesn't exist" {
+ run $SCRIPT somenonexistingcommand
+ [ "$status" -ne 0 ]
+}
+
+@test "exit with error code if no enough arg" {
+ run $SCRIPT import
+ [ "$status" -ne 0 ]
+}
+
+# -------------------------------------------------------------
+# Import
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+@test "can't import a file into itself" {
+ export ACME_ACCOUNTS=/dev/null
+ run $SCRIPT import /dev/null
+ [ "$output" = "You're trying to import /dev/null to itself" ]
+ [ "$status" -eq 2 ]
+}
+
+@test "can merge correctly two credentials files" {
+ ACME_ACCOUNTS=$(mktemp)
+ export ACME_ACCOUNTS
+ cp data/acmedns.json "$ACME_ACCOUNTS"
+
+ run $SCRIPT import data/acmedns-toimport.json
+ [ "$status" -eq 0 ]
+
+ isValid=0
+ run jsondiff "$ACME_ACCOUNTS" data/acmedns-merged.json
+ rm "$ACME_ACCOUNTS"
+ [ "$status" -eq 0 ]
+ [ "$output" = "{}" ] || isValid=1
+
+ if [ $isValid -ne 0 ]; then
+ echo "Non matching part according jsondiff:"
+ echo "$output"
+ fi
+
+ return $isValid
+}
diff --git a/_tests/scripts/python/test_edit_acme_dns_accounts.py b/_tests/scripts/python/test_edit_acme_dns_accounts.py
new file mode 100644
--- /dev/null
+++ b/_tests/scripts/python/test_edit_acme_dns_accounts.py
@@ -0,0 +1,48 @@
+from importlib.machinery import SourceFileLoader
+import os
+import unittest
+
+
+os.environ["ACME_ACCOUNTS"] = "/path/to/acmedns.json"
+
+path = "roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py"
+script = SourceFileLoader('script', "../" + path).load_module()
+
+
+class TestInstance(unittest.TestCase):
+ def setUp(self):
+ self.testAccounts = script.AcmeAccounts("/dev/null")
+ pass
+
+ def test_read_path_from_environment(self):
+ self.assertEqual("/path/to/acmedns.json", script.get_acme_accounts_path())
+
+ def test_accounts_are_empty_on_init(self):
+ self.assertEqual({}, self.testAccounts.accounts)
+
+ def test_add(self):
+ self.testAccounts.add("foo.tld", {})
+ self.assertEqual(1, len(self.testAccounts.accounts))
+ self.assertIn("foo.tld", self.testAccounts.accounts)
+
+ def test_remove_existing(self):
+ self.testAccounts.add("foo.tld", {})
+ self.assertTrue(self.testAccounts.remove("foo.tld"))
+ self.assertEqual(0, len(self.testAccounts.accounts))
+
+ def test_remove_non_existing(self):
+ self.assertFalse(self.testAccounts.remove("not-existing.tld"))
+
+ def test_merge(self):
+ accounts_to_merge = script.AcmeAccounts("/dev/null") \
+ .add("bar.tld", {})
+
+ self.testAccounts \
+ .add("foo.tld", {}) \
+ .merge_with(accounts_to_merge)
+
+ self.assertEqual(2, len(self.testAccounts.accounts))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,3 +9,4 @@
# Tests
mock>=2.0.0,<3.0
salt==2019.2.2
+jsondiff==1.2.0
diff --git a/roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py b/roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py
new file mode 100755
--- /dev/null
+++ b/roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# -------------------------------------------------------------
+# Let's encrypt — ACME DNS server accounts editor
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Created: 2020-02-22
+# Description: Edit /srv/letsencrypt/etc/acmedns.json to import
+# credentials for a specific subdomain to verify.
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import json
+import os
+import sys
+
+
+def get_acme_accounts_path():
+ try:
+ return os.environ["ACME_ACCOUNTS"]
+ except KeyError:
+ return "/srv/letsencrypt/etc/acmedns.json"
+
+
+ACME_ACCOUNTS_PATH = get_acme_accounts_path()
+
+
+class AcmeAccounts:
+ def __init__(self, path):
+ self.path = path
+ self.accounts = {}
+
+ def read_from_file(self):
+ with open(self.path) as fd:
+ self.accounts = json.load(fd)
+
+ return self
+
+ def write_to_file(self):
+ with open(self.path, "w") as fd:
+ json.dump(self.accounts, fd)
+
+ return self
+
+ def add(self, domain, account_parameters):
+ self.accounts[domain] = account_parameters
+
+ return self
+
+ def remove(self, domain):
+ try:
+ del self.accounts[domain]
+ return True
+ except KeyError:
+ return False
+
+ def merge_with(self, other_accounts: 'AcmeAccounts'):
+ self.accounts.update(other_accounts.accounts)
+
+ return self
+
+
+def usage():
+ print(f"Usage: {sys.argv[0]} <command> [parameters]", file=sys.stderr)
+ exit(1)
+
+
+def import_other_file(file_to_import):
+ if file_to_import == ACME_ACCOUNTS_PATH:
+ print(f"You're trying to import {ACME_ACCOUNTS_PATH} to itself")
+ exit(2)
+
+ accounts_to_import = AcmeAccounts(file_to_import).read_from_file()
+
+ AcmeAccounts(ACME_ACCOUNTS_PATH)\
+ .read_from_file()\
+ .merge_with(accounts_to_import)\
+ .write_to_file()
+
+
+commands = {
+ "import": {
+ "required_argc": 3,
+ "command_usage": "import <file>",
+ "callable": import_other_file
+ },
+},
+
+
+if __name__ == "__main__":
+ argc = len(sys.argv)
+
+ if argc < 2 or sys.argv[1] in ["-h", "--help", "/?", "/help"]:
+ usage()
+
+ command = sys.argv[1]
+
+ if command not in commands:
+ print(f"Unknown command: {command}", file=sys.stderr)
+ usage()
+
+ command = commands[command]
+
+ if argc < command["required_argc"]:
+ print(f"Usage: {sys.argv[0]} {command['command_usage']}", file=sys.stderr)
+ exit(1)
+
+ # We're good, time to invoke our command
+ command["callable"](*sys.argv[2:])
diff --git a/roles/paas-docker/letsencrypt/init.sls b/roles/paas-docker/letsencrypt/init.sls
--- a/roles/paas-docker/letsencrypt/init.sls
+++ b/roles/paas-docker/letsencrypt/init.sls
@@ -48,3 +48,8 @@
file.managed:
- source: salt://roles/paas-docker/letsencrypt/files/acme-dns-auth.py
- mode: 755
+
+/usr/local/bin/edit-acme-dns-accounts:
+ file.managed:
+ - source: salt://roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py
+ - mode: 755
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 27, 10:14 (20 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2266716
Default Alt Text
D2249.diff (9 KB)
Attached To
Mode
D2249: Allow to update ACME DNS accounts
Attached
Detach File
Event Timeline
Log In to Comment