Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7efdd60643 | |||
| e13b5e97bd | |||
| d6a02cf945 | |||
| 8660d0ce75 |
10 changed files with 202 additions and 5 deletions
50
Dockerfile
Normal file
50
Dockerfile
Normal 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"
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ USE_TZ = True
|
|||
|
||||
STATIC_URL = '/static/'
|
||||
STATICFILES_DIRS = [ ]
|
||||
STATIC_ROOT = "/home/app/app/static"
|
||||
STATIC_ROOT = "/app/static"
|
||||
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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("/")
|
||||
|
|
|
|||
|
|
@ -2,3 +2,5 @@ django==3.1.4
|
|||
django-auth-ldap
|
||||
psycopg2
|
||||
django-crispy-forms
|
||||
setuptools
|
||||
uwsgi
|
||||
|
|
|
|||
12
uwsgiconfig/app.ini
Normal file
12
uwsgiconfig/app.ini
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue