diff --git a/plinth/modules/first_boot/forms.py b/plinth/modules/first_boot/forms.py index 0cead0456..457f866a9 100644 --- a/plinth/modules/first_boot/forms.py +++ b/plinth/modules/first_boot/forms.py @@ -22,7 +22,6 @@ Forms for first boot module. import json import logging import requests -import subprocess from django import forms from django.contrib import auth @@ -43,6 +42,7 @@ logger = logging.getLogger(__name__) class State1Form(ValidNewUsernameCheckMixin, auth.forms.UserCreationForm): """Firstboot state 1: create a new user.""" + def __init__(self, *args, **kwargs): self.request = kwargs.pop('request') super().__init__(*args, **kwargs) @@ -103,22 +103,6 @@ class State1Form(ValidNewUsernameCheckMixin, auth.forms.UserCreationForm): messages.success(self.request, message) -class SubdomainWidget(forms.widgets.TextInput): - """Append the domain to the subdomain bootstrap input field""" - def __init__(self, domain, *args, **kwargs): - """Intialize the widget by storing the domain value.""" - super().__init__(*args, **kwargs) - self.domain = domain - - def render(self, *args, **kwargs): - """Return the HTML for the widget.""" - inputfield = super().render(*args, **kwargs) - return """
- {0} - {1} -
""".format(inputfield, self.domain) - - class State5Form(forms.Form): """Set up freedombox.me pagekite subdomain""" DOMAIN_APPENDIX = '.freedombox.me' diff --git a/plinth/modules/first_boot/middleware.py b/plinth/modules/first_boot/middleware.py index d2cc7db84..99b25d9dd 100644 --- a/plinth/modules/first_boot/middleware.py +++ b/plinth/modules/first_boot/middleware.py @@ -26,10 +26,11 @@ from django.conf import settings import logging from operator import itemgetter from plinth import kvstore, module_loader -from django.shortcuts import render LOGGER = logging.getLogger(__name__) +firstboot_steps = [] + class FirstBootMiddleware(object): """Forward to firstboot page if firstboot isn't finished yet.""" @@ -37,7 +38,11 @@ class FirstBootMiddleware(object): @staticmethod def process_request(request): """Handle a request as Django middleware request handler.""" - state = kvstore.get_default('firstboot_state', 0) + old_state = kvstore.get_default('firstboot_state', 0) + state = kvstore.get_default('setup_state', 0) + if state == 0 and old_state == 10: + state = 1 + kvstore.set('setup_state', 1) user_requests_firstboot = is_firstboot(request.path) user_requests_login = request.path.startswith(reverse(settings.LOGIN_URL)) help_index_url = reverse('help:index') @@ -77,8 +82,10 @@ def get_firstboot_steps(): def next_step(): """ Returns the next first boot step required to run """ - steps = get_firstboot_steps() - for step in steps: + global firstboot_steps + if len(firstboot_steps) == 0: + firstboot_steps = get_firstboot_steps() + for step in firstboot_steps: done = kvstore.get_default(step.get('id'), 0) if done == 0: return step.get('url') @@ -90,3 +97,14 @@ def mark_step_done(id): :param id: id of the firstboot step """ kvstore.set(id, 1) + global firstboot_steps + if len(firstboot_steps) == 0: + firstboot_steps = get_firstboot_steps() + setup_done = True + for step in firstboot_steps: + done = kvstore.get_default(step.get('id'), 0) + if done == 0: + setup_done = False + break + if setup_done: + kvstore.set('setup_state', 1) diff --git a/plinth/modules/first_boot/views.py b/plinth/modules/first_boot/views.py index 305597a05..0d9299350 100644 --- a/plinth/modules/first_boot/views.py +++ b/plinth/modules/first_boot/views.py @@ -15,19 +15,11 @@ # along with this program. If not, see . # -from django.contrib import messages from django.contrib.auth.models import User -from django.http.response import HttpResponseRedirect from django.shortcuts import render -from django.urls import reverse_lazy from django.utils.translation import ugettext as _ from django.views.generic import CreateView, FormView, TemplateView - -from plinth import cfg -from plinth import kvstore from plinth import network -from plinth.errors import DomainRegistrationError -from .forms import State1Form, State5Form from .middleware import mark_step_done, next_step @@ -51,7 +43,7 @@ def state10(request): """ # Make sure that a user exists before finishing firstboot if User.objects.all(): - mark_step_done('firstboot_state') + mark_step_done('firstboot_state10') connections = network.get_connection_list() diff --git a/plinth/modules/pagekite/forms.py b/plinth/modules/pagekite/forms.py index c2230ed5a..81ef9ae47 100644 --- a/plinth/modules/pagekite/forms.py +++ b/plinth/modules/pagekite/forms.py @@ -27,7 +27,6 @@ import logging from plinth import cfg from plinth.errors import ActionError, DomainRegistrationError -from plinth.modules.first_boot.forms import SubdomainWidget from plinth.modules.pagekite.utils import PREDEFINED_SERVICES, run from plinth.utils import format_lazy @@ -38,6 +37,7 @@ LOGGER = logging.getLogger(__name__) class TrimmedCharField(forms.CharField): """Trim the contents of a CharField""" + def clean(self, value): """Clean and validate the field value""" if value: @@ -46,6 +46,23 @@ class TrimmedCharField(forms.CharField): return super(TrimmedCharField, self).clean(value) +class SubdomainWidget(forms.widgets.TextInput): + """Append the domain to the subdomain bootstrap input field""" + + def __init__(self, domain, *args, **kwargs): + """Intialize the widget by storing the domain value.""" + super().__init__(*args, **kwargs) + self.domain = domain + + def render(self, *args, **kwargs): + """Return the HTML for the widget.""" + inputfield = super().render(*args, **kwargs) + return """
+ {0} + {1} +
""".format(inputfield, self.domain) + + class ConfigurationForm(forms.Form): """Configure PageKite credentials and frontend""" @@ -54,9 +71,9 @@ class ConfigurationForm(forms.Form): server_domain = forms.CharField( label=ugettext_lazy('Server domain'), required=False, - help_text=\ - ugettext_lazy('Select your pagekite server. Set "pagekite.net" to use ' - 'the default pagekite.net server.'), + help_text= \ + ugettext_lazy('Select your pagekite server. Set "pagekite.net" to use ' + 'the default pagekite.net server.'), widget=forms.TextInput()) server_port = forms.IntegerField( label=ugettext_lazy('Server port'), required=False, @@ -70,9 +87,9 @@ class ConfigurationForm(forms.Form): kite_secret = TrimmedCharField( label=ugettext_lazy('Kite secret'), - help_text=\ - ugettext_lazy('A secret associated with the kite or the default secret ' - 'for your account if no secret is set on the kite.')) + help_text= \ + ugettext_lazy('A secret associated with the kite or the default secret ' + 'for your account if no secret is set on the kite.')) def save(self, request): """Save the form on submission after validation.""" @@ -84,14 +101,14 @@ class ConfigurationForm(forms.Form): config_changed = False if old['kite_name'] != new['kite_name'] or \ - old['kite_secret'] != new['kite_secret']: + old['kite_secret'] != new['kite_secret']: utils.run(['set-kite', '--kite-name', new['kite_name']], input=new['kite_secret'].encode()) messages.success(request, _('Kite details set')) config_changed = True if old['server_domain'] != new['server_domain'] or \ - old['server_port'] != new['server_port']: + old['server_port'] != new['server_port']: server = "%s:%s" % (new['server_domain'], new['server_port']) utils.run(['set-frontend', server]) messages.success(request, _('Pagekite server set')) @@ -191,7 +208,6 @@ class BaseCustomServiceForm(forms.Form): class DeleteCustomServiceForm(BaseCustomServiceForm): - def delete(self, request): service = self.convert_formdata_to_service(self.cleaned_data) utils.run(['remove-service', '--service', json.dumps(service)]) @@ -244,6 +260,8 @@ class AddCustomServiceForm(BaseCustomServiceForm): messages.error(request, _('This service already exists')) else: raise + + class State5Form(forms.Form): """Set up freedombox.me pagekite subdomain""" DOMAIN_APPENDIX = '.freedombox.me' diff --git a/plinth/modules/pagekite/views.py b/plinth/modules/pagekite/views.py index c81f69c61..3746ead85 100644 --- a/plinth/modules/pagekite/views.py +++ b/plinth/modules/pagekite/views.py @@ -21,7 +21,6 @@ from django.urls import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ from django.views.generic import View, TemplateView from django.views.generic.edit import FormView -from plinth import kvstore from plinth.errors import DomainRegistrationError from . import utils diff --git a/plinth/modules/users/forms.py b/plinth/modules/users/forms.py index f4b429c2c..1ff3e72dd 100644 --- a/plinth/modules/users/forms.py +++ b/plinth/modules/users/forms.py @@ -28,9 +28,10 @@ from django.utils.translation import ugettext as _, ugettext_lazy from plinth import actions from plinth.errors import ActionError -# Usernames used by optional services (that might not be installed yet). from plinth.modules.security import set_restricted_access +# Usernames used by optional services (that might not be installed yet). + RESERVED_USERNAMES = [ 'debian-deluged', 'Debian-minetest', @@ -55,11 +56,12 @@ GROUP_CHOICES = ( class ValidNewUsernameCheckMixin(object): """Mixin to check if a username is valid for created new user.""" + def clean_username(self): """Check for username collisions with system users.""" username = self.cleaned_data['username'] if self.instance.username != username and \ - not self.is_valid_new_username(): + not self.is_valid_new_username(): raise ValidationError(_('Username is taken or is reserved.'), code='invalid') @@ -93,14 +95,14 @@ class CreateUserForm(ValidNewUsernameCheckMixin, UserCreationForm): label=ugettext_lazy('Groups'), required=False, widget=forms.CheckboxSelectMultiple, - help_text=\ - ugettext_lazy('Select which services should be available to the new ' - 'user. The user will be able to log in to services that ' - 'support single sign-on through LDAP, if they are in the ' - 'appropriate group.

Users in the admin group ' - 'will be able to log in to all services. They can also ' - 'log in to the system through SSH and have ' - 'administrative privileges (sudo).')) + help_text= \ + ugettext_lazy('Select which services should be available to the new ' + 'user. The user will be able to log in to services that ' + 'support single sign-on through LDAP, if they are in the ' + 'appropriate group.

Users in the admin group ' + 'will be able to log in to all services. They can also ' + 'log in to the system through SSH and have ' + 'administrative privileges (sudo).')) def __init__(self, request, *args, **kwargs): """Initialize the form with extra request argument.""" @@ -130,7 +132,7 @@ class CreateUserForm(ValidNewUsernameCheckMixin, UserCreationForm): messages.error( self.request, _('Failed to add new user to {group} group.') - .format(group=group)) + .format(group=group)) group_object, created = Group.objects.get_or_create(name=group) group_object.user_set.add(user) @@ -144,12 +146,12 @@ class UserUpdateForm(ValidNewUsernameCheckMixin, forms.ModelForm): label=ugettext_lazy('SSH Keys'), required=False, widget=forms.Textarea, - help_text=\ - ugettext_lazy('Setting an SSH public key will allow this user to ' - 'securely log in to the system without using a ' - 'password. You may enter multiple keys, one on each ' - 'line. Blank lines and lines starting with # will be ' - 'ignored.')) + help_text= \ + ugettext_lazy('Setting an SSH public key will allow this user to ' + 'securely log in to the system without using a ' + 'password. You may enter multiple keys, one on each ' + 'line. Blank lines and lines starting with # will be ' + 'ignored.')) class Meta: """Metadata to control automatic form building.""" @@ -244,8 +246,10 @@ class UserChangePasswordForm(SetPasswordForm): return user + class State1Form(ValidNewUsernameCheckMixin, auth.forms.UserCreationForm): """Firstboot state 1: create a new user.""" + def __init__(self, *args, **kwargs): self.request = kwargs.pop('request') super().__init__(*args, **kwargs) diff --git a/plinth/modules/users/views.py b/plinth/modules/users/views.py index 283078af8..5f89824a0 100644 --- a/plinth/modules/users/views.py +++ b/plinth/modules/users/views.py @@ -22,10 +22,9 @@ from django.contrib.messages.views import SuccessMessageMixin from django.urls import reverse, reverse_lazy from django.views.generic.edit import (CreateView, DeleteView, UpdateView, FormView) -from django.views.generic import ListView, CreateView +from django.views.generic import ListView, CreateView as CV from django.utils.translation import ugettext as _, ugettext_lazy from plinth import cfg -from plinth import kvstore from .forms import CreateUserForm, UserChangePasswordForm, UserUpdateForm, State1Form @@ -169,7 +168,7 @@ class UserChangePassword(ContextMixin, SuccessMessageMixin, FormView): return super(UserChangePassword, self).form_valid(form) -class State1View(CreateView): +class State1View(CV): """Create user account and log the user in.""" template_name = 'firstboot_state1.html' form_class = State1Form