Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
kjg
7efdd60643 [ipv6ula] update Docker image 2025-06-04 05:03:39 +00:00
kjg
e13b5e97bd [IPv6ULA] update the delete functon 2025-06-01 13:16:58 +00:00
kjg
d6a02cf945 [IPv6ULA] add a delete functon 2025-06-01 13:11:14 +00:00
kjg
8660d0ce75 [IPv6ULA] add the update function 2025-06-01 10:45:08 +00:00
10 changed files with 202 additions and 5 deletions

50
Dockerfile Normal file
View file

@ -0,0 +1,50 @@
FROM docker.io/python:3.12.10-alpine3.21
ENV \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
# python:
PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PYTHONDONTWRITEBYTECODE=1
RUN apk add --update --no-cache zlib-dev libxml2-dev libxslt-dev libmemcached-dev \
build-base gcc make alpine-sdk musl-dev postgresql-dev openldap-dev \
py3-distutils-extra py3-distutils-extra-pyc shadow python3-dev linux-headers pcre-dev
ENV VIRTUAL_ENV=/venv
ENV DJANGO_CONFIGURATION=production
ENV \
# pip:
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100
RUN pip install --upgrade pip wheel
RUN python3 -m venv $VIRTUAL_ENV
COPY requirements.txt .
RUN . /venv/bin/activate && pip install -r requirements.txt
RUN mkdir -p /app
COPY ./ /app/
WORKDIR /app
RUN . /venv/bin/activate && \
DJANGO_SECRET_KEY=dummy \
DJANGO_STATIC_ROOT=/app/static ./manage.py collectstatic --no-input
RUN groupadd --gid 1001 app \
&& useradd --home-dir /app --shell /bin/bash --gid app --uid 1001 app
RUN chown -R app:app ./
USER app
ENV PATH="/venv/bin:$PATH"

View file

@ -1,6 +1,8 @@
from django import forms
import ipaddress
from .models import validate_ula_prefix, ULA
from django.core.exceptions import ValidationError
import logging
class ULAForm(forms.ModelForm):
class Meta:
@ -17,3 +19,67 @@ class ULAGenerateForm(forms.ModelForm):
class Meta:
model = ULA
fields = [ 'name', 'organization', 'website' ]
class ULAUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.owner = kwargs.pop('owner', None)
super().__init__(*args, **kwargs)
class Meta:
model = ULA
fields = [ 'prefix', 'name', 'organization', 'website' ]
def clean_prefix(self):
prefix = self.cleaned_data.get("prefix")
if not prefix or not self.owner:
return prefix
net_str = f"{prefix}/48"
net = ipaddress.IPv6Network(net_str, strict=False)
normalized_prefix = str(net[0])
self.cleaned_data['prefix'] = normalized_prefix
qs = ULA.objects.filter(prefix=normalized_prefix)
if self.instance.pk:
qs = qs.exclude(pk=self.instance.pk)
if qs.exists():
existing = qs.first()
if existing.owner != self.owner:
raise ValidationError("This prefix is already registered by another user.")
else:
self.instance = existing
return normalized_prefix
class ULADeleteForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.owner = kwargs.pop('owner', None)
super().__init__(*args, **kwargs)
class Meta:
model = ULA
fields = [ 'prefix' ]
def clean_prefix(self):
prefix = self.cleaned_data['prefix']
if not prefix or not self.owner:
return prefix
net_str = f"{prefix}/48"
net = ipaddress.IPv6Network(net_str, strict=False)
normalized_prefix = str(net[0])
self.cleaned_data['prefix'] = normalized_prefix
try:
self.instance = ULA.objects.get(prefix=normalized_prefix, owner=self.owner)
except ULA.DoesNotExist:
raise ValidationError("No ULA with this prefix was found for the current user.")
return normalized_prefix
def delete(self):
if self.instance:
print(f"Deleting: {self.instance}")
self.instance.delete()

View file

@ -31,7 +31,6 @@ def validate_ula_prefix(prefix):
params = {'prefix': net }
)
class User(AbstractUser):
pass
@ -41,7 +40,7 @@ class ULA(models.Model):
on_delete=models.CASCADE
)
prefix = models.GenericIPAddressField(protocol='IPv6', unique=True, validators=[validate_ula_prefix])
prefix = models.GenericIPAddressField(protocol='IPv6', unique=True)
name = models.CharField(max_length=256)
organization = models.CharField(max_length=256)
website = models.URLField()
@ -54,7 +53,6 @@ class ULA(models.Model):
net_str = f"{self.prefix}/48"
net = ipaddress.IPv6Network(net_str, strict=False)
self.prefix = str(net[0])
super().save(*args, **kwargs)
def __str__(self):

View file

@ -140,7 +140,7 @@ USE_TZ = True
STATIC_URL = '/static/'
STATICFILES_DIRS = [ ]
STATIC_ROOT = "/home/app/app/static"
STATIC_ROOT = "/app/static"
AUTHENTICATION_BACKENDS = [

View file

@ -33,6 +33,14 @@
<a class="nav-link" href="/submit/">Submit existing
prefix</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/update/">Update existing
prefix</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/delete/">Delete existing
prefix</a>
</li>
{% if user.is_authenticated %}
<span class="navbar-text">Logged in as {{ user }}.</span>
<li class="nav-item">

View file

@ -20,6 +20,16 @@
<p>
A new random prefix will be generated for you.
</p>
{% elif method == 'update' %}
<h1>Update existing prefix</h1>
<p>
You update your existing prefix.
</p>
{% elif method == 'delete' %}
<h1>Delete existing prefix</h1>
<p>
You delete your existing prefix.
</p>
{% endif %}
<p>

View file

@ -28,4 +28,6 @@ urlpatterns = [
path('login/', views.LoginView.as_view(), name='login'),
path('logout/', views.logout_view, name='logout'),
path('admin/', admin.site.urls),
path('update/', views.UpdateView.as_view(), name='update'),
path('delete/', views.DeleteView.as_view(), name='delete'),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

View file

@ -10,6 +10,9 @@ from django.contrib.auth import logout
from django.shortcuts import redirect
import ipaddress
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from django.contrib.messages import success
from django.conf import settings
@ -73,6 +76,52 @@ class SubmitView(GenerateSubmitView):
class LoginView(auth_views.LoginView):
template_name = 'ipv6ula/login.html'
class UpdateView(GenerateSubmitView):
form_class = ULAUpdateForm
gen_method = "update"
success_message = "%(the_prefix)s/48 was updated successfully"
def get_success_message(self, cleaned_data):
return self.success_message % {
"the_prefix": cleaned_data.get("prefix", "(unknown)")
}
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['owner'] = self.request.user
return kwargs
def form_valid(self, form):
prefix = form.cleaned_data["prefix"]
form.instance.owner = self.request.user
if not ULA.objects.filter(prefix=prefix, owner=self.request.user).exists():
form.add_error('prefix', 'This prefix is not yours')
return self.form_invalid(form)
return super().form_valid(form)
class DeleteView(GenerateSubmitView):
form_class = ULADeleteForm
gen_method = "delete"
success_message = "%(the_prefix)s/48 was deleted successfully"
success_url = reverse_lazy("index")
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['owner'] = self.request.user
return kwargs
def form_valid(self, form):
prefix = form.cleaned_data["prefix"]
try:
instance = ULA.objects.get(prefix=prefix, owner=self.request.user)
instance.delete()
self.object = instance
except ULA.DoesNotExist:
form.add_error("prefix", "The prefix does not exist or does not belong to you.")
return self.form_invalid(form)
success(self.request, self.get_success_message(form.cleaned_data))
return HttpResponseRedirect(self.get_success_url())
def logout_view(request):
logout(request)
return redirect("/")

View file

@ -2,3 +2,5 @@ django==3.1.4
django-auth-ldap
psycopg2
django-crispy-forms
setuptools
uwsgi

12
uwsgiconfig/app.ini Normal file
View file

@ -0,0 +1,12 @@
[uwsgi]
socket = /app/uwsgi/uwsgi.sock
chdir = /app
venv = /venv
wsgi-file = ipv6ula/wsgi.py
processes = 4
threads = 2
chmod-socket = 666
vacuum = true
plugins = python3
uid = app
gid = app