mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-06 00:07:29 -04:00
[feature][needs-docs] Master Password integration with OS password manager
This PR adds (optional) synchronization of the master password with the OS password manager (AKA wallet/keychain). A set of new menu items has been added in the options -> authentication -> utilities to manage the new behavior. Notifications are handled by the message bar unless the password r/w operation is triggered from a modal dialog, in this case the notifications will be routed through the recently exposed QgisApp::showSystemNotification that uses the OS tray notifications. This new feature requires libqt5keychain, and was tested with v. 0.5+
This commit is contained in:
parent
669fa87eb4
commit
090d5305e5
@ -278,6 +278,8 @@ ENDIF (WITH_QTMOBILITY)
|
||||
# search for QScintilla2 (C++ lib)
|
||||
FIND_PACKAGE(QScintilla REQUIRED)
|
||||
|
||||
# Password helper
|
||||
FIND_PACKAGE(QtKeychain REQUIRED)
|
||||
# Master password hash and authentication encryption
|
||||
FIND_PACKAGE(QCA REQUIRED)
|
||||
# Check for runtime dependency of qca-ossl plugin
|
||||
|
@ -22,7 +22,7 @@
|
||||
pushd ${HOME}
|
||||
|
||||
# fetching data from github should be just as fast as S3
|
||||
curl -s -S -L https://github.com/opengisch/osgeo4travis/archive/qt5bin.tar.gz | tar --strip-components=1 -xz -C /home/travis &
|
||||
curl -s -S -L https://github.com/opengisch/osgeo4travis/archive/qt55bin.tar.gz | tar --strip-components=1 -xz -C /home/travis &
|
||||
SETUP_OSGEO4W_PID=$!
|
||||
|
||||
mkdir /home/travis/osgeo4travis
|
||||
|
51
cmake/FindQtKeychain.cmake
Normal file
51
cmake/FindQtKeychain.cmake
Normal file
@ -0,0 +1,51 @@
|
||||
# Find QtKeychain
|
||||
# ~~~~~~~~~~~~~~~
|
||||
# Copyright (c) 2016, Boundless Spatial
|
||||
# Author: Larry Shaffer <lshaffer (at) boundlessgeo (dot) com>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
# CMake module to search for QtKeychain library from:
|
||||
# https://github.com/frankosterfeld/qtkeychain
|
||||
#
|
||||
# If it's found it sets QTKEYCHAIN_FOUND to TRUE
|
||||
# and following variables are set:
|
||||
# QTKEYCHAIN_INCLUDE_DIR
|
||||
# QTKEYCHAIN_LIBRARY
|
||||
|
||||
FIND_PATH(QTKEYCHAIN_INCLUDE_DIR keychain.h
|
||||
PATHS
|
||||
${LIB_DIR}/include
|
||||
"$ENV{LIB_DIR}/include"
|
||||
$ENV{INCLUDE}
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
PATH_SUFFIXES qt5keychain qtkeychain
|
||||
)
|
||||
|
||||
FIND_LIBRARY(QTKEYCHAIN_LIBRARY NAMES qt5keychain qtkeychain
|
||||
PATHS
|
||||
${LIB_DIR}
|
||||
"$ENV{LIB_DIR}"
|
||||
$ENV{LIB}
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
)
|
||||
|
||||
|
||||
IF (QTKEYCHAIN_INCLUDE_DIR AND QTKEYCHAIN_LIBRARY)
|
||||
SET(QTKEYCHAIN_FOUND TRUE)
|
||||
ELSE()
|
||||
SET(QTKEYCHAIN_FOUND FALSE)
|
||||
ENDIF (QTKEYCHAIN_INCLUDE_DIR AND QTKEYCHAIN_LIBRARY)
|
||||
|
||||
IF (QTKEYCHAIN_FOUND)
|
||||
IF (NOT QTKEYCHAIN_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found QtKeychain: ${QTKEYCHAIN_LIBRARY}")
|
||||
ENDIF (NOT QTKEYCHAIN_FIND_QUIETLY)
|
||||
ELSE (QTKEYCHAIN_FOUND)
|
||||
IF (QTKEYCHAIN_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find QtKeychain")
|
||||
ENDIF (QTKEYCHAIN_FIND_REQUIRED)
|
||||
ENDIF (QTKEYCHAIN_FOUND)
|
@ -89,6 +89,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${QEXTSERIALPORT_INCLUDE_DIR}
|
||||
${QSCINTILLA_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${SQLITE3_INCLUDE_DIR}
|
||||
)
|
||||
INCLUDE_DIRECTORIES(
|
||||
|
@ -442,6 +442,19 @@ class QgsAuthManager : QObject
|
||||
QMutex *mutex();
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
* Signals emitted on password helper failure,
|
||||
* mainly used in the tests to exit main application loop
|
||||
*/
|
||||
void passwordHelperFailure();
|
||||
|
||||
/**
|
||||
* Signals emitted on password helper success,
|
||||
* mainly used in the tests to exit main application loop
|
||||
*/
|
||||
void passwordHelperSuccess();
|
||||
|
||||
/**
|
||||
* Custom logging signal to relay to console output and QgsMessageLog
|
||||
* @see QgsMessageLog
|
||||
@ -449,7 +462,16 @@ class QgsAuthManager : QObject
|
||||
* @param tag Associated tag (title)
|
||||
* @param level Message log level
|
||||
*/
|
||||
void messageOut( const QString& message, const QString& tag, QgsAuthManager::MessageLevel level = INFO ) const;
|
||||
void messageOut( const QString& message, const QString& tag = QgsAuthManager::AUTH_MAN_TAG, QgsAuthManager::MessageLevel level = INFO ) const;
|
||||
|
||||
/**
|
||||
* Custom logging signal to inform the user about master password <-> password manager interactions
|
||||
* @see QgsMessageLog
|
||||
* @param message Message to send
|
||||
* @param tag Associated tag (title)
|
||||
* @param level Message log level
|
||||
*/
|
||||
void passwordHelperMessageOut( const QString &message, const QString &tag = QgsAuthManager::AUTH_MAN_TAG, QgsAuthManager::MessageLevel level = INFO ) const;
|
||||
|
||||
/**
|
||||
* Emitted when a password has been verify (or not)
|
||||
|
@ -578,6 +578,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${GDAL_INCLUDE_DIR}
|
||||
${QWTPOLAR_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
IF(ENABLE_MODELTEST)
|
||||
|
@ -12254,6 +12254,8 @@ void QgisApp::masterPasswordSetup()
|
||||
{
|
||||
connect( QgsAuthManager::instance(), &QgsAuthManager::messageOut,
|
||||
this, &QgisApp::authMessageOut );
|
||||
connect( QgsAuthManager::instance(), &QgsAuthManager::passwordHelperMessageOut,
|
||||
this, &QgisApp::authMessageOut );
|
||||
connect( QgsAuthManager::instance(), &QgsAuthManager::authDatabaseEraseRequested,
|
||||
this, &QgisApp::eraseAuthenticationDatabase );
|
||||
}
|
||||
@ -12289,13 +12291,18 @@ void QgisApp::eraseAuthenticationDatabase()
|
||||
|
||||
void QgisApp::authMessageOut( const QString &message, const QString &authtag, QgsAuthManager::MessageLevel level )
|
||||
{
|
||||
// only if main window is active window
|
||||
// Use system notifications if the main window is not the active one,
|
||||
// push message to the message bar if the main window is active
|
||||
if ( qApp->activeWindow() != this )
|
||||
return;
|
||||
|
||||
{
|
||||
showSystemNotification( tr( "QGIS Authentication" ), message );
|
||||
}
|
||||
else
|
||||
{
|
||||
int levelint = static_cast< int >( level );
|
||||
messageBar()->pushMessage( authtag, message, static_cast< QgsMessageBar::MessageLevel >( levelint ), 7 );
|
||||
}
|
||||
}
|
||||
|
||||
void QgisApp::completeInitialization()
|
||||
{
|
||||
|
@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES (
|
||||
)
|
||||
INCLUDE_DIRECTORIES (SYSTEM
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
INCLUDE_DIRECTORIES (
|
||||
../../gui
|
||||
|
@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES (
|
||||
)
|
||||
INCLUDE_DIRECTORIES (SYSTEM
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
INCLUDE_DIRECTORIES (
|
||||
../../gui
|
||||
|
@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES (
|
||||
)
|
||||
INCLUDE_DIRECTORIES (SYSTEM
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
INCLUDE_DIRECTORIES (
|
||||
../../gui
|
||||
|
@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES (
|
||||
)
|
||||
INCLUDE_DIRECTORIES (SYSTEM
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
INCLUDE_DIRECTORIES (
|
||||
../../gui
|
||||
|
@ -980,6 +980,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${SQLITE3_INCLUDE_DIR}
|
||||
${SPATIALITE_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#for PAL classes
|
||||
@ -1071,7 +1072,7 @@ TARGET_LINK_LIBRARIES(qgis_core
|
||||
${OPTIONAL_QTWEBKIT}
|
||||
${QT_QTSQL_LIBRARY}
|
||||
${QCA_LIBRARY}
|
||||
|
||||
${QTKEYCHAIN_LIBRARY}
|
||||
${PROJ_LIBRARY}
|
||||
${GEOS_LIBRARY}
|
||||
${GDAL_LIBRARY}
|
||||
|
@ -37,6 +37,10 @@
|
||||
#include <QSslConfiguration>
|
||||
#endif
|
||||
|
||||
// QtKeyChain library
|
||||
#include "keychain.h"
|
||||
|
||||
// QGIS includes
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsauthcertutils.h"
|
||||
#include "qgsauthcrypto.h"
|
||||
@ -45,6 +49,8 @@
|
||||
#include "qgsauthmethodregistry.h"
|
||||
#include "qgscredentials.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
QgsAuthManager *QgsAuthManager::sInstance = nullptr;
|
||||
|
||||
@ -59,6 +65,24 @@ const QString QgsAuthManager::AUTH_MAN_TAG = QObject::tr( "Authentication Manage
|
||||
const QString QgsAuthManager::AUTH_CFG_REGEX = QStringLiteral( "authcfg=([a-z]|[A-Z]|[0-9]){7}" );
|
||||
|
||||
|
||||
const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_KEY_NAME( "QGIS-Master-Password" );
|
||||
const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_FOLDER_NAME( "QGIS" );
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Keychain" );
|
||||
static const QString sDescription = QObject::tr( "Master Password <-> KeyChain storage plugin. Store and retrieve your master password in your KeyChain" );
|
||||
#elif defined(Q_OS_WIN)
|
||||
const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Password Manager" );
|
||||
static const QString sDescription = QObject::tr( "Master Password <-> Password Manager storage plugin. Store and retrieve your master password in your Password Manager" );
|
||||
#elif defined(Q_OS_LINUX)
|
||||
const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Wallet/KeyRing" );
|
||||
static const QString sDescription = QObject::tr( "Master Password <-> Wallet/KeyRing storage plugin. Store and retrieve your master password in your Wallet/KeyRing" );
|
||||
#else
|
||||
const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Password Manager" );
|
||||
static const QString sDescription = QObject::tr( "Master Password <-> KeyChain storage plugin. Store and retrieve your master password in your Wallet/KeyChain/Password Manager" );
|
||||
#endif
|
||||
|
||||
|
||||
QgsAuthManager *QgsAuthManager::instance()
|
||||
{
|
||||
if ( !sInstance )
|
||||
@ -2719,6 +2743,15 @@ const QByteArray QgsAuthManager::getTrustedCaCertsPemText()
|
||||
return capem;
|
||||
}
|
||||
|
||||
bool QgsAuthManager::passwordHelperSync()
|
||||
{
|
||||
if ( masterPasswordIsSet( ) )
|
||||
{
|
||||
return passwordHelperWrite( mMasterPass );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
////////////////// Certificate calls - end ///////////////////////
|
||||
|
||||
@ -2821,6 +2854,12 @@ QgsAuthManager::QgsAuthManager()
|
||||
, mScheduledDbEraseRequestCount( 0 )
|
||||
, mMutex( nullptr )
|
||||
, mIgnoredSslErrorsCache( QHash<QString, QSet<QSslError::SslError> >() )
|
||||
, mPasswordHelperVerificationError( false )
|
||||
, mPasswordHelperErrorMessage( "" )
|
||||
, mPasswordHelperErrorCode( QKeychain::NoError )
|
||||
, mPasswordHelperLoggingEnabled( false )
|
||||
, mPasswordHelperFailedInit( false )
|
||||
|
||||
{
|
||||
mMutex = new QMutex( QMutex::Recursive );
|
||||
connect( this, &QgsAuthManager::messageOut,
|
||||
@ -2847,20 +2886,243 @@ QgsAuthManager::~QgsAuthManager()
|
||||
QSqlDatabase::removeDatabase( QStringLiteral( "authentication.configs" ) );
|
||||
}
|
||||
|
||||
|
||||
QString QgsAuthManager::passwordHelperName() const
|
||||
{
|
||||
return tr( "Password Helper" );
|
||||
}
|
||||
|
||||
|
||||
void QgsAuthManager::passwordHelperLog( const QString &msg ) const
|
||||
{
|
||||
if ( passwordHelperLoggingEnabled( ) )
|
||||
{
|
||||
QgsMessageLog::logMessage( msg, passwordHelperName() );
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsAuthManager::passwordHelperDelete()
|
||||
{
|
||||
passwordHelperLog( tr( "Opening %1 for DELETE ..." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
|
||||
bool result;
|
||||
QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
|
||||
QgsSettings settings;
|
||||
job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool( ) );
|
||||
job.setAutoDelete( false );
|
||||
job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
|
||||
QEventLoop loop;
|
||||
job.connect( &job, SIGNAL( finished( QKeychain::Job * ) ), &loop, SLOT( quit() ) );
|
||||
job.start();
|
||||
loop.exec();
|
||||
if ( job.error() )
|
||||
{
|
||||
mPasswordHelperErrorCode = job.error();
|
||||
mPasswordHelperErrorMessage = tr( "Delete password failed: %1." ).arg( job.errorString() );
|
||||
// Signals used in the tests to exit main application loop
|
||||
emit passwordHelperFailure();
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Signals used in the tests to exit main application loop
|
||||
emit passwordHelperSuccess();
|
||||
result = true;
|
||||
}
|
||||
passwordHelperProcessError();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString QgsAuthManager::passwordHelperRead()
|
||||
{
|
||||
// Retrieve it!
|
||||
QString password( "" );
|
||||
passwordHelperLog( tr( "Opening %1 for READ ..." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
|
||||
QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
|
||||
QgsSettings settings;
|
||||
job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool( ) );
|
||||
job.setAutoDelete( false );
|
||||
job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
|
||||
QEventLoop loop;
|
||||
job.connect( &job, SIGNAL( finished( QKeychain::Job * ) ), &loop, SLOT( quit() ) );
|
||||
job.start();
|
||||
loop.exec();
|
||||
if ( job.error() )
|
||||
{
|
||||
mPasswordHelperErrorCode = job.error();
|
||||
mPasswordHelperErrorMessage = tr( "Retrieving password from your %1 failed: %2." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, job.errorString() );
|
||||
// Signals used in the tests to exit main application loop
|
||||
emit passwordHelperFailure();
|
||||
}
|
||||
else
|
||||
{
|
||||
password = job.textData();
|
||||
// Password is there but it is empty, treat it like if it was not found
|
||||
if ( password.isEmpty() )
|
||||
{
|
||||
mPasswordHelperErrorCode = QKeychain::EntryNotFound;
|
||||
mPasswordHelperErrorMessage = tr( "Empty password retrieved from your %1." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME );
|
||||
// Signals used in the tests to exit main application loop
|
||||
emit passwordHelperFailure();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Signals used in the tests to exit main application loop
|
||||
emit passwordHelperSuccess();
|
||||
}
|
||||
}
|
||||
passwordHelperProcessError();
|
||||
return password;
|
||||
}
|
||||
|
||||
bool QgsAuthManager::passwordHelperWrite( const QString &password )
|
||||
{
|
||||
Q_ASSERT( !password.isEmpty() );
|
||||
bool result;
|
||||
passwordHelperLog( tr( "Opening %1 for WRITE ..." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
|
||||
QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
|
||||
QgsSettings settings;
|
||||
job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool( ) );
|
||||
job.setAutoDelete( false );
|
||||
job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
|
||||
job.setTextData( password );
|
||||
QEventLoop loop;
|
||||
job.connect( &job, SIGNAL( finished( QKeychain::Job * ) ), &loop, SLOT( quit() ) );
|
||||
job.start();
|
||||
loop.exec();
|
||||
if ( job.error() )
|
||||
{
|
||||
mPasswordHelperErrorCode = job.error();
|
||||
mPasswordHelperErrorMessage = tr( "Storing password in your %1 failed: %2." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, job.errorString() );
|
||||
// Signals used in the tests to exit main application loop
|
||||
emit passwordHelperFailure();
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
passwordHelperClearErrors();
|
||||
// Signals used in the tests to exit main application loop
|
||||
emit passwordHelperSuccess();
|
||||
result = true;
|
||||
}
|
||||
passwordHelperProcessError( );
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QgsAuthManager::passwordHelperEnabled() const
|
||||
{
|
||||
// Does the user want to store the password in the wallet?
|
||||
QgsSettings settings;
|
||||
return settings.value( QStringLiteral( "use_password_helper" ), true, QgsSettings::Section::Auth ).toBool( );
|
||||
}
|
||||
|
||||
void QgsAuthManager::setPasswordHelperEnabled( const bool enabled )
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "use_password_helper" ), enabled, QgsSettings::Section::Auth );
|
||||
emit messageOut( enabled ? tr( "Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
|
||||
.arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) :
|
||||
tr( "Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
|
||||
.arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
|
||||
}
|
||||
|
||||
bool QgsAuthManager::passwordHelperLoggingEnabled() const
|
||||
{
|
||||
// Does the user want to store the password in the wallet?
|
||||
QgsSettings settings;
|
||||
return settings.value( QStringLiteral( "password_helper_logging" ), false, QgsSettings::Section::Auth ).toBool( );
|
||||
}
|
||||
|
||||
void QgsAuthManager::setPasswordHelperLoggingEnabled( const bool enabled )
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "password_helper_logging" ), enabled, QgsSettings::Section::Auth );
|
||||
}
|
||||
|
||||
void QgsAuthManager::passwordHelperClearErrors()
|
||||
{
|
||||
mPasswordHelperErrorCode = QKeychain::NoError;
|
||||
mPasswordHelperErrorMessage = "";
|
||||
}
|
||||
|
||||
void QgsAuthManager::passwordHelperProcessError()
|
||||
{
|
||||
if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
|
||||
mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
|
||||
mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
|
||||
mPasswordHelperErrorCode == QKeychain::NotImplemented )
|
||||
{
|
||||
// If the error is permanent or the user denied access to the wallet
|
||||
// we also want to disable the wallet system to prevent annoying
|
||||
// notification on each subsequent access.
|
||||
setPasswordHelperEnabled( false );
|
||||
mPasswordHelperErrorMessage = tr( "There was an error and integration with your %1 system has been disabled. "
|
||||
"You can re-enable it at any time through the \"Utilities\" menu "
|
||||
"in the Authentication pane of the options dialog. %2" )
|
||||
.arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME )
|
||||
.arg( mPasswordHelperErrorMessage );
|
||||
}
|
||||
if ( mPasswordHelperErrorCode != QKeychain::NoError )
|
||||
{
|
||||
// We've got an error from the wallet
|
||||
passwordHelperLog( tr( "Error in %1: %2" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, mPasswordHelperErrorMessage ) );
|
||||
emit passwordHelperMessageOut( mPasswordHelperErrorMessage, authManTag(), CRITICAL );
|
||||
}
|
||||
passwordHelperClearErrors();
|
||||
}
|
||||
|
||||
|
||||
bool QgsAuthManager::masterPasswordInput()
|
||||
{
|
||||
if ( isDisabled() )
|
||||
return false;
|
||||
|
||||
QString pass;
|
||||
bool storedPasswordIsValid = false;
|
||||
bool ok = false;
|
||||
|
||||
// Read the password from the wallet
|
||||
if ( passwordHelperEnabled( ) )
|
||||
{
|
||||
pass = passwordHelperRead( );
|
||||
if ( ! pass.isEmpty( ) && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
|
||||
{
|
||||
// Let's check the password!
|
||||
if ( verifyMasterPassword( pass ) )
|
||||
{
|
||||
ok = true;
|
||||
storedPasswordIsValid = true;
|
||||
emit passwordHelperMessageOut( tr( "Master password has been successfully read from your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), INFO );
|
||||
}
|
||||
else
|
||||
{
|
||||
emit passwordHelperMessageOut( tr( "Master password stored in your %1 is not valid" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), WARNING );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! ok )
|
||||
{
|
||||
QgsCredentials *creds = QgsCredentials::instance();
|
||||
creds->lock();
|
||||
bool ok = creds->getMasterPassword( pass, masterPasswordHashInDatabase() );
|
||||
pass.clear();
|
||||
ok = creds->getMasterPassword( pass, masterPasswordHashInDatabase() );
|
||||
creds->unlock();
|
||||
}
|
||||
|
||||
if ( ok && !pass.isEmpty() && !masterPasswordSame( pass ) )
|
||||
if ( ok && !pass.isEmpty() && mMasterPass != pass )
|
||||
{
|
||||
mMasterPass = pass;
|
||||
if ( passwordHelperEnabled( ) && ! storedPasswordIsValid )
|
||||
{
|
||||
if ( passwordHelperWrite( pass ) )
|
||||
{
|
||||
emit passwordHelperMessageOut( tr( "Master password has been successfully written to your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), INFO );
|
||||
}
|
||||
else
|
||||
{
|
||||
emit passwordHelperMessageOut( tr( "Master password could not be written to your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), WARNING );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -37,6 +37,9 @@
|
||||
#include "qgsauthconfig.h"
|
||||
#include "qgsauthmethod.h"
|
||||
|
||||
// Qt5KeyChain library
|
||||
#include "keychain.h"
|
||||
|
||||
namespace QCA
|
||||
{
|
||||
class Initializer;
|
||||
@ -496,8 +499,54 @@ class CORE_EXPORT QgsAuthManager : public QObject
|
||||
//! Return pointer to mutex
|
||||
QMutex *mutex() { return mMutex; }
|
||||
|
||||
//! Error message getter
|
||||
//! @note not available in Python bindings
|
||||
const QString passwordHelperErrorMessage() { return mPasswordHelperErrorMessage; }
|
||||
|
||||
//! Delete master password from wallet
|
||||
//! @note not available in Python bindings
|
||||
bool passwordHelperDelete();
|
||||
|
||||
//! Password helper enabled getter
|
||||
//! @note not available in Python bindings
|
||||
bool passwordHelperEnabled() const;
|
||||
|
||||
//! Password helper enabled setter
|
||||
//! @note not available in Python bindings
|
||||
void setPasswordHelperEnabled( const bool enabled );
|
||||
|
||||
//! Password helper logging enabled getter
|
||||
//! @note not available in Python bindings
|
||||
bool passwordHelperLoggingEnabled() const;
|
||||
|
||||
//! Password helper logging enabled setter
|
||||
//! @note not available in Python bindings
|
||||
void setPasswordHelperLoggingEnabled( const bool enabled );
|
||||
|
||||
//! Store the password manager into the wallet
|
||||
//! @note not available in Python bindings
|
||||
bool passwordHelperSync( );
|
||||
|
||||
//! The display name of the password helper (platform dependent)
|
||||
static const QString AUTH_PASSWORD_HELPER_DISPLAY_NAME;
|
||||
|
||||
//! The display name of the Authentication Manager
|
||||
static const QString AUTH_MAN_TAG;
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
* Signals emitted on password helper failure,
|
||||
* mainly used in the tests to exit main application loop
|
||||
*/
|
||||
void passwordHelperFailure();
|
||||
|
||||
/**
|
||||
* Signals emitted on password helper success,
|
||||
* mainly used in the tests to exit main application loop
|
||||
*/
|
||||
void passwordHelperSuccess();
|
||||
|
||||
/**
|
||||
* Custom logging signal to relay to console output and QgsMessageLog
|
||||
* \see QgsMessageLog
|
||||
@ -507,6 +556,16 @@ class CORE_EXPORT QgsAuthManager : public QObject
|
||||
*/
|
||||
void messageOut( const QString &message, const QString &tag = AUTH_MAN_TAG, QgsAuthManager::MessageLevel level = INFO ) const;
|
||||
|
||||
/**
|
||||
* Custom logging signal to inform the user about master password <-> password manager interactions
|
||||
* @see QgsMessageLog
|
||||
* @param message Message to send
|
||||
* @param tag Associated tag (title)
|
||||
* @param level Message log level
|
||||
*/
|
||||
void passwordHelperMessageOut( const QString &message, const QString &tag = AUTH_MAN_TAG, QgsAuthManager::MessageLevel level = INFO ) const;
|
||||
|
||||
|
||||
/**
|
||||
* Emitted when a password has been verify (or not)
|
||||
* \param verified The state of password's verification
|
||||
@ -544,6 +603,31 @@ class CORE_EXPORT QgsAuthManager : public QObject
|
||||
|
||||
private:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Password Helper methods
|
||||
|
||||
//! Return name for logging
|
||||
QString passwordHelperName() const;
|
||||
|
||||
//! Print a debug message in QGIS
|
||||
void passwordHelperLog( const QString &msg ) const;
|
||||
|
||||
//! Read Master password from the wallet
|
||||
QString passwordHelperRead();
|
||||
|
||||
//! Store Master password in the wallet
|
||||
bool passwordHelperWrite( const QString &password );
|
||||
|
||||
//! Error message setter
|
||||
void passwordHelperSetErrorMessage( const QString errorMessage ) { mPasswordHelperErrorMessage = errorMessage; }
|
||||
|
||||
//! Clear error code and message
|
||||
void passwordHelperClearErrors();
|
||||
|
||||
//! Process the error: show it and/or disable the password helper system in case of
|
||||
//! access denied or no backend, reset error flags at the end
|
||||
void passwordHelperProcessError();
|
||||
|
||||
bool createConfigTables();
|
||||
|
||||
bool createCertTables();
|
||||
@ -604,7 +688,6 @@ class CORE_EXPORT QgsAuthManager : public QObject
|
||||
static const QString AUTH_SERVERS_TABLE;
|
||||
static const QString AUTH_AUTHORITIES_TABLE;
|
||||
static const QString AUTH_TRUST_TABLE;
|
||||
static const QString AUTH_MAN_TAG;
|
||||
static const QString AUTH_CFG_REGEX;
|
||||
|
||||
bool mAuthInit;
|
||||
@ -637,6 +720,31 @@ class CORE_EXPORT QgsAuthManager : public QObject
|
||||
// cache of SSL errors to be ignored in network connections, per sha-hostport
|
||||
QHash<QString, QSet<QSslError::SslError> > mIgnoredSslErrorsCache;
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Password Helper Variables
|
||||
|
||||
//! Master password verification has failed
|
||||
bool mPasswordHelperVerificationError;
|
||||
|
||||
//! Store last error message
|
||||
QString mPasswordHelperErrorMessage;
|
||||
|
||||
//! Store last error code (enum)
|
||||
QKeychain::Error mPasswordHelperErrorCode;
|
||||
|
||||
//! Enable logging
|
||||
bool mPasswordHelperLoggingEnabled;
|
||||
|
||||
//! Whether the keychain bridge failed to initialize
|
||||
bool mPasswordHelperFailedInit;
|
||||
|
||||
//! Master password name in the wallets
|
||||
static const QLatin1String AUTH_PASSWORD_HELPER_KEY_NAME;
|
||||
|
||||
//! password helper folder in the wallets
|
||||
static const QLatin1String AUTH_PASSWORD_HELPER_FOLDER_NAME;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSAUTHMANAGER_H
|
||||
|
@ -762,6 +762,7 @@ INCLUDE_DIRECTORIES(
|
||||
)
|
||||
INCLUDE_DIRECTORIES(SYSTEM
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${QWT_INCLUDE_DIR}
|
||||
${SQLITE3_INCLUDE_DIR}
|
||||
${QSCINTILLA_INCLUDE_DIR}
|
||||
|
@ -152,6 +152,20 @@ void QgsAuthEditorWidgets::setupUtilitiesMenu()
|
||||
mActionRemoveAuthConfigs = new QAction( QStringLiteral( "Remove all authentication configurations" ), this );
|
||||
mActionEraseAuthDatabase = new QAction( QStringLiteral( "Erase authentication database" ), this );
|
||||
|
||||
mActionPasswordHelperSync = new QAction( tr( "Store/update the master password in your %1" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME ), this );
|
||||
mActionPasswordHelperDelete = new QAction( tr( "Clear the master password from your %1" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME ), this );
|
||||
mActionPasswordHelperEnable = new QAction( tr( "Integrate master password with your %1" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME ), this );
|
||||
mActionPasswordHelperLoggingEnable = new QAction( tr( "Enable password helper debug log" ), this );
|
||||
|
||||
mActionPasswordHelperEnable->setCheckable( true );
|
||||
mActionPasswordHelperEnable->setChecked( QgsAuthManager::instance()->passwordHelperEnabled( ) );
|
||||
|
||||
mActionPasswordHelperLoggingEnable->setCheckable( true );
|
||||
mActionPasswordHelperLoggingEnable->setChecked( QgsAuthManager::instance()->passwordHelperLoggingEnabled( ) );
|
||||
|
||||
connect( mActionSetMasterPassword, &QAction::triggered, this, &QgsAuthEditorWidgets::setMasterPassword );
|
||||
connect( mActionClearCachedMasterPassword, &QAction::triggered, this, &QgsAuthEditorWidgets::clearCachedMasterPassword );
|
||||
connect( mActionResetMasterPassword, &QAction::triggered, this, &QgsAuthEditorWidgets::resetMasterPassword );
|
||||
@ -159,11 +173,21 @@ void QgsAuthEditorWidgets::setupUtilitiesMenu()
|
||||
connect( mActionRemoveAuthConfigs, &QAction::triggered, this, &QgsAuthEditorWidgets::removeAuthenticationConfigs );
|
||||
connect( mActionEraseAuthDatabase, &QAction::triggered, this, &QgsAuthEditorWidgets::eraseAuthenticationDatabase );
|
||||
|
||||
connect( mActionPasswordHelperSync, &QAction::triggered, this, &QgsAuthEditorWidgets::passwordHelperSync );
|
||||
connect( mActionPasswordHelperDelete, &QAction::triggered, this, &QgsAuthEditorWidgets::passwordHelperDelete );
|
||||
connect( mActionPasswordHelperEnable, &QAction::triggered, this, &QgsAuthEditorWidgets::passwordHelperEnableTriggered );
|
||||
connect( mActionPasswordHelperLoggingEnable, &QAction::triggered, this, &QgsAuthEditorWidgets::passwordHelperLoggingEnableTriggered );
|
||||
|
||||
mAuthUtilitiesMenu = new QMenu( this );
|
||||
mAuthUtilitiesMenu->addAction( mActionSetMasterPassword );
|
||||
mAuthUtilitiesMenu->addAction( mActionClearCachedMasterPassword );
|
||||
mAuthUtilitiesMenu->addAction( mActionResetMasterPassword );
|
||||
mAuthUtilitiesMenu->addSeparator();
|
||||
mAuthUtilitiesMenu->addAction( mActionPasswordHelperEnable );
|
||||
mAuthUtilitiesMenu->addAction( mActionPasswordHelperSync );
|
||||
mAuthUtilitiesMenu->addAction( mActionPasswordHelperDelete );
|
||||
mAuthUtilitiesMenu->addAction( mActionPasswordHelperLoggingEnable );
|
||||
mAuthUtilitiesMenu->addSeparator();
|
||||
mAuthUtilitiesMenu->addAction( mActionClearCachedAuthConfigs );
|
||||
mAuthUtilitiesMenu->addAction( mActionRemoveAuthConfigs );
|
||||
mAuthUtilitiesMenu->addSeparator();
|
||||
@ -208,6 +232,27 @@ void QgsAuthEditorWidgets::authMessageOut( const QString &message, const QString
|
||||
messageBar()->pushMessage( authtag, message, ( QgsMessageBar::MessageLevel )levelint, 7 );
|
||||
}
|
||||
|
||||
void QgsAuthEditorWidgets::passwordHelperDelete()
|
||||
{
|
||||
QgsAuthGuiUtils::passwordHelperDelete( messageBar(), messageTimeout(), this );
|
||||
}
|
||||
|
||||
void QgsAuthEditorWidgets::passwordHelperSync()
|
||||
{
|
||||
QgsAuthGuiUtils::passwordHelperSync( messageBar(), messageTimeout() );
|
||||
}
|
||||
|
||||
void QgsAuthEditorWidgets::passwordHelperEnableTriggered()
|
||||
{
|
||||
// Only fire on real changes
|
||||
QgsAuthGuiUtils::passwordHelperEnable( mActionPasswordHelperEnable->isChecked(), messageBar(), messageTimeout() );
|
||||
}
|
||||
|
||||
void QgsAuthEditorWidgets::passwordHelperLoggingEnableTriggered()
|
||||
{
|
||||
QgsAuthGuiUtils::passwordHelperLoggingEnable( mActionPasswordHelperLoggingEnable->isChecked(), messageBar(), messageTimeout() );
|
||||
}
|
||||
|
||||
QgsMessageBar *QgsAuthEditorWidgets::messageBar()
|
||||
{
|
||||
return mMsgBar;
|
||||
|
@ -88,6 +88,18 @@ class GUI_EXPORT QgsAuthEditorWidgets : public QWidget, private Ui::QgsAuthEdito
|
||||
//! Relay messages to widget's messagebar
|
||||
void authMessageOut( const QString &message, const QString &authtag, QgsAuthManager::MessageLevel level );
|
||||
|
||||
//! Remove master password from wallet
|
||||
void passwordHelperDelete( );
|
||||
|
||||
//! Store master password into the wallet
|
||||
void passwordHelperSync( );
|
||||
|
||||
//! Toggle password helper (enable/disable)
|
||||
void passwordHelperEnableTriggered( );
|
||||
|
||||
//! Toggle password helper logging (enable/disable)
|
||||
void passwordHelperLoggingEnableTriggered( );
|
||||
|
||||
private:
|
||||
void setupUtilitiesMenu();
|
||||
|
||||
@ -101,6 +113,10 @@ class GUI_EXPORT QgsAuthEditorWidgets : public QWidget, private Ui::QgsAuthEdito
|
||||
QAction *mActionClearCachedAuthConfigs = nullptr;
|
||||
QAction *mActionRemoveAuthConfigs = nullptr;
|
||||
QAction *mActionEraseAuthDatabase = nullptr;
|
||||
QAction *mActionPasswordHelperDelete = nullptr;
|
||||
QAction *mActionPasswordHelperSync = nullptr;
|
||||
QAction *mActionPasswordHelperEnable = nullptr;
|
||||
QAction *mActionPasswordHelperLoggingEnable = nullptr;
|
||||
};
|
||||
|
||||
#endif // QGSAUTHEDITORWIDGETS_H
|
||||
|
@ -260,3 +260,73 @@ QString QgsAuthGuiUtils::getOpenFileName( QWidget *parent, const QString &title,
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
void QgsAuthGuiUtils::passwordHelperDelete( QgsMessageBar *msgbar, int timeout, QWidget *parent )
|
||||
{
|
||||
if ( QMessageBox::warning( parent,
|
||||
QObject::tr( "Delete confirmation" ),
|
||||
QObject::tr( "Do you really want to delete the master password from your %1?" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME ),
|
||||
QMessageBox::Ok | QMessageBox::Cancel,
|
||||
QMessageBox::Cancel ) == QMessageBox::Cancel )
|
||||
{
|
||||
return;
|
||||
}
|
||||
QString msg;
|
||||
QgsMessageBar::MessageLevel level;
|
||||
if ( ! QgsAuthManager::instance()->passwordHelperDelete() )
|
||||
{
|
||||
msg = QgsAuthManager::instance()->passwordHelperErrorMessage();
|
||||
level = QgsMessageBar::WARNING;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = QObject::tr( "Master password was successfully deleted from your %1" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME );
|
||||
|
||||
level = QgsMessageBar::INFO;
|
||||
}
|
||||
msgbar->pushMessage( QObject::tr( "Password helper delete" ), msg, level, timeout );
|
||||
}
|
||||
|
||||
void QgsAuthGuiUtils::passwordHelperSync( QgsMessageBar *msgbar, int timeout )
|
||||
{
|
||||
QString msg;
|
||||
QgsMessageBar::MessageLevel level;
|
||||
if ( ! QgsAuthManager::instance()->masterPasswordIsSet( ) )
|
||||
{
|
||||
msg = QObject::tr( "Master password is not set and cannot be stored in your %1" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME );
|
||||
level = QgsMessageBar::WARNING;
|
||||
}
|
||||
else if ( ! QgsAuthManager::instance()->passwordHelperSync() )
|
||||
{
|
||||
msg = QgsAuthManager::instance()->passwordHelperErrorMessage();
|
||||
level = QgsMessageBar::WARNING;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = QObject::tr( "Master password has been successfully stored in your %1" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME );
|
||||
|
||||
level = QgsMessageBar::INFO;
|
||||
}
|
||||
msgbar->pushMessage( QObject::tr( "Password helper write" ), msg, level, timeout );
|
||||
}
|
||||
|
||||
void QgsAuthGuiUtils::passwordHelperEnable( bool enabled, QgsMessageBar *msgbar, int timeout )
|
||||
{
|
||||
QgsAuthManager::instance()->setPasswordHelperEnabled( enabled );
|
||||
QString msg = enabled ? QObject::tr( "Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME ) :
|
||||
QObject::tr( "Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME );
|
||||
msgbar->pushMessage( QObject::tr( "Password helper write" ), msg, QgsMessageBar::INFO, timeout );
|
||||
}
|
||||
|
||||
void QgsAuthGuiUtils::passwordHelperLoggingEnable( bool enabled, QgsMessageBar *msgbar, int timeout )
|
||||
{
|
||||
Q_UNUSED( msgbar );
|
||||
Q_UNUSED( timeout );
|
||||
QgsAuthManager::instance()->setPasswordHelperLoggingEnabled( enabled );
|
||||
}
|
||||
|
@ -80,6 +80,19 @@ class GUI_EXPORT QgsAuthGuiUtils
|
||||
|
||||
//! Open file dialog for auth associated widgets
|
||||
static QString getOpenFileName( QWidget *parent, const QString &title, const QString &extfilter );
|
||||
|
||||
//! Remove master password from wallet
|
||||
static void passwordHelperDelete( QgsMessageBar *msgbar, int timeout = 0, QWidget *parent = nullptr );
|
||||
|
||||
//! Store master password into the wallet
|
||||
static void passwordHelperSync( QgsMessageBar *msgbar, int timeout = 0 );
|
||||
|
||||
//! Set password helper enabled (enable/disable)
|
||||
static void passwordHelperEnable( bool enabled, QgsMessageBar *msgbar, int timeout = 0 );
|
||||
|
||||
//! Set password helper logging enabled (enable/disable)
|
||||
static void passwordHelperLoggingEnable( bool enabled, QgsMessageBar *msgbar, int timeout = 0 );
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSAUTHGUIUTILS_H
|
||||
|
@ -19,9 +19,9 @@
|
||||
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QSettings>
|
||||
#include <QThread>
|
||||
|
||||
static QString invalidStyle_( const QString &selector = QStringLiteral( "QLineEdit" ) )
|
||||
@ -43,6 +43,8 @@ QgsCredentialDialog::QgsCredentialDialog( QWidget *parent, Qt::WindowFlags fl )
|
||||
Qt::BlockingQueuedConnection );
|
||||
mOkButton = buttonBox->button( QDialogButtonBox::Ok );
|
||||
leMasterPass->setPlaceholderText( tr( "Required" ) );
|
||||
chkbxPasswordHelperEnable->setText( tr( "Store/update the master password in your %1" )
|
||||
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
|
||||
leUsername->setFocus();
|
||||
}
|
||||
|
||||
@ -68,6 +70,7 @@ void QgsCredentialDialog::requestCredentials( const QString &realm, QString *use
|
||||
QgsDebugMsg( "Entering." );
|
||||
stackedWidget->setCurrentIndex( 0 );
|
||||
|
||||
chkbxPasswordHelperEnable->setChecked( QgsAuthManager::instance()->passwordHelperEnabled( ) );
|
||||
labelRealm->setText( realm );
|
||||
leUsername->setText( *username );
|
||||
lePassword->setText( *password );
|
||||
@ -121,6 +124,8 @@ void QgsCredentialDialog::requestCredentialsMasterPassword( QString *password, b
|
||||
QString titletxt( stored ? tr( "Enter CURRENT master authentication password" ) : tr( "Set NEW master authentication password" ) );
|
||||
lblPasswordTitle->setText( titletxt );
|
||||
|
||||
chkbxPasswordHelperEnable->setChecked( QgsAuthManager::instance()->passwordHelperEnabled( ) );
|
||||
|
||||
leMasterPassVerify->setVisible( !stored );
|
||||
lblDontForget->setVisible( !stored );
|
||||
|
||||
@ -170,6 +175,11 @@ void QgsCredentialDialog::requestCredentialsMasterPassword( QString *password, b
|
||||
else
|
||||
{
|
||||
*password = leMasterPass->text();
|
||||
// Let's store user's preferences to use the password helper
|
||||
if ( chkbxPasswordHelperEnable->isChecked() != QgsAuthManager::instance()->passwordHelperEnabled( ) )
|
||||
{
|
||||
QgsAuthManager::instance()->setPasswordHelperEnabled( chkbxPasswordHelperEnable->isChecked() );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${QSCINTILLA_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
INCLUDE_DIRECTORIES(
|
||||
../../core
|
||||
|
@ -32,6 +32,7 @@ INCLUDE_DIRECTORIES(
|
||||
INCLUDE_DIRECTORIES(SYSTEM
|
||||
${GDAL_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
ADD_LIBRARY(wcsprovider MODULE ${WCS_SRCS} ${WCS_MOC_SRCS})
|
||||
|
@ -53,6 +53,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${EXPAT_INCLUDE_DIR}
|
||||
${QSCINTILLA_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${GDAL_INCLUDE_DIR} # needed by qgsvectorfilewriter.h
|
||||
${SQLITE3_INCLUDE_DIR}
|
||||
)
|
||||
|
@ -43,6 +43,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${QT_QTSCRIPT_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
ADD_LIBRARY(wmsprovider_a STATIC ${WMS_SRCS} ${WMS_MOC_SRCS})
|
||||
|
@ -105,6 +105,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${QT_INCLUDE_DIR}
|
||||
${QGIS_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
)
|
||||
INCLUDE_DIRECTORIES(
|
||||
${CMAKE_BINARY_DIR}/src/core
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>277</width>
|
||||
<width>396</width>
|
||||
<height>289</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -30,7 +30,7 @@
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
@ -154,6 +154,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chkbxPasswordHelperEnable">
|
||||
<property name="text">
|
||||
<string>Store master password in your password manager</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblDontForget">
|
||||
<property name="styleSheet">
|
||||
|
@ -32,6 +32,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${PROJ_INCLUDE_DIR}
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${QSCINTILLA_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
@ -26,6 +26,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${PROJ_INCLUDE_DIR}
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${SQLITE3_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsauthmanager.h"
|
||||
#include "qgsauthconfig.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
/** \ingroup UnitTests
|
||||
* Unit tests for QgsAuthManager
|
||||
@ -38,15 +39,20 @@ class TestQgsAuthManager: public QObject
|
||||
public:
|
||||
TestQgsAuthManager();
|
||||
|
||||
public slots:
|
||||
|
||||
void doSync();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void init();
|
||||
void cleanup() {}
|
||||
void cleanup();
|
||||
|
||||
void testMasterPassword();
|
||||
void testAuthConfigs();
|
||||
void testAuthMethods();
|
||||
void testPasswordHelper();
|
||||
|
||||
private:
|
||||
void cleanupTempDir();
|
||||
@ -129,6 +135,13 @@ void TestQgsAuthManager::initTestCase()
|
||||
// all tests should now have a valid qgis-auth.db and stored/set master password
|
||||
}
|
||||
|
||||
void TestQgsAuthManager::cleanup()
|
||||
{
|
||||
// Restore password_helper_insecure_fallback value
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth );
|
||||
}
|
||||
|
||||
void TestQgsAuthManager::cleanupTempDir()
|
||||
{
|
||||
QDir tmpDir = QDir( mTempDir );
|
||||
@ -402,5 +415,58 @@ QList<QgsAuthMethodConfig> TestQgsAuthManager::registerAuthConfigs()
|
||||
return configs;
|
||||
}
|
||||
|
||||
|
||||
void TestQgsAuthManager::doSync()
|
||||
{
|
||||
QgsAuthManager *authm = QgsAuthManager::instance();
|
||||
QVERIFY( authm->passwordHelperSync( ) );
|
||||
}
|
||||
|
||||
void TestQgsAuthManager::testPasswordHelper()
|
||||
{
|
||||
|
||||
QgsAuthManager *authm = QgsAuthManager::instance();
|
||||
authm->clearMasterPassword();
|
||||
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "password_helper_insecure_fallback" ), true, QgsSettings::Section::Auth );
|
||||
|
||||
// Test enable/disable
|
||||
// It should be enabled by default
|
||||
QVERIFY( authm->passwordHelperEnabled() );
|
||||
authm->setPasswordHelperEnabled( false );
|
||||
QVERIFY( ! authm->passwordHelperEnabled() );
|
||||
authm->setPasswordHelperEnabled( true );
|
||||
QVERIFY( authm->passwordHelperEnabled() );
|
||||
|
||||
// Sync with wallet
|
||||
QVERIFY( authm->setMasterPassword( mPass, true ) );
|
||||
QVERIFY( authm->masterPasswordIsSet( ) );
|
||||
QObject::connect( authm, &QgsAuthManager::passwordHelperSuccess,
|
||||
QApplication::instance(), &QCoreApplication::quit );
|
||||
QObject::connect( authm, &QgsAuthManager::passwordHelperFailure,
|
||||
QApplication::instance(), &QCoreApplication::quit );
|
||||
QMetaObject::invokeMethod( this, "doSync", Qt::QueuedConnection );
|
||||
qApp->exec();
|
||||
authm->clearMasterPassword();
|
||||
QVERIFY( authm->setMasterPassword( ) );
|
||||
QVERIFY( authm->masterPasswordIsSet( ) );
|
||||
|
||||
// Delete from wallet
|
||||
authm->clearMasterPassword();
|
||||
QVERIFY( authm->passwordHelperDelete( ) );
|
||||
QVERIFY( ! authm->setMasterPassword( ) );
|
||||
QVERIFY( ! authm->masterPasswordIsSet( ) );
|
||||
|
||||
// Re-sync
|
||||
QVERIFY( authm->setMasterPassword( mPass, true ) );
|
||||
QMetaObject::invokeMethod( this, "doSync", Qt::QueuedConnection );
|
||||
qApp->exec();
|
||||
authm->clearMasterPassword();
|
||||
QVERIFY( authm->setMasterPassword( ) );
|
||||
QVERIFY( authm->masterPasswordIsSet( ) );
|
||||
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsAuthManager )
|
||||
#include "testqgsauthmanager.moc"
|
||||
|
@ -30,6 +30,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${QWT_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${QSCINTILLA_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
@ -20,6 +20,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${PROJ_INCLUDE_DIR}
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${QCA_INCLUDE_DIR}
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${POSTGRES_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user