Compare commits
17 Commits
6e2526f54e
...
5593eec2d8
Author | SHA1 | Date | |
---|---|---|---|
5593eec2d8 | |||
4f96334f1a | |||
a5b8a9d0df | |||
8386d22a01 | |||
3e178b19f9 | |||
1c435d5c1e | |||
e1b3eb95ae | |||
a9883d69d2 | |||
e6cd6f5d10 | |||
51dfb75e3b | |||
8b63bd909e | |||
89c63b2275 | |||
25e6c828c0 | |||
c85f6ccc36 | |||
abc9c5a8d1 | |||
b4a38d973d | |||
8a1c410ebc |
14
.pylintrc
Normal file
14
.pylintrc
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[MAIN]
|
||||||
|
# In error mode, messages with a category besides ERROR or FATAL are
|
||||||
|
# suppressed, and no reports are done by default. Error mode is compatible with
|
||||||
|
# disabling specific errors.
|
||||||
|
errors-only=yes
|
||||||
|
|
||||||
|
# List of plugins (as comma separated values of python module names) to load,
|
||||||
|
# usually to register additional checkers.
|
||||||
|
load-plugins=pylint_django
|
||||||
|
|
||||||
|
django-settings-module=nsupdate.settings.dev
|
||||||
|
|
||||||
|
[MASTER]
|
||||||
|
init-hook='import sys; sys.path.append(".")'
|
27
DEVELOPMENT.md
Normal file
27
DEVELOPMENT.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Build
|
||||||
|
|
||||||
|
1. Install `build` for example via `pacman -S python-build` on ArchLinux
|
||||||
|
|
||||||
|
2. Afterwards run the command to generate pip packgases in `dist/`: `pyproject-build`
|
||||||
|
|
||||||
|
This is also needed before development because the command generates `./src/nsupdate/_version.py`.
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
|
||||||
|
Tests need to run inside Docker because they depend on bind9 config.
|
||||||
|
|
||||||
|
1. Build the docker image using: `docker build -t nsupdate scripts/docker/` once
|
||||||
|
|
||||||
|
2. Then run tests via `docker run --dns 127.0.0.1 -v $PWD:/app nsupdate`
|
||||||
|
|
||||||
|
# Lint
|
||||||
|
|
||||||
|
Run pylint in error-only mode to check any problems: `pipenv run pylint src/nsupdate`
|
||||||
|
|
||||||
|
# Test locally
|
||||||
|
|
||||||
|
1. Create database using `pipenv run python ./manage.py migrate`
|
||||||
|
|
||||||
|
2. Create a superuser with `pipenv run python ./manage.py createsuperuser`
|
||||||
|
|
||||||
|
2. Run the server with `pipenv run python ./manage.py runserver`
|
28
Pipfile
Normal file
28
Pipfile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
dnspython = "*"
|
||||||
|
netaddr = "*"
|
||||||
|
django = "*"
|
||||||
|
django-bootstrap-form = "*"
|
||||||
|
django-referrer-policy = "*"
|
||||||
|
django-registration-redux = "*"
|
||||||
|
django-extensions = "*"
|
||||||
|
social-auth-app-django = "*"
|
||||||
|
requests = "*"
|
||||||
|
setuptools-scm = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
django-debug-toolbar = "*"
|
||||||
|
pytest = ">=3.6"
|
||||||
|
pytest-django = "*"
|
||||||
|
pytest-pep8 = "*"
|
||||||
|
sphinx = "*"
|
||||||
|
pylint = "*"
|
||||||
|
pylint-django = "*"
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.10"
|
1022
Pipfile.lock
generated
Normal file
1022
Pipfile.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
renovate.json
Normal file
3
renovate.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
# packages always needed
|
# packages always needed
|
||||||
dnspython
|
dnspython
|
||||||
netaddr
|
netaddr
|
||||||
django~=2.2.0
|
django
|
||||||
django-bootstrap-form
|
django-bootstrap-form
|
||||||
django-referrer-policy
|
django-referrer-policy
|
||||||
django-registration-redux
|
django-registration-redux
|
||||||
|
@ -4,4 +4,6 @@ django-debug-toolbar
|
|||||||
pytest>=3.6
|
pytest>=3.6
|
||||||
pytest-django
|
pytest-django
|
||||||
pytest-pep8
|
pytest-pep8
|
||||||
|
pylint
|
||||||
|
pylint-django
|
||||||
Sphinx
|
Sphinx
|
||||||
|
9
scripts/docker/Dockerfile
Normal file
9
scripts/docker/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
FROM python:3.11-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apk add bind git
|
||||||
|
COPY bind/named.conf.local /etc/bind/named.conf.local
|
||||||
|
COPY bind/zones/ /var/lib/bind/pri/
|
||||||
|
RUN chown named -R /var/lib/bind/pri/
|
||||||
|
|
||||||
|
CMD /app/scripts/docker/test.sh
|
49
scripts/docker/bind/named.conf.local
Normal file
49
scripts/docker/bind/named.conf.local
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// Do any local configuration here
|
||||||
|
//
|
||||||
|
|
||||||
|
key "nsupdate.info." {
|
||||||
|
algorithm hmac-sha512;
|
||||||
|
secret "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==";
|
||||||
|
};
|
||||||
|
|
||||||
|
key "tests.nsupdate.info." {
|
||||||
|
algorithm hmac-sha512;
|
||||||
|
secret "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==";
|
||||||
|
};
|
||||||
|
|
||||||
|
zone "nsupdate.info" {
|
||||||
|
type master;
|
||||||
|
file "/var/lib/bind/pri/nsupdate.info";
|
||||||
|
update-policy {
|
||||||
|
// these "deny" entries are needed for the service domain,
|
||||||
|
// if you add another domain, you may want to check the need
|
||||||
|
// for other "deny" entries if the zone is not fully available.
|
||||||
|
// we don't allow updates to the infrastructure hosts:
|
||||||
|
deny nsupdate.info. name nsupdate.info;
|
||||||
|
deny nsupdate.info. name www.nsupdate.info;
|
||||||
|
deny nsupdate.info. name ipv4.nsupdate.info;
|
||||||
|
deny nsupdate.info. name ipv6.nsupdate.info;
|
||||||
|
// this host is for testing if the nameserver is configured correctly and reachable
|
||||||
|
grant nsupdate.info. name connectivity-test.nsupdate.info A;
|
||||||
|
// but we allow updates to any other host:
|
||||||
|
grant nsupdate.info. subdomain nsupdate.info;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
zone "tests.nsupdate.info" {
|
||||||
|
type master;
|
||||||
|
file "/var/lib/bind/pri/tests.nsupdate.info";
|
||||||
|
update-policy {
|
||||||
|
// these "deny" entries are needed for the service domain,
|
||||||
|
// if you add another domain, you may want to check the need
|
||||||
|
// for other "deny" entries if the zone is not fully available.
|
||||||
|
// we don't allow updates to the infrastructure hosts:
|
||||||
|
deny tests.nsupdate.info. name tests.nsupdate.info;
|
||||||
|
deny tests.nsupdate.info. name www.tests.nsupdate.info;
|
||||||
|
deny tests.nsupdate.info. name ipv4.tests.nsupdate.info;
|
||||||
|
deny tests.nsupdate.info. name ipv6.tests.nsupdate.info;
|
||||||
|
// but we allow updates to any other host:
|
||||||
|
grant tests.nsupdate.info. subdomain tests.nsupdate.info;
|
||||||
|
};
|
||||||
|
};
|
20
scripts/docker/bind/zones/nsupdate.info
Normal file
20
scripts/docker/bind/zones/nsupdate.info
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
$ORIGIN .
|
||||||
|
$TTL 3600 ; 1 hour
|
||||||
|
nsupdate.info IN SOA ns1.nsupdate.info. root.nsupdate.info. (
|
||||||
|
2016081401 ; serial
|
||||||
|
7200 ; refresh (2 hours)
|
||||||
|
1800 ; retry (30 minutes)
|
||||||
|
604800 ; expire (1 week)
|
||||||
|
60 ; minimum (1 minute)
|
||||||
|
)
|
||||||
|
NS 127.0.0.1.
|
||||||
|
A 127.0.0.1
|
||||||
|
AAAA ::1
|
||||||
|
|
||||||
|
$ORIGIN nsupdate.info.
|
||||||
|
$TTL 3600 ; 1 hour
|
||||||
|
ipv4 A 127.0.0.1
|
||||||
|
ipv6 AAAA ::1
|
||||||
|
www A 127.0.0.1
|
||||||
|
AAAA ::1
|
||||||
|
A 127.0.0.1
|
18
scripts/docker/bind/zones/tests.nsupdate.info
Normal file
18
scripts/docker/bind/zones/tests.nsupdate.info
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
$ORIGIN .
|
||||||
|
$TTL 3600 ; 1 hour
|
||||||
|
tests.nsupdate.info IN SOA ns1.tests.nsupdate.info. root.tests.nsupdate.info. (
|
||||||
|
2016081401 ; serial
|
||||||
|
7200 ; refresh (2 hours)
|
||||||
|
1800 ; retry (30 minutes)
|
||||||
|
604800 ; expire (1 week)
|
||||||
|
60 ; minimum (1 minute)
|
||||||
|
)
|
||||||
|
NS 127.0.0.1.
|
||||||
|
A 127.0.0.1
|
||||||
|
AAAA ::1
|
||||||
|
|
||||||
|
$ORIGIN tests.nsupdate.info.
|
||||||
|
ipv4 A 1.2.3.4
|
||||||
|
ipv6 AAAA ::1
|
||||||
|
www A 1.2.3.4
|
||||||
|
AAAA ::1
|
12
scripts/docker/test.sh
Executable file
12
scripts/docker/test.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
cd /tmp && named -g -u named -c /etc/bind/named.conf.local &
|
||||||
|
|
||||||
|
cd /app
|
||||||
|
pip install -e .
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
pytest src/nsupdate || true
|
||||||
|
pylint src/nsupdate || true
|
2
setup.py
2
setup.py
@ -31,7 +31,7 @@ setup(
|
|||||||
install_requires=[
|
install_requires=[
|
||||||
'dnspython',
|
'dnspython',
|
||||||
'netaddr',
|
'netaddr',
|
||||||
'django>=2.2.0',
|
'django',
|
||||||
'django-bootstrap-form',
|
'django-bootstrap-form',
|
||||||
'django-referrer-policy',
|
'django-referrer-policy',
|
||||||
'django-registration-redux',
|
'django-registration-redux',
|
||||||
|
@ -7,13 +7,10 @@ from django.conf import settings
|
|||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.contrib.auth.signals import user_logged_in
|
from django.contrib.auth.signals import user_logged_in
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.six import text_type
|
|
||||||
|
|
||||||
|
LANGUAGE_SESSION_KEY = "_language"
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class UserProfile(models.Model):
|
class UserProfile(models.Model):
|
||||||
"""
|
"""
|
||||||
stuff we need additionally to what Django stores in User model
|
stuff we need additionally to what Django stores in User model
|
||||||
@ -25,7 +22,7 @@ class UserProfile(models.Model):
|
|||||||
verbose_name=_('language'))
|
verbose_name=_('language'))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"profile for %s" % text_type(self.user)
|
return u"profile for %s" % str(self.user)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('user profile')
|
verbose_name = _('user profile')
|
||||||
|
@ -7,7 +7,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from registration.forms import RegistrationForm
|
from registration.forms import RegistrationForm
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.conf.urls import url
|
from django.urls import re_path
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
|
||||||
from registration.backends.default.views import ActivationView
|
from registration.backends.default.views import ActivationView
|
||||||
@ -8,12 +8,12 @@ from .views import UserProfileView, DeleteUserView, UserChangePasswordView
|
|||||||
|
|
||||||
|
|
||||||
urlpatterns = (
|
urlpatterns = (
|
||||||
url(r'^profile/', UserProfileView.as_view(), name="account_profile"),
|
re_path(r'^profile/', UserProfileView.as_view(), name="account_profile"),
|
||||||
url(r'^settings/', UserChangePasswordView.as_view(), name='account_settings'),
|
re_path(r'^settings/', UserChangePasswordView.as_view(), name='account_settings'),
|
||||||
url(r'^delete/', DeleteUserView.as_view(), name="account_delete"),
|
re_path(r'^delete/', DeleteUserView.as_view(), name="account_delete"),
|
||||||
|
|
||||||
# registration start
|
# registration start
|
||||||
url(r'^activate/complete/$',
|
re_path(r'^activate/complete/$',
|
||||||
TemplateView.as_view(template_name='registration/activation_complete.html'),
|
TemplateView.as_view(template_name='registration/activation_complete.html'),
|
||||||
name='registration_activation_complete'),
|
name='registration_activation_complete'),
|
||||||
# Activation keys get matched by \w+ instead of the more specific
|
# Activation keys get matched by \w+ instead of the more specific
|
||||||
@ -21,16 +21,16 @@ urlpatterns = (
|
|||||||
# that way it can return a sensible "invalid key" message instead of a
|
# that way it can return a sensible "invalid key" message instead of a
|
||||||
# confusing 404.
|
# confusing 404.
|
||||||
|
|
||||||
url(r'^activate/(?P<activation_key>\w+)/$',
|
re_path(r'^activate/(?P<activation_key>\w+)/$',
|
||||||
ActivationView.as_view(),
|
ActivationView.as_view(),
|
||||||
name='registration_activate'),
|
name='registration_activate'),
|
||||||
url(r'^register/$',
|
re_path(r'^register/$',
|
||||||
RegistrationView.as_view(),
|
RegistrationView.as_view(),
|
||||||
name='registration_register'),
|
name='registration_register'),
|
||||||
url(r'^register/complete/$',
|
re_path(r'^register/complete/$',
|
||||||
TemplateView.as_view(template_name='registration/registration_complete.html'),
|
TemplateView.as_view(template_name='registration/registration_complete.html'),
|
||||||
name='registration_complete'),
|
name='registration_complete'),
|
||||||
url(r'^register/closed/$',
|
re_path(r'^register/closed/$',
|
||||||
TemplateView.as_view(template_name='registration/registration_closed.html'),
|
TemplateView.as_view(template_name='registration/registration_closed.html'),
|
||||||
name='registration_disallowed'),
|
name='registration_disallowed'),
|
||||||
# registration end
|
# registration end
|
||||||
|
@ -10,7 +10,7 @@ from django.views.decorators.debug import sensitive_post_parameters
|
|||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.auth import update_session_auth_hash
|
from django.contrib.auth import update_session_auth_hash
|
||||||
|
|
||||||
from .forms import UserForm, UserProfileForm
|
from .forms import UserForm, UserProfileForm
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.conf.urls import url
|
from django.urls import re_path
|
||||||
from django.contrib.auth.views import LoginView, LogoutView, \
|
from django.contrib.auth.views import LoginView, LogoutView, \
|
||||||
PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteView
|
PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteView
|
||||||
|
|
||||||
urlpatterns = (
|
urlpatterns = (
|
||||||
# login and logout url
|
# login and logout url
|
||||||
url(r'^login/$', LoginView.as_view(template_name='login.html'), name='login'),
|
re_path(r'^login/$', LoginView.as_view(template_name='login.html'), name='login'),
|
||||||
# or use logout with template 'logout.html'
|
# or use logout with template 'logout.html'
|
||||||
url(r'^logout/$', LogoutView.as_view(), name='logout'),
|
re_path(r'^logout/$', LogoutView.as_view(), name='logout'),
|
||||||
|
|
||||||
# password reset urls
|
# password reset urls
|
||||||
url(r'^password_reset/$',
|
re_path(r'^password_reset/$',
|
||||||
PasswordResetView.as_view(template_name='password_reset.html'),
|
PasswordResetView.as_view(template_name='password_reset.html'),
|
||||||
name='password_reset'),
|
name='password_reset'),
|
||||||
url(r'^password_reset_done/$',
|
re_path(r'^password_reset_done/$',
|
||||||
PasswordResetDoneView.as_view(template_name='password_reset_done.html'),
|
PasswordResetDoneView.as_view(template_name='password_reset_done.html'),
|
||||||
name='password_reset_done'),
|
name='password_reset_done'),
|
||||||
url(r'^password_reset_confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
|
re_path(r'^password_reset_confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
|
||||||
PasswordResetConfirmView.as_view(template_name='password_reset_confirm.html'),
|
PasswordResetConfirmView.as_view(template_name='password_reset_confirm.html'),
|
||||||
name='password_reset_confirm'),
|
name='password_reset_confirm'),
|
||||||
url(r'^password_reset_complete/$',
|
re_path(r'^password_reset_complete/$',
|
||||||
PasswordResetCompleteView.as_view(template_name='password_reset_complete.html'),
|
PasswordResetCompleteView.as_view(template_name='password_reset_complete.html'),
|
||||||
name='password_reset_complete'),
|
name='password_reset_complete'),
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ form definitions (which fields are available, order, autofocus, ...)
|
|||||||
import binascii
|
import binascii
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .models import Host, RelatedHost, Domain, ServiceUpdaterHostConfig
|
from .models import Host, RelatedHost, Domain, ServiceUpdaterHostConfig
|
||||||
from .dnstools import check_domain, NameServerNotAvailable
|
from .dnstools import check_domain, NameServerNotAvailable
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 4.1.5 on 2023-03-07 16:47
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("main", "0012_auto_20191230_1729"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="host",
|
||||||
|
name="update_secret",
|
||||||
|
field=models.CharField(max_length=128, verbose_name="update secret"),
|
||||||
|
),
|
||||||
|
]
|
@ -17,9 +17,7 @@ from django.conf import settings
|
|||||||
from django.db.models.signals import pre_delete, post_save
|
from django.db.models.signals import pre_delete, post_save
|
||||||
from django.contrib.auth.hashers import make_password
|
from django.contrib.auth.hashers import make_password
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.six import text_type
|
|
||||||
|
|
||||||
from . import dnstools
|
from . import dnstools
|
||||||
|
|
||||||
@ -34,7 +32,6 @@ def result_fmt(msg):
|
|||||||
return msg[:RESULT_MSG_LEN]
|
return msg[:RESULT_MSG_LEN]
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class BlacklistedHost(models.Model):
|
class BlacklistedHost(models.Model):
|
||||||
name_re = models.CharField(
|
name_re = models.CharField(
|
||||||
_('name RegEx'),
|
_('name RegEx'),
|
||||||
@ -81,7 +78,6 @@ UPDATE_ALGORITHMS = {
|
|||||||
UPDATE_ALGORITHM_CHOICES = [(k, k) for k in UPDATE_ALGORITHMS]
|
UPDATE_ALGORITHM_CHOICES = [(k, k) for k in UPDATE_ALGORITHMS]
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Domain(models.Model):
|
class Domain(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
_("name"),
|
_("name"),
|
||||||
@ -154,7 +150,6 @@ class Domain(models.Model):
|
|||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Host(models.Model):
|
class Host(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
_("name"),
|
_("name"),
|
||||||
@ -170,7 +165,7 @@ class Host(models.Model):
|
|||||||
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, verbose_name=_("domain"))
|
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, verbose_name=_("domain"))
|
||||||
update_secret = models.CharField(
|
update_secret = models.CharField(
|
||||||
_("update secret"),
|
_("update secret"),
|
||||||
max_length=64, # secret gets hashed (on save) to salted sha1, 58 bytes str len
|
max_length=128, # secret gets hashed (on save) to salted sha1, "sha1" + "$" + 22 chars salt + "$" + 40 chars sha1 = 68 chars
|
||||||
)
|
)
|
||||||
comment = models.CharField(
|
comment = models.CharField(
|
||||||
_("comment"),
|
_("comment"),
|
||||||
@ -373,7 +368,6 @@ def post_save_host(sender, **kwargs):
|
|||||||
post_save.connect(post_save_host, sender=Host)
|
post_save.connect(post_save_host, sender=Host)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class RelatedHost(models.Model):
|
class RelatedHost(models.Model):
|
||||||
# host addr = network_of_main_host + interface_id
|
# host addr = network_of_main_host + interface_id
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
@ -414,7 +408,7 @@ class RelatedHost(models.Model):
|
|||||||
verbose_name=_("main host"))
|
verbose_name=_("main host"))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"%s.%s" % (self.name, text_type(self.main_host))
|
return u"%s.%s" % (self.name, str(self.main_host))
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
unique_together = (('name', 'main_host'),)
|
unique_together = (('name', 'main_host'),)
|
||||||
@ -447,7 +441,6 @@ class RelatedHost(models.Model):
|
|||||||
pre_delete.connect(pre_delete_host, sender=RelatedHost)
|
pre_delete.connect(pre_delete_host, sender=RelatedHost)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class ServiceUpdater(models.Model):
|
class ServiceUpdater(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
_("name"),
|
_("name"),
|
||||||
@ -491,7 +484,6 @@ class ServiceUpdater(models.Model):
|
|||||||
verbose_name_plural = _('service updaters')
|
verbose_name_plural = _('service updaters')
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class ServiceUpdaterHostConfig(models.Model):
|
class ServiceUpdaterHostConfig(models.Model):
|
||||||
service = models.ForeignKey(ServiceUpdater, on_delete=models.CASCADE, verbose_name=_("service"))
|
service = models.ForeignKey(ServiceUpdater, on_delete=models.CASCADE, verbose_name=_("service"))
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
main app url dispatching
|
main app url dispatching
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import re_path
|
||||||
|
|
||||||
from .views import (
|
from .views import (
|
||||||
HomeView, OverviewView, HostView, AddHostView, DeleteHostView, AboutView, GenerateSecretView, GenerateNSSecretView,
|
HomeView, OverviewView, HostView, AddHostView, DeleteHostView, AboutView, GenerateSecretView, GenerateNSSecretView,
|
||||||
@ -16,39 +16,39 @@ from ..api.views import (
|
|||||||
|
|
||||||
urlpatterns = (
|
urlpatterns = (
|
||||||
# interactive web ui
|
# interactive web ui
|
||||||
url(r'^$', HomeView.as_view(), name="home"),
|
re_path(r'^$', HomeView.as_view(), name="home"),
|
||||||
url(r'^about/$', AboutView.as_view(), name="about"),
|
re_path(r'^about/$', AboutView.as_view(), name="about"),
|
||||||
url(r'^custom/(?P<template>[\w.]+)$', CustomTemplateView.as_view(), name="custom"),
|
re_path(r'^custom/(?P<template>[\w.]+)$', CustomTemplateView.as_view(), name="custom"),
|
||||||
url(r'^update$', JsUpdateView.as_view(), name='update'),
|
re_path(r'^update$', JsUpdateView.as_view(), name='update'),
|
||||||
url(r'^overview/$', OverviewView.as_view(), name='overview'),
|
re_path(r'^overview/$', OverviewView.as_view(), name='overview'),
|
||||||
url(r'^status/$', StatusView.as_view(), name='status'),
|
re_path(r'^status/$', StatusView.as_view(), name='status'),
|
||||||
url(r'^generate_secret/(?P<pk>\d+)/$', GenerateSecretView.as_view(), name='generate_secret_view'),
|
re_path(r'^generate_secret/(?P<pk>\d+)/$', GenerateSecretView.as_view(), name='generate_secret_view'),
|
||||||
url(r'^generate_ns_secret/(?P<pk>\d+)/$', GenerateNSSecretView.as_view(), name='generate_ns_secret_view'),
|
re_path(r'^generate_ns_secret/(?P<pk>\d+)/$', GenerateNSSecretView.as_view(), name='generate_ns_secret_view'),
|
||||||
url(r'^host/(?P<pk>\d+)/$', HostView.as_view(), name='host_view'),
|
re_path(r'^host/(?P<pk>\d+)/$', HostView.as_view(), name='host_view'),
|
||||||
url(r'^host/add/$', AddHostView.as_view(), name='add_host'),
|
re_path(r'^host/add/$', AddHostView.as_view(), name='add_host'),
|
||||||
url(r'^host/(?P<pk>\d+)/delete/$', DeleteHostView.as_view(), name='delete_host'),
|
re_path(r'^host/(?P<pk>\d+)/delete/$', DeleteHostView.as_view(), name='delete_host'),
|
||||||
url(r'^host/(?P<mpk>\d+)/related/$', RelatedHostOverviewView.as_view(), name='related_host_overview'),
|
re_path(r'^host/(?P<mpk>\d+)/related/$', RelatedHostOverviewView.as_view(), name='related_host_overview'),
|
||||||
url(r'^host/(?P<mpk>\d+)/related/(?P<pk>\d+)/$', RelatedHostView.as_view(), name='related_host_view'),
|
re_path(r'^host/(?P<mpk>\d+)/related/(?P<pk>\d+)/$', RelatedHostView.as_view(), name='related_host_view'),
|
||||||
url(r'^host/(?P<mpk>\d+)/related/add/$', AddRelatedHostView.as_view(), name='add_related_host'),
|
re_path(r'^host/(?P<mpk>\d+)/related/add/$', AddRelatedHostView.as_view(), name='add_related_host'),
|
||||||
url(r'^host/(?P<mpk>\d+)/related/(?P<pk>\d+)/delete/$', DeleteRelatedHostView.as_view(),
|
re_path(r'^host/(?P<mpk>\d+)/related/(?P<pk>\d+)/delete/$', DeleteRelatedHostView.as_view(),
|
||||||
name='delete_related_host'),
|
name='delete_related_host'),
|
||||||
url(r'^domain/(?P<pk>\d+)/$', DomainView.as_view(), name='domain_view'),
|
re_path(r'^domain/(?P<pk>\d+)/$', DomainView.as_view(), name='domain_view'),
|
||||||
url(r'^domain/add/$', AddDomainView.as_view(), name='add_domain'),
|
re_path(r'^domain/add/$', AddDomainView.as_view(), name='add_domain'),
|
||||||
url(r'^domain/(?P<pk>\d+)/delete/$', DeleteDomainView.as_view(), name='delete_domain'),
|
re_path(r'^domain/(?P<pk>\d+)/delete/$', DeleteDomainView.as_view(), name='delete_domain'),
|
||||||
url(r'^updater_hostconfig_overview/(?P<pk>\d+)/$', UpdaterHostConfigOverviewView.as_view(),
|
re_path(r'^updater_hostconfig_overview/(?P<pk>\d+)/$', UpdaterHostConfigOverviewView.as_view(),
|
||||||
name='updater_hostconfig_overview'),
|
name='updater_hostconfig_overview'),
|
||||||
url(r'^updater_hostconfig/(?P<pk>\d+)/$', UpdaterHostConfigView.as_view(), name='updater_hostconfig'),
|
re_path(r'^updater_hostconfig/(?P<pk>\d+)/$', UpdaterHostConfigView.as_view(), name='updater_hostconfig'),
|
||||||
url(r'^updater_hostconfig/(?P<pk>\d+)/delete/$', DeleteUpdaterHostConfigView.as_view(),
|
re_path(r'^updater_hostconfig/(?P<pk>\d+)/delete/$', DeleteUpdaterHostConfigView.as_view(),
|
||||||
name='delete_updater_hostconfig'),
|
name='delete_updater_hostconfig'),
|
||||||
# internal use by the web ui
|
# internal use by the web ui
|
||||||
url(r'^detectip/(?P<sessionid>\w+)/$', DetectIpView.as_view(), name='detectip'),
|
re_path(r'^detectip/(?P<sessionid>\w+)/$', DetectIpView.as_view(), name='detectip'),
|
||||||
url(r'^ajax_get_ips/$', AjaxGetIps.as_view(), name="ajax_get_ips"),
|
re_path(r'^ajax_get_ips/$', AjaxGetIps.as_view(), name="ajax_get_ips"),
|
||||||
url(r'^nic/update_authorized$', AuthorizedNicUpdateView.as_view(), name='nic_update_authorized'),
|
re_path(r'^nic/update_authorized$', AuthorizedNicUpdateView.as_view(), name='nic_update_authorized'),
|
||||||
url(r'^nic/delete_authorized$', AuthorizedNicDeleteView.as_view(), name='nic_delete_authorized'),
|
re_path(r'^nic/delete_authorized$', AuthorizedNicDeleteView.as_view(), name='nic_delete_authorized'),
|
||||||
# api (for update clients)
|
# api (for update clients)
|
||||||
url(r'^myip$', myip_view, name='myip'),
|
re_path(r'^myip$', myip_view, name='myip'),
|
||||||
url(r'^nic/update$', NicUpdateView.as_view(), name='nic_update'),
|
re_path(r'^nic/update$', NicUpdateView.as_view(), name='nic_update'),
|
||||||
url(r'^nic/delete$', NicDeleteView.as_view(), name='nic_delete'), # api extension
|
re_path(r'^nic/delete$', NicDeleteView.as_view(), name='nic_delete'), # api extension
|
||||||
# for bots
|
# for bots
|
||||||
url(r'^robots.txt$', RobotsTxtView.as_view(), name='robots'),
|
re_path(r'^robots.txt$', RobotsTxtView.as_view(), name='robots'),
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,7 @@ import dns.message
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from nsupdate.main.models import Domain, Host
|
from nsupdate.main.models import Domain, Host
|
||||||
from nsupdate.main.dnstools import FQDN, query_ns, NameServerNotAvailable
|
from nsupdate.main.dnstools import FQDN, query_ns, NameServerNotAvailable
|
||||||
|
@ -7,7 +7,7 @@ import traceback
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from nsupdate.main.models import Host
|
from nsupdate.main.models import Host
|
||||||
from nsupdate.utils.mail import translate_for_user, send_mail_to_user
|
from nsupdate.utils.mail import translate_for_user, send_mail_to_user
|
||||||
|
@ -7,7 +7,7 @@ from datetime import datetime
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from nsupdate.main.models import Host
|
from nsupdate.main.models import Host
|
||||||
from nsupdate.utils.mail import translate_for_user, send_mail_to_user
|
from nsupdate.utils.mail import translate_for_user, send_mail_to_user
|
||||||
|
@ -8,7 +8,7 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from nsupdate.main.models import Host, Domain
|
from nsupdate.main.models import Host, Domain
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ AUTHENTICATION_BACKENDS = (
|
|||||||
'social_core.backends.amazon.AmazonOAuth2',
|
'social_core.backends.amazon.AmazonOAuth2',
|
||||||
'social_core.backends.bitbucket.BitbucketOAuth',
|
'social_core.backends.bitbucket.BitbucketOAuth',
|
||||||
'social_core.backends.disqus.DisqusOAuth2',
|
'social_core.backends.disqus.DisqusOAuth2',
|
||||||
'social_core.backends.dropbox.DropboxOAuth',
|
'social_core.backends.dropbox.DropboxOAuth2V2',
|
||||||
'social_core.backends.github.GithubOAuth2',
|
'social_core.backends.github.GithubOAuth2',
|
||||||
'social_core.backends.google.GoogleOAuth2',
|
'social_core.backends.google.GoogleOAuth2',
|
||||||
'social_core.backends.reddit.RedditOAuth2',
|
'social_core.backends.reddit.RedditOAuth2',
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<title>{% block title %}{{ WWW_HOST }}{% endblock %}</title>
|
<title>{% block title %}{{ WWW_HOST }}{% endblock %}</title>
|
||||||
|
|
||||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
|
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" rel="stylesheet" integrity="sha256-+N4/V/SbAFiW1MPBCXnfnP9QSN3+Keu+NlB+0ev/YKQ=" crossorigin="anonymous" />
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" rel="stylesheet" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" />
|
||||||
<link href="{% static 'css/nsupdate.css' %}" rel="stylesheet">
|
<link href="{% static 'css/nsupdate.css' %}" rel="stylesheet">
|
||||||
<link rel="icon" type="image/svg+xml" sizes="any" href="{% static "img/favicon.svg" %}">
|
<link rel="icon" type="image/svg+xml" sizes="any" href="{% static "img/favicon.svg" %}">
|
||||||
<link rel="icon" type="image/png" href="{% static "img/favicon_32.png" %}" sizes="32x32">
|
<link rel="icon" type="image/png" href="{% static "img/favicon_32.png" %}" sizes="32x32">
|
||||||
|
@ -2,12 +2,10 @@
|
|||||||
top-level url dispatching
|
top-level url dispatching
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import six
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.urls import include, re_path
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import login
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
@ -22,25 +20,25 @@ def remember_me_login(request, *args, **kw):
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if request.POST.get('remember_me'):
|
if request.POST.get('remember_me'):
|
||||||
request.session.set_expiry(settings.SESSION_COOKIE_AGE)
|
request.session.set_expiry(settings.SESSION_COOKIE_AGE)
|
||||||
return auth_views.login(request, *args, **kw)
|
return login(request, *args, **kw)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url('', include('social_django.urls', namespace='social')),
|
re_path('', include('social_django.urls', namespace='social')),
|
||||||
url(r'^accounts/', include('nsupdate.login.urls')),
|
re_path(r'^accounts/', include('nsupdate.login.urls')),
|
||||||
# registration and user settings
|
# registration and user settings
|
||||||
url(r'^account/', include('nsupdate.accounts.urls')),
|
re_path(r'^account/', include('nsupdate.accounts.urls')),
|
||||||
# https://wicg.github.io/change-password-url/index.html
|
# https://wicg.github.io/change-password-url/index.html
|
||||||
url(r'^.well-known/change-password$', RedirectView.as_view(pattern_name='account_settings', permanent=False)),
|
re_path(r'^.well-known/change-password$', RedirectView.as_view(pattern_name='account_settings', permanent=False)),
|
||||||
url(r'^admin/', include((admin.site.get_urls(), 'admin'), namespace='admin')),
|
re_path(r'^admin/', include((admin.site.get_urls(), 'admin'), namespace='admin')),
|
||||||
url(r'^i18n/', include('django.conf.urls.i18n')),
|
re_path(r'^i18n/', include('django.conf.urls.i18n')),
|
||||||
url(r'^', include('nsupdate.main.urls')),
|
re_path(r'^', include('nsupdate.main.urls')),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
urlpatterns += [url(r'^__debug__/', include(debug_toolbar.urls)), ]
|
urlpatterns += [re_path(r'^__debug__/', include(debug_toolbar.urls)), ]
|
||||||
|
|
||||||
|
|
||||||
# we have expensive context processors and do not want to invoke them for the
|
# we have expensive context processors and do not want to invoke them for the
|
||||||
@ -55,7 +53,7 @@ def http_error(request, status, exception=None):
|
|||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if isinstance(message, six.text_type):
|
if isinstance(message, str):
|
||||||
exception_repr = message
|
exception_repr = message
|
||||||
else:
|
else:
|
||||||
# we do not have an exception for 500
|
# we do not have an exception for 500
|
||||||
|
@ -3,7 +3,7 @@ Tests for mail module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from ..mail import translate_for_user
|
from ..mail import translate_for_user
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user