951 lines
35 KiB
Python
951 lines
35 KiB
Python
# -*- coding: utf-8; Mode: Python; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
#
|
|
# «usersetup» - User creation plugin.
|
|
#
|
|
# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Canonical Ltd.
|
|
#
|
|
# Authors:
|
|
#
|
|
# - Colin Watson <cjwatson@ubuntu.com>
|
|
# - Evan Dandrea <ev@ubuntu.com>
|
|
# - Roman Shtylman <shtylman@gmail.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
import os
|
|
import re
|
|
|
|
import debconf
|
|
|
|
from ubiquity import i18n, misc, plugin, validation
|
|
|
|
|
|
NAME = 'usersetup'
|
|
AFTER = 'timezone'
|
|
WEIGHT = 10
|
|
|
|
|
|
def check_hostname(hostname):
|
|
"""Returns a list of reasons why the hostname is invalid."""
|
|
errors = []
|
|
for result in validation.check_hostname(misc.utf8(hostname)):
|
|
if result == validation.HOSTNAME_LENGTH:
|
|
errors.append('hostname_error_length')
|
|
elif result == validation.HOSTNAME_BADCHAR:
|
|
errors.append('hostname_error_badchar')
|
|
elif result == validation.HOSTNAME_BADHYPHEN:
|
|
errors.append('hostname_error_badhyphen')
|
|
elif result == validation.HOSTNAME_BADDOTS:
|
|
errors.append('hostname_error_baddots')
|
|
return errors
|
|
|
|
|
|
def check_username(username):
|
|
"""Returns a list of reasons why the username is invalid."""
|
|
if username:
|
|
if not re.match('[a-z]', username[0]):
|
|
return ['username_error_badfirstchar']
|
|
# Technically both these conditions might hold. However, the common
|
|
# case seems to be that somebody starts typing their name beginning
|
|
# with an upper-case letter, and it's probably sufficient to just
|
|
# issue the first error in that case.
|
|
elif not re.match('^[-a-z0-9_]+$', username):
|
|
return ['username_error_badchar']
|
|
return []
|
|
|
|
|
|
def make_error_string(controller, errors):
|
|
"""Returns a newline-separated string of translated error reasons."""
|
|
return "\n".join([controller.get_string(error) for error in errors])
|
|
|
|
|
|
class PageBase(plugin.PluginUI):
|
|
def __init__(self):
|
|
self.suffix = misc.dmimodel()
|
|
if self.suffix:
|
|
self.suffix = '-%s' % self.suffix
|
|
else:
|
|
if misc.execute("laptop-detect"):
|
|
self.suffix = '-laptop'
|
|
else:
|
|
self.suffix = '-desktop'
|
|
self.allow_password_empty = False
|
|
self.hostname_error_text = ""
|
|
self.domain_connection_error_text = ""
|
|
|
|
def set_fullname(self, value):
|
|
"""Set the user's full name."""
|
|
raise NotImplementedError('set_fullname')
|
|
|
|
def get_fullname(self):
|
|
"""Get the user's full name."""
|
|
raise NotImplementedError('get_fullname')
|
|
|
|
def set_username(self, value):
|
|
"""Set the user's Unix user name."""
|
|
raise NotImplementedError('set_username')
|
|
|
|
def get_username(self):
|
|
"""Get the user's Unix user name."""
|
|
raise NotImplementedError('get_username')
|
|
|
|
def get_password(self):
|
|
"""Get the user's password."""
|
|
raise NotImplementedError('get_password')
|
|
|
|
def get_verified_password(self):
|
|
"""Get the user's password confirmation."""
|
|
raise NotImplementedError('get_verified_password')
|
|
|
|
def set_auto_login(self, value):
|
|
"""Set whether the user should be automatically logged in."""
|
|
raise NotImplementedError('set_auto_login')
|
|
|
|
def get_auto_login(self):
|
|
"""Returns true if the user should be automatically logged in."""
|
|
raise NotImplementedError('get_auto_login')
|
|
|
|
def username_error(self, msg):
|
|
"""The selected username was bad."""
|
|
raise NotImplementedError('username_error')
|
|
|
|
def password_error(self, msg):
|
|
"""The selected password was bad."""
|
|
raise NotImplementedError('password_error')
|
|
|
|
def hostname_error(self, msg):
|
|
""" The hostname had an error """
|
|
raise NotImplementedError('hostname_error')
|
|
|
|
def get_hostname(self):
|
|
"""Get the selected hostname."""
|
|
raise NotImplementedError('get_hostname')
|
|
|
|
def set_hostname(self, hostname):
|
|
raise NotImplementedError('set_hostname')
|
|
|
|
def clear_errors(self):
|
|
pass
|
|
|
|
def info_loop(self, *args):
|
|
"""Verify user input."""
|
|
pass
|
|
|
|
def set_allow_password_empty(self, empty):
|
|
self.allow_password_empty = empty
|
|
|
|
def plugin_translate(self, lang):
|
|
self.hostname_error_text = i18n.get_string('hostname_error', lang)
|
|
self.domain_connection_error_text = i18n.get_string('domain_connection_error', lang)
|
|
|
|
|
|
class PageGtk(PageBase):
|
|
plugin_title = 'ubiquity/text/userinfo_heading_label'
|
|
|
|
def __init__(self, controller, *args, **kwargs):
|
|
from gi.repository import Gio, Gtk
|
|
|
|
PageBase.__init__(self, *args, **kwargs)
|
|
self.resolver = Gio.Resolver.get_default()
|
|
self.controller = controller
|
|
self.username_changed_id = None
|
|
self.hostname_changed_id = None
|
|
self.username_edited = False
|
|
self.hostname_edited = False
|
|
self.hostname_timeout_id = 0
|
|
|
|
builder = Gtk.Builder()
|
|
self.controller.add_builder(builder)
|
|
builder.add_from_file(os.path.join(
|
|
os.environ['UBIQUITY_GLADE'], 'stepUserInfo.ui'))
|
|
builder.connect_signals(self)
|
|
self.page = builder.get_object('stepUserInfo')
|
|
self.username = builder.get_object('username')
|
|
self.hostname = builder.get_object('hostname')
|
|
self.fullname = builder.get_object('fullname')
|
|
self.password = builder.get_object('password')
|
|
self.verified_password = builder.get_object('verified_password')
|
|
self.login_auto = builder.get_object('login_auto')
|
|
self.login_pass = builder.get_object('login_pass')
|
|
self.username_error_label = builder.get_object('username_error_label')
|
|
self.hostname_error_label = builder.get_object('hostname_error_label')
|
|
self.password_error_label = builder.get_object('password_error_label')
|
|
self.login_vbox = builder.get_object('login_vbox')
|
|
|
|
self.username_ok = builder.get_object('username_ok')
|
|
self.hostname_ok = builder.get_object('hostname_ok')
|
|
self.fullname_ok = builder.get_object('fullname_ok')
|
|
self.password_ok = builder.get_object('password_ok')
|
|
self.password_strength = builder.get_object('password_strength')
|
|
|
|
self.login_directory = builder.get_object('login_directory')
|
|
self.login_directory_extra_label = builder.get_object('login_directory_extra_label')
|
|
self.domain_name = builder.get_object('domain_name')
|
|
self.domain_name_ok = builder.get_object('domain_name_ok')
|
|
self.domain_name_error_label = builder.get_object('domain_name_error_label')
|
|
self.domain_user = builder.get_object('domain_user')
|
|
self.domain_user_ok = builder.get_object('domain_user_ok')
|
|
self.domain_user_error_label = builder.get_object('domain_user_error_label')
|
|
self.domain_passwd = builder.get_object('domain_passwd')
|
|
self.directory_testbutton = builder.get_object('directory_testbutton')
|
|
|
|
self.userinfo_notebook = builder.get_object('userinfo_notebook')
|
|
|
|
# Dodgy hack to let us center the contents of the page without it
|
|
# moving as elements appear and disappear, specifically the full name
|
|
# okay check icon and the hostname error messages.
|
|
paddingbox = builder.get_object('paddingbox')
|
|
|
|
def func(box):
|
|
box.get_parent().child_set_property(box, 'expand', False)
|
|
box.set_size_request(box.get_allocation().width / 2, -1)
|
|
|
|
paddingbox.connect('realize', func)
|
|
|
|
# Some signals need to be connected by hand so that we have the
|
|
# handler ids.
|
|
self.username_changed_id = self.username.connect(
|
|
'changed', self.on_username_changed)
|
|
self.hostname_changed_id = self.hostname.connect(
|
|
'changed', self.on_hostname_changed)
|
|
|
|
if not os.path.exists('/usr/sbin/realm'):
|
|
self.login_directory.hide()
|
|
self.login_directory_extra_label.hide()
|
|
self.login_directory_extra_label.set_sensitive(False)
|
|
|
|
if self.controller.oem_config:
|
|
self.fullname.set_text('OEM Configuration (temporary user)')
|
|
self.fullname.set_editable(False)
|
|
self.fullname.set_sensitive(False)
|
|
self.username.set_text('oem')
|
|
self.username.set_editable(False)
|
|
self.username.set_sensitive(False)
|
|
self.username_edited = True
|
|
self.hostname.set_text('oem%s' % self.suffix)
|
|
self.hostname_edited = True
|
|
self.login_vbox.hide()
|
|
# The UserSetup component takes care of preseeding passwd/user-uid.
|
|
misc.execute_root('apt-install', 'oem-config-gtk',
|
|
'oem-config-slideshow-ubuntu')
|
|
|
|
self.resolver_ok = True
|
|
self.plugin_widgets = self.page
|
|
|
|
# Functions called by the Page.
|
|
|
|
def set_fullname(self, value):
|
|
self.fullname.set_text(value)
|
|
|
|
def get_fullname(self):
|
|
return self.fullname.get_text()
|
|
|
|
def set_username(self, value):
|
|
self.username.set_text(value)
|
|
|
|
def get_username(self):
|
|
return self.username.get_text()
|
|
|
|
def get_password(self):
|
|
return self.password.get_text()
|
|
|
|
def get_verified_password(self):
|
|
return self.verified_password.get_text()
|
|
|
|
def set_auto_login(self, value):
|
|
self.login_auto.set_active(value)
|
|
|
|
def get_auto_login(self):
|
|
return self.login_auto.get_active()
|
|
|
|
def username_error(self, msg):
|
|
self.username_ok.hide()
|
|
m = '<small><span foreground="darkred"><b>%s</b></span></small>' % msg
|
|
self.username_error_label.set_markup(m)
|
|
self.username_error_label.show()
|
|
|
|
def hostname_error(self, msg):
|
|
self.hostname_ok.hide()
|
|
m = '<small><span foreground="darkred"><b>%s</b></span></small>' % msg
|
|
self.hostname_error_label.set_markup(m)
|
|
self.hostname_error_label.show()
|
|
|
|
def password_error(self, msg):
|
|
self.password_strength.hide()
|
|
m = '<small><span foreground="darkred"><b>%s</b></span></small>' % msg
|
|
self.password_error_label.set_markup(m)
|
|
self.password_error_label.show()
|
|
|
|
def get_hostname(self):
|
|
return self.hostname.get_text()
|
|
|
|
def set_hostname(self, value):
|
|
self.hostname.set_text(value)
|
|
|
|
def get_login_directory(self):
|
|
""" Use a directory for authentication """
|
|
return self.login_directory.get_active()
|
|
|
|
def get_domain_name(self):
|
|
""" Get the domain name """
|
|
return self.domain_name.get_text()
|
|
|
|
def get_domain_user(self):
|
|
""" Get the domain name """
|
|
return self.domain_user.get_text()
|
|
|
|
def get_domain_passwd(self):
|
|
""" Get the domain name """
|
|
return self.domain_passwd.get_text()
|
|
|
|
def domain_name_error(self, msg):
|
|
self.domain_name_ok.hide()
|
|
m = '<small><span foreground="darkred"><b>%s</b></span></small>' % msg
|
|
self.domain_name_error_label.set_markup(m)
|
|
self.domain_name_error_label.show()
|
|
|
|
def domain_user_error(self, msg):
|
|
self.domain_user_ok.hide()
|
|
m = '<small><span foreground="darkred"><b>%s</b></span></small>' % msg
|
|
self.domain_user_error_label.set_markup(m)
|
|
self.domain_user_error_label.show()
|
|
|
|
def clear_errors(self):
|
|
self.username_error_label.hide()
|
|
self.hostname_error_label.hide()
|
|
self.password_error_label.hide()
|
|
|
|
self.domain_name_error_label.hide()
|
|
|
|
# Callback functions.
|
|
|
|
def info_loop(self, widget):
|
|
"""check if all entries from Identification screen are filled. Callback
|
|
defined in ui file."""
|
|
|
|
if (self.username_changed_id is None or
|
|
self.hostname_changed_id is None):
|
|
return
|
|
|
|
if (widget is not None and widget.get_name() == 'fullname' and
|
|
not self.username_edited):
|
|
self.username.handler_block(self.username_changed_id)
|
|
new_username = misc.utf8(widget.get_text().split(' ')[0])
|
|
new_username = new_username.encode('ascii', 'ascii_transliterate')
|
|
new_username = new_username.decode().lower()
|
|
new_username = re.sub('^[^a-z]+', '', new_username)
|
|
new_username = re.sub('[^-a-z0-9_]', '', new_username)
|
|
self.username.set_text(new_username)
|
|
self.username.handler_unblock(self.username_changed_id)
|
|
elif (widget is not None and widget.get_name() == 'username' and
|
|
not self.hostname_edited):
|
|
self.hostname.handler_block(self.hostname_changed_id)
|
|
t = widget.get_text()
|
|
if t:
|
|
self.hostname.set_text(re.sub(r'\W', '', t) + self.suffix)
|
|
self.hostname.handler_unblock(self.hostname_changed_id)
|
|
|
|
# Do some initial validation. We have to process all the widgets so we
|
|
# can know if we can really show the next button. Otherwise we'd show
|
|
# it on any field being valid.
|
|
complete = True
|
|
|
|
if self.fullname.get_text():
|
|
self.fullname_ok.show()
|
|
else:
|
|
self.fullname_ok.hide()
|
|
|
|
text = self.username.get_text()
|
|
if text:
|
|
errors = check_username(text)
|
|
if errors:
|
|
self.username_error(make_error_string(self.controller, errors))
|
|
complete = False
|
|
else:
|
|
self.username_ok.show()
|
|
self.username_error_label.hide()
|
|
else:
|
|
self.username_ok.hide()
|
|
self.username_error_label.hide()
|
|
complete = False
|
|
|
|
password_ok = validation.gtk_password_validate(
|
|
self.controller,
|
|
self.password,
|
|
self.verified_password,
|
|
self.password_ok,
|
|
self.password_error_label,
|
|
self.password_strength,
|
|
self.allow_password_empty,
|
|
)
|
|
|
|
complete = complete and password_ok
|
|
|
|
txt = self.hostname.get_text()
|
|
self.hostname_ok.show()
|
|
if txt:
|
|
errors = check_hostname(txt)
|
|
if errors:
|
|
self.hostname_error(make_error_string(self.controller, errors))
|
|
complete = False
|
|
self.hostname_ok.hide()
|
|
else:
|
|
self.hostname_ok.show()
|
|
self.hostname_error_label.hide()
|
|
else:
|
|
complete = False
|
|
self.hostname_ok.hide()
|
|
self.hostname_error_label.hide()
|
|
|
|
self.controller.allow_go_forward(complete)
|
|
|
|
def on_password_toggle_visibility(self, widget, icon_pos, event):
|
|
from gi.repository import Gtk
|
|
visibility = self.password.get_visibility()
|
|
self.password.set_visibility(not visibility)
|
|
self.verified_password.set_visibility(not visibility)
|
|
self.password.set_icon_from_icon_name(
|
|
Gtk.EntryIconPosition.SECONDARY, ('view-conceal-symbolic', 'view-reveal-symbolic')[visibility])
|
|
|
|
def on_username_changed(self, widget):
|
|
self.username_edited = (widget.get_text() != '')
|
|
|
|
def on_hostname_changed(self, widget):
|
|
self.hostname_edited = (widget.get_text() != '')
|
|
|
|
if not self.is_automatic:
|
|
# Let's not call this every time the user presses a key.
|
|
from gi.repository import GLib
|
|
if self.hostname_timeout_id:
|
|
GLib.source_remove(self.hostname_timeout_id)
|
|
self.hostname_timeout_id = GLib.timeout_add(
|
|
300, self.hostname_timeout, widget)
|
|
|
|
def lookup_result(self, resolver, result, unused):
|
|
from gi.repository import GLib
|
|
try:
|
|
resolver.lookup_by_name_finish(result)
|
|
except GLib.GError:
|
|
pass
|
|
else:
|
|
self.hostname_error(self.hostname_error_text)
|
|
self.hostname_ok.hide()
|
|
|
|
def hostname_timeout(self, widget):
|
|
if self.hostname_ok.get_property('visible') and self.resolver_ok:
|
|
hostname = widget.get_text()
|
|
for host in (hostname, '%s.local' % hostname):
|
|
self.resolver.lookup_by_name_async(
|
|
host, None, self.lookup_result, None)
|
|
|
|
def detect_bogus_result(self, hostname='xyzzy_does_not_exist'):
|
|
# bug 760884
|
|
# On networks where DNS fakes a response for unknown hosts,
|
|
# don't display a warning for hostnames that already exist.
|
|
self.resolver.lookup_by_name_async(
|
|
hostname, None, self.bogus_lookup_result, None)
|
|
|
|
def bogus_lookup_result(self, resolver, result, unused):
|
|
from gi.repository import GLib
|
|
try:
|
|
resolver.lookup_by_name_finish(result)
|
|
except GLib.GError:
|
|
self.resolver_ok = True
|
|
else:
|
|
self.resolver_ok = False
|
|
|
|
def validate_directory_info(self, widget=None):
|
|
""" Validate domain information """
|
|
domain_name_is_valid = True
|
|
domain_info_complete = True
|
|
|
|
domain_name_txt = self.domain_name.get_text().strip()
|
|
domain_user_txt = self.domain_user.get_text()
|
|
domain_passwd_txt = self.domain_passwd.get_text()
|
|
|
|
self.domain_name_ok.hide()
|
|
if domain_name_txt:
|
|
errors = check_hostname(domain_name_txt)
|
|
if errors:
|
|
self.domain_name_error(make_error_string(self.controller, errors))
|
|
domain_name_is_valid = False
|
|
else:
|
|
self.domain_name_error_label.hide()
|
|
else:
|
|
self.domain_name_error_label.hide()
|
|
domain_name_is_valid = False
|
|
self.directory_testbutton.set_sensitive(domain_name_is_valid)
|
|
domain_info_complete = domain_name_is_valid
|
|
|
|
if domain_user_txt:
|
|
# Don't enforce lower case for AD administrator.
|
|
errors = check_username(domain_user_txt.lower())
|
|
if errors:
|
|
self.domain_user_error(make_error_string(self.controller, errors))
|
|
domain_info_complete = False
|
|
else:
|
|
self.domain_user_ok.show()
|
|
self.domain_user_error_label.hide()
|
|
else:
|
|
self.domain_user_ok.hide()
|
|
self.domain_user_error_label.hide()
|
|
domain_info_complete = False
|
|
|
|
if not domain_passwd_txt:
|
|
domain_info_complete = False
|
|
|
|
self.controller.allow_go_forward(domain_info_complete)
|
|
|
|
def switch_userinfo_tab(self, tab):
|
|
self.userinfo_notebook.set_current_page(tab)
|
|
|
|
if tab == 1:
|
|
self.title = 'ubiquity/text/directory_information_title'
|
|
self.controller.allow_go_backward(True)
|
|
self.validate_directory_info()
|
|
else:
|
|
self.title = self.plugin_title
|
|
self.controller.allow_go_backward(False)
|
|
self.controller.allow_go_forward(True)
|
|
|
|
self.controller._wizard.set_page_title(self)
|
|
|
|
def plugin_set_connectivity_state(self, state):
|
|
# For AD we need network connectivity but it can be local and not
|
|
# necessarily internet connectivity
|
|
if not state:
|
|
self.login_directory.set_active(False)
|
|
self.login_directory.set_sensitive(state)
|
|
|
|
def plugin_on_next_clicked(self):
|
|
if self.userinfo_notebook.get_current_page() == 0 and self.get_login_directory():
|
|
self.switch_userinfo_tab(1)
|
|
return True
|
|
return False
|
|
|
|
def plugin_on_back_clicked(self):
|
|
if self.userinfo_notebook.get_current_page() == 1:
|
|
self.switch_userinfo_tab(0)
|
|
return True
|
|
return False
|
|
|
|
def on_testdomain_click(self, widget):
|
|
if misc.execute('realm', 'discover', self.domain_name.get_text()):
|
|
self.domain_name_ok.show()
|
|
self.domain_name_error_label.hide()
|
|
else:
|
|
self.domain_name_ok.hide()
|
|
self.domain_name_error(self.domain_connection_error_text)
|
|
self.domain_name_error_label.show()
|
|
|
|
def on_login_directory_toggled(self, widget):
|
|
self.login_directory_extra_label.set_sensitive(widget.get_active())
|
|
|
|
|
|
class PageKde(PageBase):
|
|
plugin_breadcrumb = 'ubiquity/text/breadcrumb_user'
|
|
|
|
def __init__(self, controller, *args, **kwargs):
|
|
PageBase.__init__(self, *args, **kwargs)
|
|
self.controller = controller
|
|
|
|
from PyQt5 import uic
|
|
from PyQt5.QtGui import QPixmap, QIcon
|
|
|
|
self.plugin_widgets = uic.loadUi(
|
|
'/usr/share/ubiquity/qt/stepUserSetup.ui')
|
|
self.page = self.plugin_widgets
|
|
|
|
self.username_edited = False
|
|
self.hostname_edited = False
|
|
|
|
if self.controller.oem_config:
|
|
self.page.fullname.setText('OEM Configuration (temporary user)')
|
|
self.page.fullname.setReadOnly(True)
|
|
self.page.fullname.setEnabled(False)
|
|
self.page.username.setText('oem')
|
|
self.page.username.setReadOnly(True)
|
|
self.page.username.setEnabled(False)
|
|
self.page.login_pass.hide()
|
|
self.page.login_auto.hide()
|
|
self.username_edited = True
|
|
self.hostname_edited = True
|
|
|
|
self.page.hostname.setText('oem%s' % self.suffix)
|
|
|
|
# The UserSetup component takes care of preseeding passwd/user-uid.
|
|
misc.execute_root('apt-install', 'oem-config-kde')
|
|
|
|
warningIcon = QPixmap(
|
|
"/usr/share/icons/oxygen/48x48/status/dialog-warning.png")
|
|
self.page.fullname_error_image.setPixmap(warningIcon)
|
|
self.page.username_error_image.setPixmap(warningIcon)
|
|
self.page.password_error_image.setPixmap(warningIcon)
|
|
self.page.hostname_error_image.setPixmap(warningIcon)
|
|
|
|
self.page.show_password.setIcon(QIcon.fromTheme("password-show-off"))
|
|
|
|
self.clear_errors()
|
|
|
|
self.page.fullname.textChanged[str].connect(self.on_fullname_changed)
|
|
self.page.username.textChanged[str].connect(self.on_username_changed)
|
|
self.page.hostname.textChanged[str].connect(self.on_hostname_changed)
|
|
self.page.show_password.toggled.connect(self.on_show_password)
|
|
# self.page.password.textChanged[str].connect(self.on_password_changed)
|
|
# self.page.verified_password.textChanged[str].connect(
|
|
# self.on_verified_password_changed)
|
|
|
|
self.page.password_debug_warning_label.setVisible(
|
|
'UBIQUITY_DEBUG' in os.environ)
|
|
|
|
def on_show_password(self, state):
|
|
from PyQt5 import QtWidgets
|
|
from PyQt5.QtGui import QIcon
|
|
|
|
modes = (QtWidgets.QLineEdit.Password, QtWidgets.QLineEdit.Normal)
|
|
icons = ("password-show-off", "password-show-on")
|
|
self.page.password.setEchoMode(modes[state])
|
|
self.page.verified_password.setEchoMode(modes[state])
|
|
self.page.show_password.setIcon(QIcon.fromTheme(icons[state]))
|
|
|
|
def on_fullname_changed(self):
|
|
# If the user did not manually enter a username create one for him.
|
|
if not self.username_edited:
|
|
self.page.username.blockSignals(True)
|
|
new_username = str(self.page.fullname.text()).split(' ')[0]
|
|
new_username = new_username.encode('ascii', 'ascii_transliterate')
|
|
new_username = new_username.decode().lower()
|
|
self.page.username.setText(new_username)
|
|
self.on_username_changed()
|
|
self.username_edited = False
|
|
self.page.username.blockSignals(False)
|
|
|
|
def on_username_changed(self):
|
|
if not self.hostname_edited:
|
|
self.page.hostname.blockSignals(True)
|
|
self.page.hostname.setText(
|
|
str(self.page.username.text()).strip() + self.suffix)
|
|
self.page.hostname.blockSignals(False)
|
|
|
|
self.username_edited = (self.page.username.text() != '')
|
|
|
|
def on_password_changed(self):
|
|
pass
|
|
|
|
def on_verified_password_changed(self):
|
|
pass
|
|
|
|
def on_hostname_changed(self):
|
|
self.hostname_edited = (self.page.hostname.text() != '')
|
|
|
|
def set_fullname(self, value):
|
|
self.page.fullname.setText(misc.utf8(value))
|
|
|
|
def get_fullname(self):
|
|
return str(self.page.fullname.text())
|
|
|
|
def set_username(self, value):
|
|
self.page.username.setText(misc.utf8(value))
|
|
|
|
def get_username(self):
|
|
return str(self.page.username.text())
|
|
|
|
def get_password(self):
|
|
return str(self.page.password.text())
|
|
|
|
def get_verified_password(self):
|
|
return str(self.page.verified_password.text())
|
|
|
|
def set_auto_login(self, value):
|
|
return self.page.login_auto.setChecked(value)
|
|
|
|
def get_auto_login(self):
|
|
return self.page.login_auto.isChecked()
|
|
|
|
def username_error(self, msg):
|
|
self.page.username_error_reason.setText(msg)
|
|
self.page.username_error_image.show()
|
|
self.page.username_error_reason.show()
|
|
|
|
def password_error(self, msg):
|
|
self.page.password_error_reason.setText(msg)
|
|
self.page.password_error_image.show()
|
|
self.page.password_error_reason.show()
|
|
|
|
def hostname_error(self, msg):
|
|
self.page.hostname_error_reason.setText(msg)
|
|
self.page.hostname_error_image.show()
|
|
self.page.hostname_error_reason.show()
|
|
|
|
def get_hostname(self):
|
|
return str(self.page.hostname.text())
|
|
|
|
def set_hostname(self, value):
|
|
self.page.hostname.setText(value)
|
|
|
|
def clear_errors(self):
|
|
self.page.fullname_error_image.hide()
|
|
self.page.username_error_image.hide()
|
|
self.page.password_error_image.hide()
|
|
self.page.hostname_error_image.hide()
|
|
|
|
self.page.username_error_reason.hide()
|
|
self.page.password_error_reason.hide()
|
|
self.page.hostname_error_reason.hide()
|
|
|
|
|
|
class PageDebconf(PageBase):
|
|
plugin_title = 'ubiquity/text/userinfo_heading_label'
|
|
|
|
def __init__(self, controller, *args, **kwargs):
|
|
self.controller = controller
|
|
|
|
|
|
class PageNoninteractive(PageBase):
|
|
def __init__(self, controller, *args, **kwargs):
|
|
PageBase.__init__(self, *args, **kwargs)
|
|
self.controller = controller
|
|
self.fullname = ''
|
|
self.username = ''
|
|
self.password = ''
|
|
self.verifiedpassword = ''
|
|
self.auto_login = False
|
|
self.encrypt_home = False
|
|
self.console = self.controller._wizard.console
|
|
|
|
def set_fullname(self, value):
|
|
"""Set the user's full name."""
|
|
self.fullname = value
|
|
|
|
def get_fullname(self):
|
|
"""Get the user's full name."""
|
|
if self.controller.oem_config:
|
|
return 'OEM Configuration (temporary user)'
|
|
return self.fullname
|
|
|
|
def set_username(self, value):
|
|
"""Set the user's Unix user name."""
|
|
self.username = value
|
|
|
|
def get_username(self):
|
|
"""Get the user's Unix user name."""
|
|
if self.controller.oem_config:
|
|
return 'oem'
|
|
return self.username
|
|
|
|
def get_password(self):
|
|
"""Get the user's password."""
|
|
return self.controller.dbfilter.db.get('passwd/user-password')
|
|
|
|
def get_verified_password(self):
|
|
"""Get the user's password confirmation."""
|
|
return self.controller.dbfilter.db.get('passwd/user-password-again')
|
|
|
|
def set_auto_login(self, value):
|
|
self.auto_login = value
|
|
|
|
def get_auto_login(self):
|
|
return self.auto_login
|
|
|
|
def username_error(self, msg):
|
|
"""The selected username was bad."""
|
|
print('\nusername error: %s' % msg, file=self.console)
|
|
self.username = input('Username: ')
|
|
|
|
def password_error(self, msg):
|
|
"""The selected password was bad."""
|
|
print('\nBad password: %s' % msg, file=self.console)
|
|
import getpass
|
|
self.password = getpass.getpass('Password: ')
|
|
self.verifiedpassword = getpass.getpass('Password again: ')
|
|
|
|
def set_hostname(self, name):
|
|
pass
|
|
|
|
def get_hostname(self):
|
|
"""Get the selected hostname."""
|
|
# We set a default in install.py in case it isn't preseeded but when we
|
|
# preseed, we are looking for None anyhow.
|
|
return ''
|
|
|
|
def clear_errors(self):
|
|
pass
|
|
|
|
|
|
class Page(plugin.Plugin):
|
|
def prepare(self, unfiltered=False):
|
|
if ('UBIQUITY_FRONTEND' not in os.environ or
|
|
os.environ['UBIQUITY_FRONTEND'] != 'debconf_ui'):
|
|
self.preseed_bool('user-setup/allow-password-weak', True)
|
|
if self.ui.get_hostname() == '':
|
|
try:
|
|
seen = self.db.fget(
|
|
'netcfg/get_hostname', 'seen') == 'true'
|
|
if seen:
|
|
hostname = self.db.get('netcfg/get_hostname')
|
|
domain = self.db.get('netcfg/get_domain')
|
|
if hostname and domain:
|
|
hostname = '%s.%s' % (hostname.rstrip('.'),
|
|
domain.strip('.'))
|
|
if hostname != '':
|
|
self.ui.set_hostname(hostname)
|
|
except debconf.DebconfError:
|
|
pass
|
|
if self.ui.get_fullname() == '':
|
|
try:
|
|
fullname = self.db.get('passwd/user-fullname')
|
|
if fullname != '':
|
|
self.ui.set_fullname(fullname)
|
|
except debconf.DebconfError:
|
|
pass
|
|
if self.ui.get_username() == '':
|
|
try:
|
|
username = self.db.get('passwd/username')
|
|
if username != '':
|
|
self.ui.set_username(username)
|
|
except debconf.DebconfError:
|
|
pass
|
|
try:
|
|
auto_login = self.db.get('passwd/auto-login')
|
|
self.ui.set_auto_login(auto_login == 'true')
|
|
except debconf.DebconfError:
|
|
pass
|
|
try:
|
|
empty = self.db.get('user-setup/allow-password-empty') == 'true'
|
|
except debconf.DebconfError:
|
|
empty = False
|
|
self.ui.set_allow_password_empty(empty)
|
|
|
|
# We need to call info_loop as we switch to the page so the next button
|
|
# gets disabled.
|
|
self.ui.info_loop(None)
|
|
|
|
# Trigger the bogus DNS server detection
|
|
if (not self.is_automatic and hasattr(self.ui, 'detect_bogus_result')):
|
|
self.ui.detect_bogus_result()
|
|
|
|
# We intentionally don't listen to passwd/auto-login or
|
|
# user-setup/encrypt-home because we don't want those alone to force
|
|
# the page to be shown, if they're the only questions not preseeded.
|
|
questions = ['^passwd/user-fullname$', '^passwd/username$',
|
|
'^passwd/user-password$', '^passwd/user-password-again$',
|
|
'ERROR']
|
|
if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
|
|
command = ['/usr/lib/ubiquity/user-setup/user-setup-ask-oem']
|
|
environ = {'OVERRIDE_SYSTEM_USER': '1'}
|
|
return command, questions, environ
|
|
else:
|
|
# TODO: It would be neater to use a wrapper script.
|
|
command = [
|
|
'sh', '-c',
|
|
'/usr/lib/ubiquity/user-setup/user-setup-ask /target && '
|
|
'/usr/share/ubiquity/user-setup-encrypted-swap',
|
|
]
|
|
return command, questions
|
|
|
|
def set(self, question, value):
|
|
if question == 'passwd/username':
|
|
if self.ui.get_username() != '':
|
|
self.ui.set_username(value)
|
|
|
|
def run(self, priority, question):
|
|
return plugin.Plugin.run(self, priority, question)
|
|
|
|
def ok_handler(self):
|
|
self.ui.clear_errors()
|
|
|
|
fullname = self.ui.get_fullname()
|
|
username = self.ui.get_username().strip()
|
|
password = self.ui.get_password()
|
|
password_confirm = self.ui.get_verified_password()
|
|
auto_login = self.ui.get_auto_login()
|
|
|
|
self.preseed('passwd/user-fullname', fullname)
|
|
self.preseed('passwd/username', username)
|
|
# TODO: maybe encrypt these first
|
|
self.preseed('passwd/user-password', password)
|
|
self.preseed('passwd/user-password-again', password_confirm)
|
|
if self.ui.controller.oem_config:
|
|
self.preseed('passwd/user-uid', '29999')
|
|
else:
|
|
self.preseed('passwd/user-uid', '')
|
|
self.preseed_bool('passwd/auto-login', auto_login)
|
|
self.preseed_bool('user-setup/encrypt-home', False)
|
|
|
|
hostname = self.ui.get_hostname()
|
|
|
|
# check if the hostname had errors
|
|
errors = check_hostname(hostname)
|
|
|
|
# showing warning message is error is set
|
|
if errors:
|
|
self.ui.hostname_error(
|
|
make_error_string(self.ui.controller, errors))
|
|
self.done = False
|
|
self.enter_ui_loop()
|
|
return
|
|
|
|
if hostname is not None and hostname != '':
|
|
hd = hostname.split('.', 1)
|
|
self.preseed('netcfg/get_hostname', hd[0])
|
|
if len(hd) > 1:
|
|
self.preseed('netcfg/get_domain', hd[1])
|
|
else:
|
|
self.preseed('netcfg/get_domain', '')
|
|
|
|
if hasattr(self.ui, 'get_login_directory'):
|
|
self.preseed_bool('ubiquity/login_use_directory', self.ui.get_login_directory())
|
|
if self.ui.get_login_directory():
|
|
self.preseed('ubiquity/directory_domain', self.ui.get_domain_name())
|
|
self.preseed('ubiquity/directory_user', self.ui.get_domain_user())
|
|
self.preseed('ubiquity/directory_passwd', self.ui.get_domain_passwd())
|
|
|
|
plugin.Plugin.ok_handler(self)
|
|
|
|
def error(self, priority, question):
|
|
if question.startswith('passwd/username-'):
|
|
self.ui.username_error(self.extended_description(question))
|
|
elif question.startswith('user-setup/password-'):
|
|
self.ui.password_error(self.extended_description(question))
|
|
else:
|
|
self.ui.error_dialog(
|
|
self.description(question),
|
|
self.extended_description(question))
|
|
return plugin.Plugin.error(self, priority, question)
|
|
|
|
|
|
class Install(plugin.InstallPlugin):
|
|
def prepare(self, unfiltered=False):
|
|
if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
|
|
command = ['/usr/lib/ubiquity/user-setup/user-setup-apply']
|
|
environ = {'OVERRIDE_SYSTEM_USER': '1'}
|
|
else:
|
|
command = [
|
|
'/usr/lib/ubiquity/user-setup/user-setup-apply', '/target']
|
|
environ = {}
|
|
if os.path.exists('/var/lib/ubiquity/encrypted-swap'):
|
|
environ['OVERRIDE_ALREADY_ENCRYPTED_SWAP'] = '1'
|
|
return command, [], environ
|
|
|
|
def error(self, priority, question):
|
|
self.ui.error_dialog(self.description(question),
|
|
self.extended_description(question))
|
|
return plugin.InstallPlugin.error(self, priority, question)
|
|
|
|
def install(self, target, progress, *args, **kwargs):
|
|
progress.info('ubiquity/install/user')
|
|
return plugin.InstallPlugin.install(
|
|
self, target, progress, *args, **kwargs)
|