diff options
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | pyanaconda/ui/gui/spokes/password.glade | 151 | ||||
-rw-r--r-- | pyanaconda/ui/gui/spokes/password.py | 167 |
3 files changed, 320 insertions, 0 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in index 977d425c9..5fee5ce59 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -84,6 +84,7 @@ pyanaconda/ui/gui/spokes/custom.py pyanaconda/ui/gui/spokes/datetime_spoke.py pyanaconda/ui/gui/spokes/keyboard.py pyanaconda/ui/gui/spokes/network.py +pyanaconda/ui/gui/spokes/password.py pyanaconda/ui/gui/spokes/software.py pyanaconda/ui/gui/spokes/source.py pyanaconda/ui/gui/spokes/storage.py @@ -97,6 +98,7 @@ pyanaconda/ui/gui/spokes/network.glade pyanaconda/ui/gui/spokes/software.glade pyanaconda/ui/gui/spokes/storage.glade pyanaconda/ui/gui/spokes/keyboard.glade +pyanaconda/ui/gui/spokes/password.glade pyanaconda/ui/gui/spokes/source.glade pyanaconda/ui/gui/spokes/welcome.glade pyanaconda/ui/gui/spokes/custom.glade diff --git a/pyanaconda/ui/gui/spokes/password.glade b/pyanaconda/ui/gui/spokes/password.glade new file mode 100644 index 000000000..38c21a811 --- /dev/null +++ b/pyanaconda/ui/gui/spokes/password.glade @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <!-- interface-requires AnacondaWidgets 1.0 --> + <object class="AnacondaSpokeWindow" id="passwordWindow"> + <property name="startup_id">filler</property> + <property name="can_focus">False</property> + <property name="startup_id">filler</property> + <property name="window_name">ROOT PASSWORD</property> + <signal name="back-clicked" handler="on_back_clicked" swapped="no"/> + <child internal-child="main_box"> + <object class="GtkBox" id="AnacondaSpokeWindow-main_box1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child internal-child="nav_area"> + <object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1"> + <property name="can_focus">False</property> + <property name="margin_left">6</property> + <property name="margin_right">6</property> + <property name="margin_top">6</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child internal-child="alignment"> + <object class="GtkAlignment" id="AnacondaSpokeWindow-alignment1"> + <property name="can_focus">False</property> + <property name="yalign">0</property> + <property name="xscale">0</property> + <property name="yscale">0.5</property> + <child internal-child="action_area"> + <object class="GtkBox" id="AnacondaSpokeWindow-action_area1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkGrid" id="pwgrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="pwlabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Root Password:</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="confirmlabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Confirm:</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="pw"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="visibility">False</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="confirm"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="visibility">False</property> + <property name="invisible_char">●</property> + <property name="activates_default">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="capslock"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="details"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">The root account is used for administering the system. Enter a password for the root user. Leave blank to disable (lock) the account.</property> + <property name="wrap">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">2</property> + <property name="height">1</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/pyanaconda/ui/gui/spokes/password.py b/pyanaconda/ui/gui/spokes/password.py new file mode 100644 index 000000000..ac6e35364 --- /dev/null +++ b/pyanaconda/ui/gui/spokes/password.py @@ -0,0 +1,167 @@ +# root password spoke class +# +# Copyright (C) 2012 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties 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 Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Jesse Keating <jkeating@redhat.com> +# + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) +N_ = lambda x: x + +from gi.repository import Gtk + +from pyanaconda.users import cryptPassword +import pwquality +import string + +from pyanaconda.ui.gui.spokes import NormalSpoke +# Need a new category I guess +from pyanaconda.ui.gui.categories.localization import LocalizationCategory +#from _isys import isCapsLockEnabled + +__all__ = ["PasswordSpoke"] + + +class PasswordSpoke(NormalSpoke): + builderObjects = ["passwordWindow"] + + mainWidgetName = "passwordWindow" + uiFile = "spokes/password.glade" + + # update this category to something... new? + category = LocalizationCategory + + icon = "dialog-password-symbolic" + title = N_("ROOT PASSWORD") + + def __init__(self, *args): + NormalSpoke.__init__(self, *args) + self._password = None + self._error = False + self._oldweak = None + + def initialize(self): + NormalSpoke.initialize(self) + # Set the rootpw to locked by default, setting a password is optional + self.data.rootpw.lock = True + # place holders for the text boxes + self.pw = self.builder.get_object("pw") + self.confirm = self.builder.get_object("confirm") + + def refresh(self): +# self.setCapsLockLabel() + self.pw.grab_focus() + +# Caps lock detection isn't hooked up right now +# def setCapsLockLabel(self): +# if isCapsLockEnabled(): +# self.capslock.set_text("<b>" + _("Caps Lock is on.") + "</b>") +# self.capslock.set_use_markup(True) +# else: +# self.capslock..set_text("") + + @property + def status(self): + if self._error: + return _("Error setting root password") + elif self.data.rootpw.lock: + return _("Root account is disabled") + else: + return _("Root password is set") + + def apply(self): + if self._password: + self.data.rootpw.password = cryptPassword(self._password) + self.data.rootpw.isCrypted = True + self.data.rootpw.lock = False + else: + # Blank password case, disable the account + self.data.rootpw.lock = True + self.data.rootpw.password = '' + self.data.rootpw.isCrypted = False + + @property + def completed(self): + # FUTURE -- update completed to false if package payload doesn't + # include firstboot and some environment to run it in + # We are by default complete, but locked. If a user attempts to set + # a password but fails, then we are no longer complete. + return not self._error + + def _validatePassword(self): + # Do various steps to validate the password + # sets self._error to an error string + # Return True if valid, False otherwise + pw = self.pw.get_text() + confirm = self.confirm.get_text() + + # if both pw and confirm are blank, password is disabled. + if (pw and not confirm) or (confirm and not pw): + self._error = _("You must enter your root password " + "and confirm it by typing it a second " + "time to continue.") + return False + + if pw != confirm: + self._error = _("The passwords you entered were " + "different. Please try again.") + return False + + if pw and len(pw) < 6: + self._error = _("The root password must be at least " + "six characters long.") + return False + + if pw: + try: + settings = pwquality.PWQSettings() + settings.read_config() + settings.check(pw, None, "root") + except pwquality.PWQError as (e, msg): + if pw == self._oldweak: + # We got a second attempt with the same weak password + pass + else: + self._error = _("You have provided a weak password: %s. " + " Press Back again to use anyway.") % msg + self._oldweak = pw + return False + + legal = string.digits + string.ascii_letters + string.punctuation + " " + for letter in pw: + if letter not in legal: + self._error = _("Requested password contains " + "non-ASCII characters, which are " + "not allowed.") + return False + + # if no errors, clear the info for next time we go into the spoke + self._password = pw + self.window.clear_info() + self._error = False + return True + + def on_back_clicked(self, button): + if self._validatePassword(): + self.window.clear_info() + NormalSpoke.on_back_clicked(self, button) + else: + self.window.clear_info() + self.window.set_info(Gtk.MessageType.WARNING, self._error) + self.pw.grab_focus() + self.window.show_all()
\ No newline at end of file |