Compare commits
18 Commits
5593eec2d8
...
6e2526f54e
Author | SHA1 | Date | |
---|---|---|---|
6e2526f54e | |||
3b1fb41bf8 | |||
e7d10e9a20 | |||
a60f830927 | |||
12966222e3 | |||
5e7e7a933f | |||
9d8a50e92f | |||
1aed14f487 | |||
6a6d9a5ab2 | |||
f6239649aa | |||
b32b05705d | |||
0f8d7fe8ce | |||
77bf293d56 | |||
897515b112 | |||
f4ab1964e2 | |||
5ea29a913b | |||
|
7b94ecdf30 | ||
|
84e3e19af9 |
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(".")'
|
68
DEVELOPMENT.md
Normal file
68
DEVELOPMENT.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Dependency management
|
||||
|
||||
Get [Pipenv](https://pipenv.pypa.io/en/latest/installation/) and checkout the [Pipenv Command reference](https://pipenv.pypa.io/en/latest/commands/)
|
||||
|
||||
## Install new dependencies
|
||||
|
||||
https://pipenv.pypa.io/en/latest/commands/#install
|
||||
|
||||
```
|
||||
pipenv install mypkg
|
||||
```
|
||||
|
||||
## Spawn a shell with correct python paths
|
||||
|
||||
```
|
||||
pipenv shell
|
||||
```
|
||||
|
||||
Exit the shell with `exit`
|
||||
|
||||
## Dependency maintenance
|
||||
|
||||
### Update requirements.txt files including transitive dependencies
|
||||
|
||||
```
|
||||
pipenv update
|
||||
```
|
||||
|
||||
NOTE: This is not done today and only a suggestion.
|
||||
|
||||
```
|
||||
pipenv requirements --exclude-markers > requirements.d/all.txt
|
||||
pipenv requirements --exclude-markers --dev-only > requirements.d/dev.txt
|
||||
```
|
||||
|
||||
Verify the updated dependencies don't include any security vulnerabilities
|
||||
|
||||
```
|
||||
pipenv check
|
||||
```
|
||||
|
||||
# Build locally
|
||||
|
||||
1. Install `build` (see [docs](https://packaging.python.org/en/latest/tutorials/packaging-projects/#generating-distribution-archives) for example via `pacman -S python-build` on ArchLinux
|
||||
2. Afterwards run the command to generate pip packgases in `dist/`: `pyproject-build`
|
||||
|
||||
NOTE: This is also needed before development because the command generates `./src/nsupdate/_version.py`.
|
||||
|
||||
# Run locally
|
||||
|
||||
1. Install dependencies `pipenv install --dev`
|
||||
2. Generate `src/nsupdate/_version.py` file by running `pyproject-build`
|
||||
2. Create database using `pipenv run ./manage.py migrate`
|
||||
3. Create a superuser with `pipenv run ./manage.py createsuperuser`
|
||||
4. Run the server with `pipenv run ./manage.py runserver`
|
||||
|
||||
# Lint
|
||||
|
||||
Run [pylint](https://pylint.readthedocs.io/en/stable/) in error-only mode to check any problems: `pipenv run pylint src/nsupdate`
|
||||
|
||||
NOTE: The project does not use pylint for formatting. Disabling the `errors-only` mode in `.pylintrc` will show a lot of warnings.
|
||||
|
||||
# Run tests
|
||||
|
||||
Tests need to run inside Docker because they depend on specific bind9 config on 127.0.0.1:53.
|
||||
|
||||
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`
|
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"
|
1012
Pipfile.lock
generated
Normal file
1012
Pipfile.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -360,7 +360,7 @@ Here are some clients that likely qualify:
|
||||
|
||||
- we offer configuration help for it, just copy & paste
|
||||
- good working, reliable
|
||||
- the official version is IPv4 only, IPv6 support needs a patched version
|
||||
- IPv4 and IPv6 support
|
||||
- Linux & other POSIX systems
|
||||
* inadyn (>= 1.99.11)
|
||||
|
||||
|
@ -4,6 +4,9 @@ import sys
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
PROJECT_ROOT = os.path.dirname(__file__)
|
||||
sys.path.insert(0, os.path.join(PROJECT_ROOT, 'src'))
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "nsupdate.settings.dev")
|
||||
from django.core.management import execute_from_command_line
|
||||
execute_from_command_line(sys.argv)
|
||||
|
@ -1,7 +1,7 @@
|
||||
# packages always needed
|
||||
dnspython
|
||||
netaddr
|
||||
django~=2.2.0
|
||||
django
|
||||
django-bootstrap-form
|
||||
django-referrer-policy
|
||||
django-registration-redux
|
||||
|
@ -4,4 +4,6 @@ django-debug-toolbar
|
||||
pytest>=3.6
|
||||
pytest-django
|
||||
pytest-pep8
|
||||
pylint
|
||||
pylint-django
|
||||
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
|
||||
|
||||
pylint src/nsupdate
|
||||
pytest src/nsupdate
|
2
setup.py
2
setup.py
@ -31,7 +31,7 @@ setup(
|
||||
install_requires=[
|
||||
'dnspython',
|
||||
'netaddr',
|
||||
'django>=2.2.0',
|
||||
'django',
|
||||
'django-bootstrap-form',
|
||||
'django-referrer-policy',
|
||||
'django-registration-redux',
|
||||
|
@ -7,13 +7,10 @@ from django.conf import settings
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.contrib.auth.signals import user_logged_in
|
||||
from django.utils.translation import ugettext_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
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
LANGUAGE_SESSION_KEY = "_language"
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class UserProfile(models.Model):
|
||||
"""
|
||||
stuff we need additionally to what Django stores in User model
|
||||
@ -25,7 +22,7 @@ class UserProfile(models.Model):
|
||||
verbose_name=_('language'))
|
||||
|
||||
def __str__(self):
|
||||
return u"profile for %s" % text_type(self.user)
|
||||
return u"profile for %s" % str(self.user)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('user profile')
|
||||
|
@ -7,7 +7,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
from django import forms
|
||||
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
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import re_path
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from registration.backends.default.views import ActivationView
|
||||
@ -8,12 +8,12 @@ from .views import UserProfileView, DeleteUserView, UserChangePasswordView
|
||||
|
||||
|
||||
urlpatterns = (
|
||||
url(r'^profile/', UserProfileView.as_view(), name="account_profile"),
|
||||
url(r'^settings/', UserChangePasswordView.as_view(), name='account_settings'),
|
||||
url(r'^delete/', DeleteUserView.as_view(), name="account_delete"),
|
||||
re_path(r'^profile/', UserProfileView.as_view(), name="account_profile"),
|
||||
re_path(r'^settings/', UserChangePasswordView.as_view(), name='account_settings'),
|
||||
re_path(r'^delete/', DeleteUserView.as_view(), name="account_delete"),
|
||||
|
||||
# registration start
|
||||
url(r'^activate/complete/$',
|
||||
re_path(r'^activate/complete/$',
|
||||
TemplateView.as_view(template_name='registration/activation_complete.html'),
|
||||
name='registration_activation_complete'),
|
||||
# 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
|
||||
# confusing 404.
|
||||
|
||||
url(r'^activate/(?P<activation_key>\w+)/$',
|
||||
re_path(r'^activate/(?P<activation_key>\w+)/$',
|
||||
ActivationView.as_view(),
|
||||
name='registration_activate'),
|
||||
url(r'^register/$',
|
||||
re_path(r'^register/$',
|
||||
RegistrationView.as_view(),
|
||||
name='registration_register'),
|
||||
url(r'^register/complete/$',
|
||||
re_path(r'^register/complete/$',
|
||||
TemplateView.as_view(template_name='registration/registration_complete.html'),
|
||||
name='registration_complete'),
|
||||
url(r'^register/closed/$',
|
||||
re_path(r'^register/closed/$',
|
||||
TemplateView.as_view(template_name='registration/registration_closed.html'),
|
||||
name='registration_disallowed'),
|
||||
# registration end
|
||||
|
@ -10,7 +10,7 @@ from django.views.decorators.debug import sensitive_post_parameters
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.shortcuts import redirect
|
||||
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 .forms import UserForm, UserProfileForm
|
||||
|
@ -1,25 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.conf.urls import url
|
||||
from django.urls import re_path
|
||||
from django.contrib.auth.views import LoginView, LogoutView, \
|
||||
PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteView
|
||||
|
||||
urlpatterns = (
|
||||
# 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'
|
||||
url(r'^logout/$', LogoutView.as_view(), name='logout'),
|
||||
re_path(r'^logout/$', LogoutView.as_view(), name='logout'),
|
||||
|
||||
# password reset urls
|
||||
url(r'^password_reset/$',
|
||||
re_path(r'^password_reset/$',
|
||||
PasswordResetView.as_view(template_name='password_reset.html'),
|
||||
name='password_reset'),
|
||||
url(r'^password_reset_done/$',
|
||||
re_path(r'^password_reset_done/$',
|
||||
PasswordResetDoneView.as_view(template_name='password_reset_done.html'),
|
||||
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'),
|
||||
name='password_reset_confirm'),
|
||||
url(r'^password_reset_complete/$',
|
||||
re_path(r'^password_reset_complete/$',
|
||||
PasswordResetCompleteView.as_view(template_name='password_reset_complete.html'),
|
||||
name='password_reset_complete'),
|
||||
)
|
||||
|
@ -6,7 +6,7 @@ form definitions (which fields are available, order, autofocus, ...)
|
||||
import binascii
|
||||
|
||||
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 .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.contrib.auth.hashers import make_password
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.six import text_type
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from . import dnstools
|
||||
|
||||
@ -34,7 +32,6 @@ def result_fmt(msg):
|
||||
return msg[:RESULT_MSG_LEN]
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class BlacklistedHost(models.Model):
|
||||
name_re = models.CharField(
|
||||
_('name RegEx'),
|
||||
@ -81,7 +78,6 @@ UPDATE_ALGORITHMS = {
|
||||
UPDATE_ALGORITHM_CHOICES = [(k, k) for k in UPDATE_ALGORITHMS]
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Domain(models.Model):
|
||||
name = models.CharField(
|
||||
_("name"),
|
||||
@ -154,7 +150,6 @@ class Domain(models.Model):
|
||||
ordering = ('name',)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Host(models.Model):
|
||||
name = models.CharField(
|
||||
_("name"),
|
||||
@ -170,7 +165,7 @@ class Host(models.Model):
|
||||
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, verbose_name=_("domain"))
|
||||
update_secret = models.CharField(
|
||||
_("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"),
|
||||
@ -373,7 +368,6 @@ def post_save_host(sender, **kwargs):
|
||||
post_save.connect(post_save_host, sender=Host)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class RelatedHost(models.Model):
|
||||
# host addr = network_of_main_host + interface_id
|
||||
name = models.CharField(
|
||||
@ -414,7 +408,7 @@ class RelatedHost(models.Model):
|
||||
verbose_name=_("main host"))
|
||||
|
||||
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):
|
||||
unique_together = (('name', 'main_host'),)
|
||||
@ -447,7 +441,6 @@ class RelatedHost(models.Model):
|
||||
pre_delete.connect(pre_delete_host, sender=RelatedHost)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ServiceUpdater(models.Model):
|
||||
name = models.CharField(
|
||||
_("name"),
|
||||
@ -491,7 +484,6 @@ class ServiceUpdater(models.Model):
|
||||
verbose_name_plural = _('service updaters')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ServiceUpdaterHostConfig(models.Model):
|
||||
service = models.ForeignKey(ServiceUpdater, on_delete=models.CASCADE, verbose_name=_("service"))
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
main app url dispatching
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import re_path
|
||||
|
||||
from .views import (
|
||||
HomeView, OverviewView, HostView, AddHostView, DeleteHostView, AboutView, GenerateSecretView, GenerateNSSecretView,
|
||||
@ -16,39 +16,39 @@ from ..api.views import (
|
||||
|
||||
urlpatterns = (
|
||||
# interactive web ui
|
||||
url(r'^$', HomeView.as_view(), name="home"),
|
||||
url(r'^about/$', AboutView.as_view(), name="about"),
|
||||
url(r'^custom/(?P<template>[\w.]+)$', CustomTemplateView.as_view(), name="custom"),
|
||||
url(r'^update$', JsUpdateView.as_view(), name='update'),
|
||||
url(r'^overview/$', OverviewView.as_view(), name='overview'),
|
||||
url(r'^status/$', StatusView.as_view(), name='status'),
|
||||
url(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'),
|
||||
url(r'^host/(?P<pk>\d+)/$', HostView.as_view(), name='host_view'),
|
||||
url(r'^host/add/$', AddHostView.as_view(), name='add_host'),
|
||||
url(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'),
|
||||
url(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'),
|
||||
url(r'^host/(?P<mpk>\d+)/related/(?P<pk>\d+)/delete/$', DeleteRelatedHostView.as_view(),
|
||||
re_path(r'^$', HomeView.as_view(), name="home"),
|
||||
re_path(r'^about/$', AboutView.as_view(), name="about"),
|
||||
re_path(r'^custom/(?P<template>[\w.]+)$', CustomTemplateView.as_view(), name="custom"),
|
||||
re_path(r'^update$', JsUpdateView.as_view(), name='update'),
|
||||
re_path(r'^overview/$', OverviewView.as_view(), name='overview'),
|
||||
re_path(r'^status/$', StatusView.as_view(), name='status'),
|
||||
re_path(r'^generate_secret/(?P<pk>\d+)/$', GenerateSecretView.as_view(), name='generate_secret_view'),
|
||||
re_path(r'^generate_ns_secret/(?P<pk>\d+)/$', GenerateNSSecretView.as_view(), name='generate_ns_secret_view'),
|
||||
re_path(r'^host/(?P<pk>\d+)/$', HostView.as_view(), name='host_view'),
|
||||
re_path(r'^host/add/$', AddHostView.as_view(), name='add_host'),
|
||||
re_path(r'^host/(?P<pk>\d+)/delete/$', DeleteHostView.as_view(), name='delete_host'),
|
||||
re_path(r'^host/(?P<mpk>\d+)/related/$', RelatedHostOverviewView.as_view(), name='related_host_overview'),
|
||||
re_path(r'^host/(?P<mpk>\d+)/related/(?P<pk>\d+)/$', RelatedHostView.as_view(), name='related_host_view'),
|
||||
re_path(r'^host/(?P<mpk>\d+)/related/add/$', AddRelatedHostView.as_view(), name='add_related_host'),
|
||||
re_path(r'^host/(?P<mpk>\d+)/related/(?P<pk>\d+)/delete/$', DeleteRelatedHostView.as_view(),
|
||||
name='delete_related_host'),
|
||||
url(r'^domain/(?P<pk>\d+)/$', DomainView.as_view(), name='domain_view'),
|
||||
url(r'^domain/add/$', AddDomainView.as_view(), name='add_domain'),
|
||||
url(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'^domain/(?P<pk>\d+)/$', DomainView.as_view(), name='domain_view'),
|
||||
re_path(r'^domain/add/$', AddDomainView.as_view(), name='add_domain'),
|
||||
re_path(r'^domain/(?P<pk>\d+)/delete/$', DeleteDomainView.as_view(), name='delete_domain'),
|
||||
re_path(r'^updater_hostconfig_overview/(?P<pk>\d+)/$', UpdaterHostConfigOverviewView.as_view(),
|
||||
name='updater_hostconfig_overview'),
|
||||
url(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+)/$', UpdaterHostConfigView.as_view(), name='updater_hostconfig'),
|
||||
re_path(r'^updater_hostconfig/(?P<pk>\d+)/delete/$', DeleteUpdaterHostConfigView.as_view(),
|
||||
name='delete_updater_hostconfig'),
|
||||
# internal use by the web ui
|
||||
url(r'^detectip/(?P<sessionid>\w+)/$', DetectIpView.as_view(), name='detectip'),
|
||||
url(r'^ajax_get_ips/$', AjaxGetIps.as_view(), name="ajax_get_ips"),
|
||||
url(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'^detectip/(?P<sessionid>\w+)/$', DetectIpView.as_view(), name='detectip'),
|
||||
re_path(r'^ajax_get_ips/$', AjaxGetIps.as_view(), name="ajax_get_ips"),
|
||||
re_path(r'^nic/update_authorized$', AuthorizedNicUpdateView.as_view(), name='nic_update_authorized'),
|
||||
re_path(r'^nic/delete_authorized$', AuthorizedNicDeleteView.as_view(), name='nic_delete_authorized'),
|
||||
# api (for update clients)
|
||||
url(r'^myip$', myip_view, name='myip'),
|
||||
url(r'^nic/update$', NicUpdateView.as_view(), name='nic_update'),
|
||||
url(r'^nic/delete$', NicDeleteView.as_view(), name='nic_delete'), # api extension
|
||||
re_path(r'^myip$', myip_view, name='myip'),
|
||||
re_path(r'^nic/update$', NicUpdateView.as_view(), name='nic_update'),
|
||||
re_path(r'^nic/delete$', NicDeleteView.as_view(), name='nic_delete'), # api extension
|
||||
# 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.mail import send_mail
|
||||
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.dnstools import FQDN, query_ns, NameServerNotAvailable
|
||||
|
@ -7,7 +7,7 @@ import traceback
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.mail import send_mail
|
||||
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.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.db import transaction
|
||||
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.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.db import transaction
|
||||
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
|
||||
|
||||
|
@ -308,7 +308,7 @@ AUTHENTICATION_BACKENDS = (
|
||||
'social_core.backends.amazon.AmazonOAuth2',
|
||||
'social_core.backends.bitbucket.BitbucketOAuth',
|
||||
'social_core.backends.disqus.DisqusOAuth2',
|
||||
'social_core.backends.dropbox.DropboxOAuth',
|
||||
'social_core.backends.dropbox.DropboxOAuth2V2',
|
||||
'social_core.backends.github.GithubOAuth2',
|
||||
'social_core.backends.google.GoogleOAuth2',
|
||||
'social_core.backends.reddit.RedditOAuth2',
|
||||
|
@ -10,7 +10,7 @@
|
||||
<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://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 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">
|
||||
|
@ -2,12 +2,10 @@
|
||||
top-level url dispatching
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
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.auth import views as auth_views
|
||||
from django.contrib.auth import login
|
||||
from django.conf.urls.static import static
|
||||
from django.http import HttpResponse
|
||||
from django.views.generic import RedirectView
|
||||
@ -22,25 +20,25 @@ def remember_me_login(request, *args, **kw):
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('remember_me'):
|
||||
request.session.set_expiry(settings.SESSION_COOKIE_AGE)
|
||||
return auth_views.login(request, *args, **kw)
|
||||
return login(request, *args, **kw)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url('', include('social_django.urls', namespace='social')),
|
||||
url(r'^accounts/', include('nsupdate.login.urls')),
|
||||
re_path('', include('social_django.urls', namespace='social')),
|
||||
re_path(r'^accounts/', include('nsupdate.login.urls')),
|
||||
# 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
|
||||
url(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')),
|
||||
url(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
url(r'^', include('nsupdate.main.urls')),
|
||||
re_path(r'^.well-known/change-password$', RedirectView.as_view(pattern_name='account_settings', permanent=False)),
|
||||
re_path(r'^admin/', include((admin.site.get_urls(), 'admin'), namespace='admin')),
|
||||
re_path(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
re_path(r'^', include('nsupdate.main.urls')),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
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
|
||||
@ -55,7 +53,7 @@ def http_error(request, status, exception=None):
|
||||
except (AttributeError, IndexError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(message, six.text_type):
|
||||
if isinstance(message, str):
|
||||
exception_repr = message
|
||||
else:
|
||||
# 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.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from ..mail import translate_for_user
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user