mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-08-26 12:46:08 +00:00
names: Allow adding multiple static domain names
- Change the mechanism for storing domain names in /etc/hosts. Don't write hostname to /etc/hosts. Don't prepend hostname to domain name. This means that when hostname changes, set_domain_name need not be called. - This means that domain names such as example.fbx.one were not resolvable using /etc/hosts but these will now resolve to 127.0.1.1. This is a minor concern to becoming a breaking change. - Don't use socket.getfqdn() for finding the domain name of the machine. Instead read from /etc/hosts. There does not seem to a glibc/python API for querying domain names from /etc/hosts with all variations it allows. Forward resolution properly works no matter the library. - Drop a pre-Python 3 conversion from unicode to ascii string for hostname. This is no longer relevant. - Domain name form is now domain add form. Passing domain name is mandatory. Domain delete form and view have been introduced. - Use augeas to edit hosts file. Add privileged methods to add/delete/get domains. Add method to migration from old format to new. Support reading old format too in get_domains. Tests: - Without hostname written in /etc/hosts, 'resolvectl query <hostname>' and 'ping <hostname>' work. - With old /etc/hosts format apply patches and restart service. It will be converted to new format. - Adding a domain adds a new line to /etc/hosts file. The domain is shown in domains list in Names app. Applications get reconfigured with the new domain name. - Deleting a domain adds a new line to /etc/hosts file. The domain is shown in domains list in Names app. Applications get reconfigured with the new domain name. - Restarting app triggers domain added signal for all domains and all the domains are shown in the Names app. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
889453daff
commit
863d170219
@ -133,7 +133,8 @@ def _jsxc_login(browser):
|
|||||||
|
|
||||||
def _jsxc_add_contact(browser):
|
def _jsxc_add_contact(browser):
|
||||||
"""Add a contact to JSXC user's roster."""
|
"""Add a contact to JSXC user's roster."""
|
||||||
functional.set_domain_name(browser, 'localhost')
|
# Configure a static domain
|
||||||
|
functional.domain_add(browser, 'mydomain.example')
|
||||||
functional.install(browser, 'jsxc')
|
functional.install(browser, 'jsxc')
|
||||||
_jsxc_login(browser)
|
_jsxc_login(browser)
|
||||||
functional.eventually(_is_jsxc_buddy_list_loaded, args=[browser])
|
functional.eventually(_is_jsxc_buddy_list_loaded, args=[browser])
|
||||||
|
|||||||
@ -5,7 +5,6 @@ FreedomBox app to configure name services.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
import socket
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
@ -43,7 +42,7 @@ class NamesApp(app_module.App):
|
|||||||
|
|
||||||
app_id = 'names'
|
app_id = 'names'
|
||||||
|
|
||||||
_version = 2
|
_version = 3
|
||||||
|
|
||||||
can_be_disabled = False
|
can_be_disabled = False
|
||||||
|
|
||||||
@ -66,7 +65,8 @@ class NamesApp(app_module.App):
|
|||||||
self.add(packages)
|
self.add(packages)
|
||||||
|
|
||||||
domain_type = DomainType('domain-type-static', _('Domain Name'),
|
domain_type = DomainType('domain-type-static', _('Domain Name'),
|
||||||
configuration_url='names:domains',
|
delete_url='names:domain-delete',
|
||||||
|
add_url='names:domain-add',
|
||||||
can_have_certificate=True)
|
can_have_certificate=True)
|
||||||
self.add(domain_type)
|
self.add(domain_type)
|
||||||
|
|
||||||
@ -84,11 +84,10 @@ class NamesApp(app_module.App):
|
|||||||
domain_removed.connect(on_domain_removed)
|
domain_removed.connect(on_domain_removed)
|
||||||
|
|
||||||
# Register domain with Name Services module.
|
# Register domain with Name Services module.
|
||||||
domain_name = get_domain_name()
|
for domain in privileged.get_domains():
|
||||||
if domain_name:
|
|
||||||
domain_added.send_robust(sender='names',
|
domain_added.send_robust(sender='names',
|
||||||
domain_type='domain-type-static',
|
domain_type='domain-type-static',
|
||||||
name=domain_name, services='__all__')
|
name=domain, services='__all__')
|
||||||
|
|
||||||
# Schedule installation of systemd-resolved if not already installed.
|
# Schedule installation of systemd-resolved if not already installed.
|
||||||
if not is_resolved_installed():
|
if not is_resolved_installed():
|
||||||
@ -115,6 +114,9 @@ class NamesApp(app_module.App):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if old_version < 3:
|
||||||
|
privileged.domains_migrate()
|
||||||
|
|
||||||
if is_resolved_installed():
|
if is_resolved_installed():
|
||||||
# Fresh install or upgrading to version 2
|
# Fresh install or upgrading to version 2
|
||||||
if old_version < 2:
|
if old_version < 2:
|
||||||
@ -235,10 +237,10 @@ def on_domain_removed(sender, domain_type, name='', **kwargs):
|
|||||||
######################################################
|
######################################################
|
||||||
|
|
||||||
|
|
||||||
def get_domain_name():
|
def get_domain_name() -> str | None:
|
||||||
"""Return the currently set static domain name."""
|
"""Return the currently set static domain name."""
|
||||||
fqdn = socket.getfqdn()
|
domains = privileged.get_domains()
|
||||||
return '.'.join(fqdn.split('.')[1:])
|
return domains[0] if domains else ''
|
||||||
|
|
||||||
|
|
||||||
def get_hostname():
|
def get_hostname():
|
||||||
@ -251,11 +253,6 @@ def get_hostname():
|
|||||||
def set_hostname(hostname):
|
def set_hostname(hostname):
|
||||||
"""Set machine hostname and send signals before and after."""
|
"""Set machine hostname and send signals before and after."""
|
||||||
old_hostname = get_hostname()
|
old_hostname = get_hostname()
|
||||||
domain_name = get_domain_name()
|
|
||||||
|
|
||||||
# Hostname should be ASCII. If it's unicode but passed our
|
|
||||||
# valid_hostname check, convert
|
|
||||||
hostname = str(hostname)
|
|
||||||
|
|
||||||
pre_hostname_change.send_robust(sender='names', old_hostname=old_hostname,
|
pre_hostname_change.send_robust(sender='names', old_hostname=old_hostname,
|
||||||
new_hostname=hostname)
|
new_hostname=hostname)
|
||||||
@ -263,9 +260,6 @@ def set_hostname(hostname):
|
|||||||
logger.info('Changing hostname to - %s', hostname)
|
logger.info('Changing hostname to - %s', hostname)
|
||||||
privileged.set_hostname(hostname)
|
privileged.set_hostname(hostname)
|
||||||
|
|
||||||
logger.info('Setting domain name after hostname change - %s', domain_name)
|
|
||||||
privileged.set_domain_name(domain_name)
|
|
||||||
|
|
||||||
post_hostname_change.send_robust(sender='names', old_hostname=old_hostname,
|
post_hostname_change.send_robust(sender='names', old_hostname=old_hostname,
|
||||||
new_hostname=hostname)
|
new_hostname=hostname)
|
||||||
|
|
||||||
|
|||||||
@ -99,8 +99,8 @@ def _domain_label_validator(domain_name):
|
|||||||
raise ValidationError(_('Invalid domain name'))
|
raise ValidationError(_('Invalid domain name'))
|
||||||
|
|
||||||
|
|
||||||
class DomainNameForm(forms.Form):
|
class DomainAddForm(forms.Form):
|
||||||
"""Form to update system's static domain name."""
|
"""Form to add a static domain name."""
|
||||||
|
|
||||||
domain_name = forms.CharField(
|
domain_name = forms.CharField(
|
||||||
label=_('Domain Name'), help_text=format_lazy(
|
label=_('Domain Name'), help_text=format_lazy(
|
||||||
@ -111,7 +111,7 @@ class DomainNameForm(forms.Form):
|
|||||||
'only alphabets, digits and hyphens. Length of each label '
|
'only alphabets, digits and hyphens. Length of each label '
|
||||||
'must be 63 characters or less. Total length of domain name '
|
'must be 63 characters or less. Total length of domain name '
|
||||||
'must be 253 characters or less.'), box_name=_(cfg.box_name)),
|
'must be 253 characters or less.'), box_name=_(cfg.box_name)),
|
||||||
required=False, validators=[
|
required=True, validators=[
|
||||||
validators.RegexValidator(
|
validators.RegexValidator(
|
||||||
r'^[a-zA-Z0-9]([-a-zA-Z0-9.]{,251}[a-zA-Z0-9])?$',
|
r'^[a-zA-Z0-9]([-a-zA-Z0-9.]{,251}[a-zA-Z0-9])?$',
|
||||||
_('Invalid domain name')), _domain_label_validator
|
_('Invalid domain name')), _domain_label_validator
|
||||||
|
|||||||
@ -16,6 +16,8 @@ source_fallback_conf = pathlib.Path(
|
|||||||
'/usr/share/freedombox'
|
'/usr/share/freedombox'
|
||||||
'/etc/systemd/resolved.conf.d/freedombox-fallback.conf')
|
'/etc/systemd/resolved.conf.d/freedombox-fallback.conf')
|
||||||
|
|
||||||
|
HOSTS_LOCAL_IP = '127.0.1.1'
|
||||||
|
|
||||||
|
|
||||||
@privileged
|
@privileged
|
||||||
def set_hostname(hostname: str):
|
def set_hostname(hostname: str):
|
||||||
@ -26,30 +28,97 @@ def set_hostname(hostname: str):
|
|||||||
action_utils.service_restart('avahi-daemon')
|
action_utils.service_restart('avahi-daemon')
|
||||||
|
|
||||||
|
|
||||||
|
def _load_augeas_hosts():
|
||||||
|
"""Initialize Augeas for editing /etc/hosts."""
|
||||||
|
hosts_file = '/etc/hosts'
|
||||||
|
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
|
||||||
|
augeas.Augeas.NO_MODL_AUTOLOAD)
|
||||||
|
aug.transform('hosts', hosts_file)
|
||||||
|
aug.set('/augeas/context', '/files' + hosts_file)
|
||||||
|
aug.load()
|
||||||
|
return aug
|
||||||
|
|
||||||
|
|
||||||
|
def get_domains(aug=None) -> list[str]:
|
||||||
|
"""Return the list of domains."""
|
||||||
|
if not aug:
|
||||||
|
aug = _load_augeas_hosts()
|
||||||
|
|
||||||
|
domains = {} # Maintain order of entries
|
||||||
|
for match in aug.match('*'):
|
||||||
|
if aug.get(match + '/ipaddr') != HOSTS_LOCAL_IP:
|
||||||
|
continue
|
||||||
|
|
||||||
|
domain = aug.get(match + '/canonical')
|
||||||
|
aliases = []
|
||||||
|
for alias_match in aug.match(match + '/alias'):
|
||||||
|
aliases.append(aug.get(alias_match))
|
||||||
|
|
||||||
|
# Read old style domains too.
|
||||||
|
if aliases and '.' not in aliases[-1]:
|
||||||
|
hostname = aliases[-1]
|
||||||
|
if domain.startswith(hostname + '.'):
|
||||||
|
domain = domain.partition('.')[2]
|
||||||
|
aliases = aliases[:-1]
|
||||||
|
|
||||||
|
for value in [domain] + aliases:
|
||||||
|
if value:
|
||||||
|
domains[domain] = True
|
||||||
|
|
||||||
|
return list(domains.keys())
|
||||||
|
|
||||||
|
|
||||||
@privileged
|
@privileged
|
||||||
def set_domain_name(domain_name: str | None = None):
|
def domain_add(domain_name: str | None = None):
|
||||||
"""Set system's static domain name in /etc/hosts."""
|
"""Set system's static domain name in /etc/hosts."""
|
||||||
hostname = subprocess.check_output(['hostname']).decode().strip()
|
aug = _load_augeas_hosts()
|
||||||
hosts_path = pathlib.Path('/etc/hosts')
|
domains = get_domains(aug)
|
||||||
if domain_name:
|
if domain_name in domains:
|
||||||
insert_line = f'127.0.1.1 {hostname}.{domain_name} {hostname}\n'
|
return # Domain already present in /etc/hosts
|
||||||
else:
|
|
||||||
insert_line = f'127.0.1.1 {hostname}\n'
|
|
||||||
|
|
||||||
lines = hosts_path.read_text(encoding='utf-8').splitlines(keepends=True)
|
aug.set('./01/ipaddr', HOSTS_LOCAL_IP)
|
||||||
new_lines = []
|
aug.set('./01/canonical', domain_name)
|
||||||
found = False
|
aug.save()
|
||||||
for line in lines:
|
|
||||||
if '127.0.1.1' in line:
|
|
||||||
new_lines.append(insert_line)
|
|
||||||
found = True
|
|
||||||
else:
|
|
||||||
new_lines.append(line)
|
|
||||||
|
|
||||||
if not found:
|
|
||||||
new_lines.append(insert_line)
|
|
||||||
|
|
||||||
hosts_path.write_text(''.join(new_lines), encoding='utf-8')
|
@privileged
|
||||||
|
def domain_delete(domain_name: str | None = None):
|
||||||
|
"""Set system's static domain name in /etc/hosts."""
|
||||||
|
aug = _load_augeas_hosts()
|
||||||
|
domains = get_domains(aug)
|
||||||
|
if domain_name not in domains:
|
||||||
|
return # Domain already not present in /etc/hosts
|
||||||
|
|
||||||
|
for match in aug.match('*'):
|
||||||
|
if aug.get(match + '/ipaddr') == HOSTS_LOCAL_IP and \
|
||||||
|
aug.get(match + '/canonical') == domain_name:
|
||||||
|
aug.remove(match)
|
||||||
|
|
||||||
|
aug.save()
|
||||||
|
|
||||||
|
|
||||||
|
@privileged
|
||||||
|
def domains_migrate() -> None:
|
||||||
|
"""Convert old style of adding domain names to /etc/hosts to new.
|
||||||
|
|
||||||
|
Old format:
|
||||||
|
127.0.1.1 <hostname>.<domain> <hostname>
|
||||||
|
|
||||||
|
New format:
|
||||||
|
127.0.1.1 <domain1>
|
||||||
|
127.0.1.1 <domain2>
|
||||||
|
"""
|
||||||
|
aug = _load_augeas_hosts()
|
||||||
|
domains = get_domains(aug)
|
||||||
|
for match in aug.match('*'):
|
||||||
|
if aug.get(match + '/ipaddr') == HOSTS_LOCAL_IP:
|
||||||
|
aug.remove(match)
|
||||||
|
|
||||||
|
for number, domain in enumerate(domains):
|
||||||
|
aug.set(f'./0{number}/ipaddr', HOSTS_LOCAL_IP)
|
||||||
|
aug.set(f'./0{number}/canonical', domain)
|
||||||
|
|
||||||
|
aug.save()
|
||||||
|
|
||||||
|
|
||||||
@privileged
|
@privileged
|
||||||
|
|||||||
25
plinth/modules/names/templates/names-domain-delete.html
Normal file
25
plinth/modules/names/templates/names-domain-delete.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h3>{{ title }}</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
App configurations will be updated.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form class="form form-delete" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-md btn-danger"
|
||||||
|
value="{% blocktrans %}Delete{% endblocktrans %}"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""Tests for forms in names app."""
|
"""Tests for forms in names app."""
|
||||||
|
|
||||||
from ..forms import DomainNameForm, HostnameForm
|
from ..forms import DomainAddForm, HostnameForm
|
||||||
|
|
||||||
|
|
||||||
def test_hostname_field():
|
def test_hostname_field():
|
||||||
@ -27,20 +27,20 @@ def test_hostname_field():
|
|||||||
def test_domain_name_field():
|
def test_domain_name_field():
|
||||||
"""Test that domain name field accepts only valid domain names."""
|
"""Test that domain name field accepts only valid domain names."""
|
||||||
valid_domain_names = [
|
valid_domain_names = [
|
||||||
'', 'a', '0a', 'a0', 'AAA', '00', '0-0', 'example-hostname', 'example',
|
'a', '0a', 'a0', 'AAA', '00', '0-0', 'example-hostname', 'example',
|
||||||
'example.org', 'a.b.c.d', 'a-0.b-0.c-0',
|
'example.org', 'a.b.c.d', 'a-0.b-0.c-0',
|
||||||
'012345678901234567890123456789012345678901234567890123456789012',
|
'012345678901234567890123456789012345678901234567890123456789012',
|
||||||
((('x' * 63) + '.') * 3) + 'x' * 61
|
((('x' * 63) + '.') * 3) + 'x' * 61
|
||||||
]
|
]
|
||||||
invalid_domain_names = [
|
invalid_domain_names = [
|
||||||
'-', '-a', 'a-', '.a', 'a.', '?', 'a?a', 'a..a', 'a.-a', '.',
|
'', '-', '-a', 'a-', '.a', 'a.', '?', 'a?a', 'a..a', 'a.-a', '.',
|
||||||
((('x' * 63) + '.') * 3) + 'x' * 62, 'x' * 64
|
((('x' * 63) + '.') * 3) + 'x' * 62, 'x' * 64
|
||||||
]
|
]
|
||||||
|
|
||||||
for domain_name in valid_domain_names:
|
for domain_name in valid_domain_names:
|
||||||
form = DomainNameForm({'domain_name': domain_name})
|
form = DomainAddForm({'domain_name': domain_name})
|
||||||
assert form.is_valid()
|
assert form.is_valid()
|
||||||
|
|
||||||
for domain_name in invalid_domain_names:
|
for domain_name in invalid_domain_names:
|
||||||
form = DomainNameForm({'domain_name': domain_name})
|
form = DomainAddForm({'domain_name': domain_name})
|
||||||
assert not form.is_valid()
|
assert not form.is_valid()
|
||||||
|
|||||||
@ -30,14 +30,11 @@ def _get_hostname(browser):
|
|||||||
|
|
||||||
def test_change_domain_name(session_browser):
|
def test_change_domain_name(session_browser):
|
||||||
"""Test changing the domain name."""
|
"""Test changing the domain name."""
|
||||||
functional.set_domain_name(session_browser, 'mydomain.example')
|
functional.domain_remove(session_browser, 'mydomain.example')
|
||||||
assert _get_domain_name(session_browser) == 'mydomain.example'
|
functional.domain_add(session_browser, 'mydomain.example')
|
||||||
|
assert 'mydomain.example' in functional.domain_list(session_browser)
|
||||||
|
|
||||||
# Capitalization is ignored.
|
# Capitalization is ignored.
|
||||||
functional.set_domain_name(session_browser, 'Mydomain.example')
|
functional.domain_remove(session_browser, 'mydomain2.example')
|
||||||
assert _get_domain_name(session_browser) == 'mydomain.example'
|
functional.domain_add(session_browser, 'Mydomain2.example')
|
||||||
|
assert 'mydomain2.example' in functional.domain_list(session_browser)
|
||||||
|
|
||||||
def _get_domain_name(browser):
|
|
||||||
functional.visit(browser, '/plinth/sys/names/domains/')
|
|
||||||
return browser.find_by_id('id_domain-name-domain_name').value
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""
|
"""URLs for the name services module."""
|
||||||
URLs for the name services module
|
|
||||||
"""
|
|
||||||
|
|
||||||
from django.urls import re_path
|
from django.urls import re_path
|
||||||
|
|
||||||
@ -11,6 +9,8 @@ urlpatterns = [
|
|||||||
re_path(r'^sys/names/$', views.NamesAppView.as_view(), name='index'),
|
re_path(r'^sys/names/$', views.NamesAppView.as_view(), name='index'),
|
||||||
re_path(r'^sys/names/hostname/$', views.HostnameView.as_view(),
|
re_path(r'^sys/names/hostname/$', views.HostnameView.as_view(),
|
||||||
name='hostname'),
|
name='hostname'),
|
||||||
re_path(r'^sys/names/domains/$', views.DomainNameView.as_view(),
|
re_path(r'^sys/names/domains/$', views.DomainAddView.as_view(),
|
||||||
name='domains'),
|
name='domain-add'),
|
||||||
|
re_path(r'^sys/names/domains/(?P<domain>[^/]+)/delete/$',
|
||||||
|
views.DomainDeleteView.as_view(), name='domain-delete'),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -6,8 +6,10 @@ FreedomBox app for name services.
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.generic import TemplateView
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
|
|
||||||
from plinth.modules import names
|
from plinth.modules import names
|
||||||
@ -15,7 +17,7 @@ from plinth.signals import domain_added, domain_removed
|
|||||||
from plinth.views import AppView
|
from plinth.views import AppView
|
||||||
|
|
||||||
from . import components, privileged, resolved
|
from . import components, privileged, resolved
|
||||||
from .forms import DomainNameForm, HostnameForm, NamesConfigurationForm
|
from .forms import DomainAddForm, HostnameForm, NamesConfigurationForm
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -102,40 +104,46 @@ class HostnameView(FormView):
|
|||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class DomainNameView(FormView):
|
class DomainAddView(FormView):
|
||||||
"""View to update system's static domain name."""
|
"""View to update system's static domain name."""
|
||||||
template_name = 'form.html'
|
template_name = 'form.html'
|
||||||
form_class = DomainNameForm
|
form_class = DomainAddForm
|
||||||
prefix = 'domain-name'
|
prefix = 'domain-add'
|
||||||
success_url = reverse_lazy('names:index')
|
success_url = reverse_lazy('names:index')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""Return additional context for rendering the template."""
|
"""Return additional context for rendering the template."""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['title'] = _('Set Domain Name')
|
context['title'] = _('Add Domain Name')
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
"""Return the values to fill in the form."""
|
|
||||||
initial = super().get_initial()
|
|
||||||
initial['domain_name'] = names.get_domain_name()
|
|
||||||
return initial
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
"""Apply the form changes."""
|
"""Apply the form changes."""
|
||||||
if form.initial['domain_name'] != form.cleaned_data['domain_name']:
|
_domain_add(form.cleaned_data['domain_name'])
|
||||||
try:
|
messages.success(self.request, _('Configuration updated'))
|
||||||
set_domain_name(form.cleaned_data['domain_name'])
|
|
||||||
messages.success(self.request, _('Configuration updated'))
|
|
||||||
except Exception as exception:
|
|
||||||
messages.error(
|
|
||||||
self.request,
|
|
||||||
_('Error setting domain name: {exception}').format(
|
|
||||||
exception=exception))
|
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class DomainDeleteView(TemplateView):
|
||||||
|
"""Confirm and delete a domain."""
|
||||||
|
template_name = 'names-domain-delete.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
"""Return additional context data for rendering the template."""
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
domain = self.kwargs['domain']
|
||||||
|
context['domain'] = domain
|
||||||
|
context['title'] = str(
|
||||||
|
_('Delete Domain {domain}?')).format(domain=domain)
|
||||||
|
return context
|
||||||
|
|
||||||
|
def post(self, request, domain):
|
||||||
|
"""Delete a domain."""
|
||||||
|
_domain_delete(domain)
|
||||||
|
messages.success(request, _('Domain deleted.'))
|
||||||
|
return redirect('names:index')
|
||||||
|
|
||||||
|
|
||||||
def get_status():
|
def get_status():
|
||||||
"""Get configured services per name."""
|
"""Get configured services per name."""
|
||||||
domains = components.DomainName.list()
|
domains = components.DomainName.list()
|
||||||
@ -148,24 +156,29 @@ def get_status():
|
|||||||
return {'domains': domains, 'unused_domain_types': unused_domain_types}
|
return {'domains': domains, 'unused_domain_types': unused_domain_types}
|
||||||
|
|
||||||
|
|
||||||
def set_domain_name(domain_name):
|
def _domain_add(domain_name: str):
|
||||||
"""Set system's static domain name to domain_name."""
|
"""Add a static domain name."""
|
||||||
old_domain_name = names.get_domain_name()
|
|
||||||
|
|
||||||
# Domain name is not case sensitive, but Let's Encrypt certificate
|
# Domain name is not case sensitive, but Let's Encrypt certificate
|
||||||
# paths use lower-case domain name.
|
# paths use lower-case domain name.
|
||||||
domain_name = domain_name.lower()
|
domain_name = domain_name.lower()
|
||||||
|
|
||||||
logger.info('Changing domain name to - %s', domain_name)
|
logger.info('Adding domain name - %s', domain_name)
|
||||||
privileged.set_domain_name(domain_name)
|
privileged.domain_add(domain_name)
|
||||||
|
|
||||||
|
domain_added.send_robust(sender='names', domain_type='domain-type-static',
|
||||||
|
name=domain_name, services='__all__')
|
||||||
|
|
||||||
|
|
||||||
|
def _domain_delete(domain_name: str):
|
||||||
|
"""Remove a static domain name."""
|
||||||
|
# Domain name is not case sensitive, but Let's Encrypt certificate
|
||||||
|
# paths use lower-case domain name.
|
||||||
|
domain_name = domain_name.lower()
|
||||||
|
|
||||||
|
logger.info('Removing domain name - %s', domain_name)
|
||||||
|
privileged.domain_delete(domain_name)
|
||||||
|
|
||||||
# Update domain registered with Name Services module.
|
# Update domain registered with Name Services module.
|
||||||
if old_domain_name:
|
domain_removed.send_robust(sender='names',
|
||||||
domain_removed.send_robust(sender='names',
|
domain_type='domain-type-static',
|
||||||
domain_type='domain-type-static',
|
name=domain_name)
|
||||||
name=old_domain_name)
|
|
||||||
|
|
||||||
if domain_name:
|
|
||||||
domain_added.send_robust(sender='names',
|
|
||||||
domain_type='domain-type-static',
|
|
||||||
name=domain_name, services='__all__')
|
|
||||||
|
|||||||
@ -592,16 +592,40 @@ def running_inside_container():
|
|||||||
#############################
|
#############################
|
||||||
# System -> Names utilities #
|
# System -> Names utilities #
|
||||||
#############################
|
#############################
|
||||||
def set_hostname(browser, hostname):
|
def set_hostname(browser, hostname: str):
|
||||||
|
"""Configure the system hostname."""
|
||||||
visit(browser, '/plinth/sys/names/hostname/')
|
visit(browser, '/plinth/sys/names/hostname/')
|
||||||
browser.find_by_id('id_hostname-hostname').fill(hostname)
|
browser.find_by_id('id_hostname-hostname').fill(hostname)
|
||||||
submit(browser, form_class='form-hostname')
|
submit(browser, form_class='form-hostname')
|
||||||
|
|
||||||
|
|
||||||
def set_domain_name(browser, domain_name):
|
def domain_add(browser, domain_name: str):
|
||||||
visit(browser, '/plinth/sys/names/domains/')
|
"""Add a domain to list of domains."""
|
||||||
browser.find_by_id('id_domain-name-domain_name').fill(domain_name)
|
if domain_name in domain_list(browser):
|
||||||
submit(browser, form_class='form-domain-name')
|
return
|
||||||
|
|
||||||
|
visit(browser, '/plinth/sys/names/')
|
||||||
|
click_link_by_href(browser, '/plinth/sys/names/domains/')
|
||||||
|
browser.find_by_id('id_domain-add-domain_name').fill(domain_name)
|
||||||
|
submit(browser, form_class='form-domain-add')
|
||||||
|
|
||||||
|
|
||||||
|
def domain_remove(browser, domain_name: str):
|
||||||
|
"""Remove a domain from list of domains."""
|
||||||
|
if domain_name not in domain_list(browser):
|
||||||
|
return
|
||||||
|
|
||||||
|
visit(browser, '/plinth/sys/names/')
|
||||||
|
click_link_by_href(browser,
|
||||||
|
f'/plinth/sys/names/domains/{domain_name}/delete/')
|
||||||
|
submit(browser, form_class='form-delete')
|
||||||
|
|
||||||
|
|
||||||
|
def domain_list(browser) -> list[str]:
|
||||||
|
"""Return a list of domains configured."""
|
||||||
|
visit(browser, '/plinth/sys/names/')
|
||||||
|
elements = browser.find_by_css('td.names-domain-column')
|
||||||
|
return [element.text for element in elements]
|
||||||
|
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user