]> git.ipfire.org Git - dbl.git/commitdiff
dnsbl: Add a command to create a new list
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 5 Dec 2025 15:37:09 +0000 (15:37 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 5 Dec 2025 15:37:09 +0000 (15:37 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/dnsbl/__init__.py
src/dnsbl/lists.py [new file with mode: 0644]
src/dnsbl/util.py [new file with mode: 0644]
src/scripts/dnsbl.in

index 4d7a13011c2d846f9b3b2dae492cf8e30ac78ece..ddbfcb24c703777d7c78bdb487567ad7297c6f3b 100644 (file)
@@ -52,7 +52,9 @@ SED_PROCESS = \
 dist_pkgpython_PYTHON = \
        src/dnsbl/__init__.py \
        src/dnsbl/i18n.py \
-       src/dnsbl/logger.py
+       src/dnsbl/lists.py \
+       src/dnsbl/logger.py \
+       src/dnsbl/util.py
 
 # ------------------------------------------------------------------------------
 
index b5a0ea109bc1b1d75af836c0782434e752c6e31c..4b108be4167fae66f7b34b840438aa1bb744eeea 100644 (file)
@@ -19,6 +19,7 @@
 ###############################################################################
 
 import configparser
+import functools
 import logging
 import sqlmodel
 
@@ -28,6 +29,9 @@ from . import logger
 # Setup logging
 log = logging.getLogger(__name__)
 
+# Import sub-modules
+from . import lists
+
 class Backend(object):
        def __init__(self, config=None, debug=False):
                self.debug = debug
@@ -70,6 +74,16 @@ class Backend(object):
                        logging_name=log.name,
                )
 
+       def session(self):
+               """
+                       Returns a new database session
+               """
+               return sqlmodel.Session(self.db)
+
+       @functools.cached_property
+       def lists(self):
+               return lists.Lists(self)
+
        def update_sources(self):
                """
                        Updates all sources
diff --git a/src/dnsbl/lists.py b/src/dnsbl/lists.py
new file mode 100644 (file)
index 0000000..3f683d4
--- /dev/null
@@ -0,0 +1,107 @@
+###############################################################################
+#                                                                             #
+# dnsbl - A DNS Blacklist Compositor For IPFire                               #
+# Copyright (C) 2025 IPFire Development Team                                  #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+import datetime
+import sqlmodel
+
+from . import util
+
+class Lists(object):
+       def __init__(self, backend):
+               self.backend = backend
+
+       def get_by_slug(self, slug):
+               stmt = (
+                       sqlmodel
+                       .select(
+                               List,
+                       )
+                       .where(
+                               List.slug == slug,
+                               List.deleted_at == None,
+                       )
+               )
+
+               with self.backend.session() as session:
+                       result = session.execute(stmt)
+
+                       return result.scalar_one_or_none()
+
+       def _make_slug(self, name):
+               i = 0
+
+               while True:
+                       slug = util.slugify(name, i)
+
+                       # Skip if the list already exists
+                       if self.get_by_slug(slug):
+                               i += 1
+                               continue
+
+                       return slug
+
+       def create(self, name, created_by, license):
+               """
+                       Creates a new list
+               """
+               slug = self._make_slug(name)
+
+               # Create a new list
+               with self.backend.session() as session:
+                       list = List(
+                               name       = name,
+                               slug       = slug,
+                               created_by = created_by,
+                               license    = license,
+                       )
+                       session.add(list)
+                       session.commit()
+
+               return list
+
+
+class List(sqlmodel.SQLModel, table=True):
+       __tablename__ = "lists"
+
+       # ID
+       id : int = sqlmodel.Field(primary_key=True)
+
+       # Name
+       name : str
+
+       # Slug
+       slug : str
+
+       # Created At
+       created_at : datetime.datetime = sqlmodel.Field(
+               sa_column_kwargs = {"server_default" : sqlmodel.text("CURRENT_TIMESTAMP")}
+       )
+
+       # Created By
+       created_by : str
+
+       # Deleted At
+       deleted_at : datetime.datetime | None
+
+       # Deleted By
+       deleted_by : str | None
+
+       # License
+       license : str
diff --git a/src/dnsbl/util.py b/src/dnsbl/util.py
new file mode 100644 (file)
index 0000000..a35aa3c
--- /dev/null
@@ -0,0 +1,45 @@
+###############################################################################
+#                                                                             #
+# dnsbl - A DNS Blacklist Compositor For IPFire                               #
+# Copyright (C) 2025 IPFire Development Team                                  #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+import re
+import unicodedata
+
+def slugify(text, iteration=None):
+       # Append the iteration
+       if iteration:
+               text = "%s-%s" % (text, iteration)
+
+       # Normalize Unicode characters (e.g., é → e)
+       text = unicodedata.normalize("NFKD", text)
+       text = text.encode("ascii", "ignore").decode("ascii")
+
+       # Lowercase
+       text = text.lower()
+
+       # Replace non-alphanumeric characters with hyphens
+       text = re.sub(r"[^a-z0-9]+", "-", text)
+
+       # Remove leading/trailing hyphens
+       text = text.strip("-")
+
+       # Collapse multiple hyphens
+       text = re.sub(r"-+", "-", text)
+
+       return text
index 1c99f59c9b0e4e4b9f654ca0903ad21c9a43ba44..ef4556d5aab6b9a25239e655cd6c3c74e680a428 100644 (file)
@@ -22,6 +22,7 @@
 import argparse
 import dnsbl
 import logging
+import os
 import sys
 
 # i18n
@@ -45,6 +46,16 @@ class CLI(object):
                # Show Version
                parser.add_argument("--version", action="version", version="%(prog)s @VERSION@")
 
+               # create-list
+               create_list = subparsers.add_parser("create-list", help=_("Create a new list"))
+               create_list.add_argument("--name", required=True,
+                               help=_("The name of the list"))
+               create_list.add_argument("--created-by", required=True,
+                               default=os.environ.get("USER"), help=_("The creator of the list"))
+               create_list.add_argument("--license", required=True,
+                               help=_("The license of the list"))
+               create_list.set_defaults(func=self.__create_list)
+
                # update
                update = subparsers.add_parser("update", help=_("Update sources"))
                update.set_defaults(func=self.__update)
@@ -83,6 +94,16 @@ class CLI(object):
                # Otherwise just exit
                sys.exit(0)
 
+       def __create_list(self, backend, args):
+               """
+                       Creates a new list
+               """
+               backend.lists.create(
+                       name       = args.name,
+                       created_by = args.created_by,
+                       license    = args.license,
+               )
+
        def __update(self, backend, args):
                """
                        Updates all sources