diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | changes/VERSION_COMPAT | 1 | ||||
| -rw-r--r-- | changes/feature-4448_advanced-key-management | 1 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/advanced_key_management.py | 185 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 19 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/advanced_key_management.ui | 153 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/mainwindow.ui | 13 | 
7 files changed, 371 insertions, 3 deletions
| @@ -20,7 +20,7 @@ TRANSLAT_DIR = data/translations  PROJFILE = data/bitmask.pro  #UI files to compile -UI_FILES = loggerwindow.ui mainwindow.ui wizard.ui login.ui preferences.ui eip_status.ui mail_status.ui eippreferences.ui +UI_FILES = loggerwindow.ui mainwindow.ui wizard.ui login.ui preferences.ui eip_status.ui mail_status.ui eippreferences.ui advanced_key_management.ui  #Qt resource files to compile  RESOURCES = locale.qrc loggerwindow.qrc mainwindow.qrc icons.qrc diff --git a/changes/VERSION_COMPAT b/changes/VERSION_COMPAT index e689170c..71a3d477 100644 --- a/changes/VERSION_COMPAT +++ b/changes/VERSION_COMPAT @@ -9,3 +9,4 @@  # BEGIN DEPENDENCY LIST -------------------------  # leap.foo.bar>=x.y.z  leap.mail >= 0.3.7 +leap.keymanager >= 0.3.6 diff --git a/changes/feature-4448_advanced-key-management b/changes/feature-4448_advanced-key-management new file mode 100644 index 00000000..85e77c39 --- /dev/null +++ b/changes/feature-4448_advanced-key-management @@ -0,0 +1 @@ +- Add advanced key management feature (Closes #4448). diff --git a/src/leap/bitmask/gui/advanced_key_management.py b/src/leap/bitmask/gui/advanced_key_management.py new file mode 100644 index 00000000..2c0fa034 --- /dev/null +++ b/src/leap/bitmask/gui/advanced_key_management.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +# advanced_key_management.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 <http://www.gnu.org/licenses/>. +""" +Advanced Key Management +""" +import logging + +from PySide import QtGui +from zope.proxy import sameProxiedObjects + +from leap.keymanager import openpgp +from leap.keymanager.errors import KeyAddressMismatch, KeyFingerprintMismatch +from leap.bitmask.services import get_service_display_name, MX_SERVICE +from ui_advanced_key_management import Ui_AdvancedKeyManagement + +logger = logging.getLogger(__name__) + + +class AdvancedKeyManagement(QtGui.QWidget): +    """ +    Advanced Key Management +    """ +    def __init__(self, user, keymanager, soledad): +        """ +        :param user: the current logged in user. +        :type user: unicode +        :param keymanager: the existing keymanager instance +        :type keymanager: KeyManager +        :param soledad: a loaded instance of Soledad +        :type soledad: Soledad +        """ +        QtGui.QWidget.__init__(self) + +        self.ui = Ui_AdvancedKeyManagement() +        self.ui.setupUi(self) + +        # if Soledad is not started yet +        if sameProxiedObjects(soledad, None): +            self.ui.container.setEnabled(False) +            msg = self.tr("<span style='color:#0000FF;'>NOTE</span>: " +                          "To use this, you need to enable/start {0}.") +            msg = msg.format(get_service_display_name(MX_SERVICE)) +            self.ui.lblStatus.setText(msg) +            return +        else: +            msg = self.tr( +                "<span style='color:#ff0000;'>WARNING</span>:<br>" +                "This is an experimental feature, you can lose access to " +                "existing e-mails.") +            self.ui.lblStatus.setText(msg) + +        self._keymanager = keymanager +        self._soledad = soledad + +        self._key = keymanager.get_key(user, openpgp.OpenPGPKey) +        self._key_priv = keymanager.get_key( +            user, openpgp.OpenPGPKey, private=True) + +        # show current key information +        self.ui.leUser.setText(user) +        self.ui.leKeyID.setText(self._key.key_id) +        self.ui.leFingerprint.setText(self._key.fingerprint) + +        # set up connections +        self.ui.pbImportKeys.clicked.connect(self._import_keys) +        self.ui.pbExportKeys.clicked.connect(self._export_keys) + +    def _import_keys(self): +        """ +        Imports the user's key pair. +        Those keys need to be ascii armored. +        """ +        fileName, filtr = QtGui.QFileDialog.getOpenFileName( +            self, self.tr("Open keys file"), +            options=QtGui.QFileDialog.DontUseNativeDialog) + +        if fileName: +            new_key = '' +            try: +                with open(fileName, 'r') as keys_file: +                    new_key = keys_file.read() +            except IOError as e: +                logger.error("IOError importing key. {0!r}".format(e)) +                QtGui.QMessageBox.critical( +                    self, self.tr("Input/Output error"), +                    self.tr("There was an error accessing the file.\n" +                            "Import canceled.")) +                return + +            keymanager = self._keymanager +            try: +                public_key, private_key = keymanager.parse_openpgp_ascii_key( +                    new_key) +            except (KeyAddressMismatch, KeyFingerprintMismatch) as e: +                logger.error(repr(e)) +                QtGui.QMessageBox.warning( +                    self, self.tr("Data mismatch"), +                    self.tr("The public and private key should have the " +                            "same address and fingerprint.\n" +                            "Import canceled.")) +                return + +            if public_key is None or private_key is None: +                QtGui.QMessageBox.warning( +                    self, self.tr("Missing key"), +                    self.tr("You need to provide the public AND private " +                            "key in the same file.\n" +                            "Import canceled.")) +                return + +            if public_key.address != self._key.address: +                logger.error("The key does not match the ID") +                QtGui.QMessageBox.warning( +                    self, self.tr("Address mismatch"), +                    self.tr("The identity for the key needs to be the same " +                            "as your user address.\n" +                            "Import canceled.")) +                return + +            question = self.tr("Are you sure that you want to replace " +                               "the current key pair whith the imported?") +            res = QtGui.QMessageBox.question( +                None, "Change key pair", question, +                QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, +                QtGui.QMessageBox.No)  # default No + +            if res == QtGui.QMessageBox.No: +                return + +            keymanager.delete_key(self._key) +            keymanager.delete_key(self._key_priv) +            keymanager.put_key(public_key) +            keymanager.put_key(private_key) +            keymanager.send_key(openpgp.OpenPGPKey) + +            logger.debug('Import ok') + +            QtGui.QMessageBox.information( +                self, self.tr("Import Successful"), +                self.tr("The key pair was imported successfully.")) +        else: +            logger.debug('Import canceled by the user.') + +    def _export_keys(self): +        """ +        Exports the user's key pair. +        """ +        fileName, filtr = QtGui.QFileDialog.getSaveFileName( +            self, self.tr("Save keys file"), +            options=QtGui.QFileDialog.DontUseNativeDialog) + +        if fileName: +            try: +                with open(fileName, 'w') as keys_file: +                    keys_file.write(self._key.key_data) +                    keys_file.write(self._key_priv.key_data) + +                logger.debug('Export ok') +                QtGui.QMessageBox.information( +                    self, self.tr("Export Successful"), +                    self.tr("The key pair was exported successfully.\n" +                            "Please, store your private key in a safe place.")) +            except IOError as e: +                logger.error("IOError exporting key. {0!r}".format(e)) +                QtGui.QMessageBox.critical( +                    self, self.tr("Input/Output error"), +                    self.tr("There was an error accessing the file.\n" +                            "Export canceled.")) +                return +        else: +            logger.debug('Export canceled by the user.') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index c24735d8..b0f25af1 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -29,6 +29,7 @@ from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.config.providerconfig import ProviderConfig  from leap.bitmask.crypto.srpauth import SRPAuth  from leap.bitmask.gui.loggerwindow import LoggerWindow +from leap.bitmask.gui.advanced_key_management import AdvancedKeyManagement  from leap.bitmask.gui.login import LoginWidget  from leap.bitmask.gui.preferenceswindow import PreferencesWindow  from leap.bitmask.gui.eip_preferenceswindow import EIPPreferencesWindow @@ -252,6 +253,9 @@ class MainWindow(QtGui.QMainWindow):          self.ui.action_create_new_account.triggered.connect(              self._launch_wizard) +        self.ui.action_advanced_key_management.triggered.connect( +            self._show_AKM) +          if IS_MAC:              self.ui.menuFile.menuAction().setText(self.tr("File")) @@ -437,6 +441,20 @@ class MainWindow(QtGui.QMainWindow):          else:              self._logger_window.setVisible(not self._logger_window.isVisible()) +    def _show_AKM(self): +        """ +        SLOT +        TRIGGERS: +            self.ui.action_advanced_key_management.triggered + +        Displays the Advanced Key Management dialog. +        """ +        domain = self._login_widget.get_selected_provider() +        logged_user = "{0}@{1}".format(self._logged_user, domain) +        self._akm = AdvancedKeyManagement( +            logged_user, self._keymanager, self._soledad) +        self._akm.show() +      def _show_preferences(self):          """          SLOT @@ -1521,6 +1539,7 @@ class MainWindow(QtGui.QMainWindow):          """          self._soledad_bootstrapper.cancel_bootstrap() +        setProxiedObject(self._soledad, None)          # XXX: If other defers are doing authenticated stuff, this          # might conflict with those. CHECK! diff --git a/src/leap/bitmask/gui/ui/advanced_key_management.ui b/src/leap/bitmask/gui/ui/advanced_key_management.ui new file mode 100644 index 00000000..d61aa87e --- /dev/null +++ b/src/leap/bitmask/gui/ui/advanced_key_management.ui @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AdvancedKeyManagement</class> + <widget class="QWidget" name="AdvancedKeyManagement"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>431</width> +    <height>188</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Advanced Key Management</string> +  </property> +  <property name="windowIcon"> +   <iconset resource="../../../../../data/resources/mainwindow.qrc"> +    <normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset> +  </property> +  <layout class="QGridLayout" name="gridLayout_3"> +   <item row="0" column="0"> +    <widget class="QWidget" name="container" native="true"> +     <layout class="QGridLayout" name="gridLayout_2"> +      <item row="0" column="0"> +       <widget class="QLabel" name="label"> +        <property name="text"> +         <string>User:</string> +        </property> +       </widget> +      </item> +      <item row="0" column="1"> +       <widget class="QLineEdit" name="leUser"> +        <property name="enabled"> +         <bool>true</bool> +        </property> +        <property name="text"> +         <string>user_name@provider</string> +        </property> +        <property name="alignment"> +         <set>Qt::AlignCenter</set> +        </property> +        <property name="readOnly"> +         <bool>true</bool> +        </property> +       </widget> +      </item> +      <item row="1" column="0"> +       <widget class="QLabel" name="label_3"> +        <property name="text"> +         <string>Key ID:</string> +        </property> +       </widget> +      </item> +      <item row="1" column="1"> +       <widget class="QLineEdit" name="leKeyID"> +        <property name="enabled"> +         <bool>true</bool> +        </property> +        <property name="text"> +         <string>key ID</string> +        </property> +        <property name="alignment"> +         <set>Qt::AlignCenter</set> +        </property> +        <property name="readOnly"> +         <bool>true</bool> +        </property> +       </widget> +      </item> +      <item row="2" column="0"> +       <widget class="QLabel" name="label_5"> +        <property name="text"> +         <string>Key fingerprint:</string> +        </property> +       </widget> +      </item> +      <item row="2" column="1"> +       <widget class="QLineEdit" name="leFingerprint"> +        <property name="enabled"> +         <bool>true</bool> +        </property> +        <property name="text"> +         <string>fingerprint</string> +        </property> +        <property name="alignment"> +         <set>Qt::AlignCenter</set> +        </property> +        <property name="readOnly"> +         <bool>true</bool> +        </property> +       </widget> +      </item> +      <item row="3" column="1"> +       <spacer name="verticalSpacer"> +        <property name="orientation"> +         <enum>Qt::Vertical</enum> +        </property> +        <property name="sizeHint" stdset="0"> +         <size> +          <width>20</width> +          <height>40</height> +         </size> +        </property> +       </spacer> +      </item> +      <item row="4" column="0" colspan="2"> +       <layout class="QGridLayout" name="gridLayout"> +        <item row="1" column="1"> +         <widget class="QPushButton" name="pbExportKeys"> +          <property name="text"> +           <string>Export current key pair</string> +          </property> +         </widget> +        </item> +        <item row="1" column="0" rowspan="2"> +         <spacer name="horizontalSpacer"> +          <property name="orientation"> +           <enum>Qt::Horizontal</enum> +          </property> +          <property name="sizeHint" stdset="0"> +           <size> +            <width>40</width> +            <height>20</height> +           </size> +          </property> +         </spacer> +        </item> +        <item row="2" column="1"> +         <widget class="QPushButton" name="pbImportKeys"> +          <property name="text"> +           <string>Import custom key pair</string> +          </property> +         </widget> +        </item> +       </layout> +      </item> +     </layout> +    </widget> +   </item> +   <item row="1" column="0"> +    <widget class="QLabel" name="lblStatus"> +     <property name="text"> +      <string/> +     </property> +    </widget> +   </item> +  </layout> + </widget> + <resources> +  <include location="../../../../../data/resources/mainwindow.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index badd291d..3b83788e 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -75,7 +75,7 @@           <x>0</x>           <y>0</y>           <width>524</width> -         <height>636</height> +         <height>651</height>          </rect>         </property>         <layout class="QVBoxLayout" name="verticalLayout"> @@ -380,7 +380,7 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb       <x>0</x>       <y>0</y>       <width>524</width> -     <height>22</height> +     <height>21</height>      </rect>     </property>     <widget class="QMenu" name="menuFile"> @@ -388,6 +388,7 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb       <string>&Bitmask</string>      </property>      <addaction name="action_create_new_account"/> +    <addaction name="action_advanced_key_management"/>      <addaction name="separator"/>      <addaction name="action_quit"/>     </widget> @@ -439,6 +440,14 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb      <string>Create a new account...</string>     </property>    </action> +  <action name="action_advanced_key_management"> +   <property name="enabled"> +    <bool>true</bool> +   </property> +   <property name="text"> +    <string>Advanced Key Management</string> +   </property> +  </action>   </widget>   <resources>    <include location="../../../../../data/resources/mainwindow.qrc"/> | 
