# -*- coding: utf-8 -*-
# preferenceswindow.py
# Copyright (C) 2013 LEAP
#
# 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 3 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, see .
"""
Preferences window
"""
import logging
from functools import partial
from PySide import QtGui
from leap.bitmask.gui.ui_preferences import Ui_Preferences
from leap.soledad.client import NoStorageSecret
from leap.bitmask.crypto.srpauth import SRPAuthBadPassword
logger = logging.getLogger(__name__)
class PreferencesWindow(QtGui.QDialog):
"""
Window that displays the preferences.
"""
WEAK_PASSWORDS = ("123456", "qweasd", "qwerty", "password")
def __init__(self, parent, srp_auth, soledad):
"""
:param parent: parent object of the PreferencesWindow.
:parent type: QWidget
:param srp_auth: SRPAuth object configured in the main app.
:type srp_auth: SRPAuth
:param soledad: Soledad object configured in the main app.
:type soledad: Soledad
"""
QtGui.QDialog.__init__(self, parent)
self._srp_auth = srp_auth
self._soledad = soledad
# Load UI
self.ui = Ui_Preferences()
self.ui.setupUi(self)
self.ui.lblPasswordChangeStatus.setVisible(False)
# Connections
self.ui.pbChangePassword.clicked.connect(self._change_password)
def _basic_password_checks(self, username, password, password2):
"""
Performs basic password checks to avoid really easy passwords.
:param username: username provided at the registrarion form
:type username: str
:param password: password from the registration form
:type password: str
:param password2: second password from the registration form
:type password: str
:returns: True and empty message if all the checks pass,
False and an error message otherwise
:rtype: tuple(bool, str)
"""
message = None
if message is None and password != password2:
message = self.tr("Passwords don't match")
if message is None and len(password) < 6:
message = self.tr("Password too short")
if message is None and password in self.WEAK_PASSWORDS:
message = self.tr("Password too easy")
if message is None and username == password:
message = self.tr("Password equal to username")
return message is None, message
def _set_password_change_status(self, status, error=False, success=False):
"""
Sets the status label for the password change.
:param status: status message to display, can be HTML
:type status: str
"""
if error:
status = "%s" % (status,)
elif success:
status = "%s" % (status,)
self.ui.lblPasswordChangeStatus.setVisible(True)
self.ui.lblPasswordChangeStatus.setText(status)
def _set_changing_password(self, disable):
"""
Enables or disables the widgets in the password change group box.
:param disable: True if the widgets should be disabled and
it displays the status label that shows that is
changing the password.
False if they should be enabled.
:type disable: bool
"""
if disable:
self._set_password_change_disable(self.tr("Changing password..."))
self.ui.leCurrentPassword.setEnabled(not disable)
self.ui.leNewPassword.setEnabled(not disable)
self.ui.leNewPassword2.setEnabled(not disable)
self.ui.pbChangePassword.setEnabled(not disable)
def _change_password(self):
"""
Changes the user's password if the inputboxes are correctly filled.
"""
username = self._srp_auth.get_username()
current_password = self.ui.leCurrentPassword.text()
new_password = self.ui.leNewPassword.text()
new_password2 = self.ui.leNewPassword2.text()
ok, msg = self._basic_password_checks(
username, new_password, new_password2)
if not ok:
self._set_changing_password(False)
self._set_password_change_status(msg, error=True)
self.ui.leNewPassword.setFocus()
return
self._set_changing_password(True)
d = self._srp_auth.change_password(current_password, new_password)
d.addCallback(partial(self._change_password_success, new_password))
d.addErrback(self._change_password_problem)
def _change_password_success(self, new_password, _):
"""
Callback used to display a successfully performed action.
:param new_password: the new password for the user.
:type new_password: str.
:param _: the returned data from self._srp_auth.change_password
Ignored
"""
logger.debug("SRP password changed successfully.")
try:
self._soledad.change_passphrase(str(new_password))
logger.debug("Soledad password changed successfully.")
except NoStorageSecret:
logger.debug(
"No storage secret for password change in Soledad.")
self._set_password_change_status(
self.tr("Password changed successfully."), success=True)
self._clear_inputs()
self._set_changing_password(False)
def _change_password_problem(self, failure):
"""
Errback called if there was a problem with the deferred.
Also is used to display an error message.
:param failure: the cause of the method failed.
:type failure: twisted.python.Failure
"""
logger.error("Error changing password: %s", (failure, ))
problem = self.tr("There was a problem changing the password.")
if failure.check(SRPAuthBadPassword):
problem = self.tr("You did not enter a correct current password.")
self._set_password_change_status(problem, error=True)
self._set_changing_password(False)
failure.trap(Exception)
def _clear_inputs(self):
"""
Clear the contents of the inputs.
"""
self.ui.leCurrentPassword.setText("")
self.ui.leNewPassword.setText("")
self.ui.leNewPassword2.setText("")