anaconda: Modify user configuration spoke for Qubes OS
This commit is contained in:
parent
34e37e3dc8
commit
ed17da5dc6
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<!-- Generated with glade 3.19.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.6"/>
|
||||
<requires lib="AnacondaWidgets" version="1.0"/>
|
||||
@ -53,33 +53,15 @@
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">8</property>
|
||||
<property name="column_spacing">9</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="xpad">10</property>
|
||||
<property name="label" translatable="yes" context="GUI|User">_Full name</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">t_fullname</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="xpad">10</property>
|
||||
<property name="label" translatable="yes" context="GUI|User">_User name</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">t_username</property>
|
||||
<property name="xalign">1</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@ -89,23 +71,6 @@
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="t_fullname">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="caps_lock_warning">False</property>
|
||||
<signal name="changed" handler="full_name_changed" swapped="no"/>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="t_fullname-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">Full Name</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="t_username">
|
||||
<property name="visible">True</property>
|
||||
@ -126,11 +91,11 @@
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="xpad">10</property>
|
||||
<property name="label" translatable="yes" context="GUI|User">_Password</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">t_password</property>
|
||||
<property name="xalign">1</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@ -144,11 +109,11 @@
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="xpad">10</property>
|
||||
<property name="label" translatable="yes" context="GUI|User">_Confirm password</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">t_verifypassword</property>
|
||||
<property name="xalign">1</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@ -197,9 +162,9 @@
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Tip:</b> Keep your user name shorter than 32 characters and do not use spaces.</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
@ -266,44 +231,10 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="c_admin">
|
||||
<property name="label" translatable="yes" context="GUI|User">_Make this user administrator</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="b_advanced">
|
||||
<property name="label" translatable="yes" context="GUI|User">_Advanced...</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_advanced_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
|
@ -40,7 +40,7 @@ from pyanaconda.constants import ANACONDA_ENVIRON, FIRSTBOOT_ENVIRON,\
|
||||
PW_ASCII_CHARS, PASSWORD_ASCII
|
||||
from pyanaconda.regexes import GECOS_VALID, USERNAME_VALID, GROUPNAME_VALID, GROUPLIST_FANCY_PARSE
|
||||
|
||||
__all__ = ["UserSpoke", "AdvancedUserDialog"]
|
||||
__all__ = ["UserSpoke"]
|
||||
|
||||
class AdvancedUserDialog(GUIObject, GUIDialogInputCheckHandler):
|
||||
"""
|
||||
@ -232,7 +232,7 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
builderObjects = ["userCreationWindow"]
|
||||
|
||||
mainWidgetName = "userCreationWindow"
|
||||
focusWidgetName = "t_fullname"
|
||||
focusWidgetName = "t_username"
|
||||
uiFile = "spokes/user.glade"
|
||||
helpFile = "UserSpoke.xml"
|
||||
|
||||
@ -243,9 +243,6 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
|
||||
@classmethod
|
||||
def should_run(cls, environment, data):
|
||||
# The Qubes installer still uses old firstboot to create users (TODO)
|
||||
return False
|
||||
|
||||
# the user spoke should run always in the anaconda and in firstboot only
|
||||
# when doing reconfig or if no user has been created in the installation
|
||||
if environment == ANACONDA_ENVIRON:
|
||||
@ -271,17 +268,17 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
self._user = self.data.user.userList[0]
|
||||
else:
|
||||
self._user = self.data.UserData()
|
||||
|
||||
self._wheel = self.data.GroupData(name="wheel")
|
||||
self._groupDict = {"wheel": self._wheel}
|
||||
self._qubes = self.data.GroupData(name="qubes")
|
||||
|
||||
self._groupDict = {"wheel": self._wheel, "qubes": self._qubes}
|
||||
|
||||
# placeholders for the text boxes
|
||||
self.fullname = self.builder.get_object("t_fullname")
|
||||
self.username = self.builder.get_object("t_username")
|
||||
self.pw = self.builder.get_object("t_password")
|
||||
self.confirm = self.builder.get_object("t_verifypassword")
|
||||
self.admin = self.builder.get_object("c_admin")
|
||||
self.usepassword = self.builder.get_object("c_usepassword")
|
||||
self.b_advanced = self.builder.get_object("b_advanced")
|
||||
|
||||
# Counters for checks that ask the user to click Done to confirm
|
||||
self._waiveStrengthClicks = 0
|
||||
@ -336,8 +333,6 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
self.add_re_check(self.username, re.compile(USERNAME_VALID.pattern + r'|^$'),
|
||||
_("Invalid user name"))
|
||||
|
||||
self.add_re_check(self.fullname, GECOS_VALID, _("Full name cannot contain colon characters"))
|
||||
|
||||
# Modify the GUI based on the kickstart and policy information
|
||||
# This needs to happen after the input checks have been created, since
|
||||
# the Gtk signal handlers use the input check variables.
|
||||
@ -358,18 +353,12 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
# User isn't allowed to change whether password is required or not
|
||||
self.usepassword.set_sensitive(False)
|
||||
|
||||
self._advanced = AdvancedUserDialog(self._user, self._groupDict,
|
||||
self.data)
|
||||
self._advanced.initialize()
|
||||
|
||||
def refresh(self):
|
||||
# Enable the input checks in case they were disabled on the last exit
|
||||
for check in self.checks:
|
||||
check.enabled = True
|
||||
|
||||
self.username.set_text(self._user.name)
|
||||
self.fullname.set_text(self._user.gecos)
|
||||
self.admin.set_active(self._wheel.name in self._user.groups)
|
||||
|
||||
self.pw.emit("changed")
|
||||
self.confirm.emit("changed")
|
||||
@ -377,12 +366,8 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
if self.username.get_text() and self.usepassword.get_active() and \
|
||||
self._user.password == "":
|
||||
self.pw.grab_focus()
|
||||
elif self.fullname.get_text():
|
||||
self.username.grab_focus()
|
||||
else:
|
||||
self.fullname.grab_focus()
|
||||
|
||||
self.b_advanced.set_sensitive(bool(self._user.name))
|
||||
self.username.grab_focus()
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
@ -420,7 +405,6 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
self._user.password_kickstarted = False
|
||||
|
||||
self._user.name = self.username.get_text()
|
||||
self._user.gecos = self.fullname.get_text()
|
||||
|
||||
# Remove any groups that were created in a previous visit to this spoke
|
||||
self.data.group.groupList = [g for g in self.data.group.groupList \
|
||||
@ -428,15 +412,14 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
|
||||
# the user will be created only if the username is set
|
||||
if self._user.name:
|
||||
if self.admin.get_active() and \
|
||||
self._wheel.name not in self._user.groups:
|
||||
if self._wheel.name not in self._user.groups:
|
||||
self._user.groups.append(self._wheel.name)
|
||||
elif not self.admin.get_active() and \
|
||||
self._wheel.name in self._user.groups:
|
||||
self._user.groups.remove(self._wheel.name)
|
||||
|
||||
if self._qubes.name not in self._user.groups:
|
||||
self._user.groups.append(self._qubes.name)
|
||||
|
||||
anaconda_groups = [self._groupDict[g] for g in self._user.groups
|
||||
if g != self._wheel.name]
|
||||
if g not in (self._wheel.name, self._qubes.name)]
|
||||
|
||||
self.data.group.groupList += anaconda_groups
|
||||
|
||||
@ -515,28 +498,13 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
|
||||
if editable.get_text() == "":
|
||||
self.guesser[editable] = True
|
||||
self.b_advanced.set_sensitive(False)
|
||||
else:
|
||||
self.guesser[editable] = False
|
||||
self.b_advanced.set_sensitive(True)
|
||||
|
||||
# Re-run the password checks against the new username
|
||||
self.pw.emit("changed")
|
||||
self.confirm.emit("changed")
|
||||
|
||||
def full_name_changed(self, editable=None, data=None):
|
||||
"""Called by Gtk callback when the full name field changes.
|
||||
It guesses the username and hostname, strips diacritics
|
||||
and make those lowercase.
|
||||
"""
|
||||
|
||||
# after the text is updated in guesser, the guess has to be reenabled
|
||||
if self.guesser[self.username]:
|
||||
fullname = self.fullname.get_text()
|
||||
username = guess_username(fullname)
|
||||
self.username.set_text(username)
|
||||
self.guesser[self.username] = True
|
||||
|
||||
def _checkPasswordEmpty(self, inputcheck):
|
||||
"""Check whether a password has been specified at all.
|
||||
|
||||
@ -637,26 +605,6 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
|
||||
|
||||
return InputCheck.CHECK_OK
|
||||
|
||||
def on_advanced_clicked(self, _button, data=None):
|
||||
"""Handler for the Advanced.. button. It starts the Advanced dialog
|
||||
for setting homedit, uid, gid and groups.
|
||||
"""
|
||||
|
||||
self._user.name = self.username.get_text()
|
||||
|
||||
if self.admin.get_active() and \
|
||||
self._wheel.name not in self._user.groups:
|
||||
self._user.groups.append(self._wheel.name)
|
||||
elif not self.admin.get_active() and \
|
||||
self._wheel.name in self._user.groups:
|
||||
self._user.groups.remove(self._wheel.name)
|
||||
|
||||
self._advanced.refresh()
|
||||
with self.main_window.enlightbox(self._advanced.window):
|
||||
self._advanced.run()
|
||||
|
||||
self.admin.set_active(self._wheel.name in self._user.groups)
|
||||
|
||||
def on_back_clicked(self, button):
|
||||
# If the failed check is for non-ASCII characters,
|
||||
# add a click to the counter and check again
|
||||
|
@ -43,11 +43,9 @@ class UserSpoke(FirstbootSpokeMixIn, EditTUISpoke):
|
||||
|
||||
edit_fields = [
|
||||
Entry("Create user", "_create", EditTUISpoke.CHECK, True),
|
||||
Entry("Fullname", "gecos", GECOS_VALID, lambda self, args: args._create),
|
||||
Entry("Username", "name", USERNAME_VALID, lambda self, args: args._create),
|
||||
Entry("Use password", "_use_password", EditTUISpoke.CHECK, lambda self, args: args._create),
|
||||
Entry("Password", "_password", EditTUISpoke.PASSWORD, lambda self, args: args._use_password and args._create),
|
||||
Entry("Administrator", "_admin", EditTUISpoke.CHECK, lambda self, args: args._create),
|
||||
Entry("Groups", "_groups", GROUPLIST_SIMPLE_VALID, lambda self, args: args._create)
|
||||
]
|
||||
|
||||
@ -91,7 +89,6 @@ class UserSpoke(FirstbootSpokeMixIn, EditTUISpoke):
|
||||
self.errors = []
|
||||
|
||||
def refresh(self, args=None):
|
||||
self.args._admin = "wheel" in self.args.groups
|
||||
self.args._groups = ", ".join(self.args.groups)
|
||||
|
||||
# if we have any errors, display them
|
||||
@ -135,20 +132,14 @@ class UserSpoke(FirstbootSpokeMixIn, EditTUISpoke):
|
||||
return _("User %s will be created") % self.data.user.userList[0].name
|
||||
|
||||
def apply(self):
|
||||
if self.args.gecos and not self.args.name:
|
||||
username = guess_username(self.args.gecos)
|
||||
if not USERNAME_VALID.match(username):
|
||||
self.errors.append(_("Invalid user name: %s.\n") % username)
|
||||
else:
|
||||
self.args.name = guess_username(self.args.gecos)
|
||||
|
||||
self.args.groups = [g.strip() for g in self.args._groups.split(",") if g]
|
||||
|
||||
# Add or remove the user from wheel group
|
||||
if self.args._admin and "wheel" not in self.args.groups:
|
||||
# Add the user to the wheel and qubes groups
|
||||
if "wheel" not in self.args.groups:
|
||||
self.args.groups.append("wheel")
|
||||
elif not self.args._admin and "wheel" in self.args.groups:
|
||||
self.args.groups.remove("wheel")
|
||||
|
||||
if "qubes" not in self.args.groups:
|
||||
self.args.groups.append("qubes")
|
||||
|
||||
# Add or remove the user from userlist as needed
|
||||
if self.args._create and (self.args not in self.data.user.userList and self.args.name):
|
||||
|
Loading…
Reference in New Issue
Block a user