Merge pull request #43193 from nirvn/auth_imex

[authentication] Add a pair of APIs to export and import configurations to/from XML
This commit is contained in:
Mathieu Pellerin 2021-05-13 12:55:06 +07:00 committed by GitHub
commit e3cb3e5a01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 430 additions and 12 deletions

View File

@ -11,6 +11,7 @@
class QgsAuthMethodConfig
{
%Docstring(signature="appended")
@ -184,6 +185,27 @@ against the config's :py:func:`~QgsAuthMethodConfig.uri` for auto-selecting auth
:param accessurl: A URL to process
:param resource: Output variable for result
:param withpath: Whether to include the URI's path in output
%End
bool writeXml( QDomElement &parentElement, QDomDocument &document );
%Docstring
Stores the configuration in a DOM
:param parentElement: parent DOM element
:param document: DOM document
.. seealso:: :py:func:`readXml`
.. versionadded:: 3.20
%End
bool readXml( const QDomElement &element );
%Docstring
from a DOM element.
:param element: is the DOM node corresponding to item (e.g. 'LayoutItem' element)
.. versionadded:: 3.20
%End
};

View File

@ -298,6 +298,27 @@ Remove an authentication config in the database
:param authcfg: Associated authentication config id
:return: Whether operation succeeded
%End
bool exportAuthenticationConfigsToXml( const QString &filename, const QStringList &authcfgs, const QString &password = QString() );
%Docstring
Export authentication configurations to an XML file
:param filename: The file path to save the XML content to
:param authcfgs: The list of configuration IDs to export
:param password: A password string to encrypt the XML content
.. versionadded:: 3.20
%End
bool importAuthenticationConfigsFromXml( const QString &filename, const QString &password = QString() );
%Docstring
Import authentication configurations from an XML file
:param filename: The file path from which the XML content will be read
:param password: A password string to decrypt the XML content
.. versionadded:: 3.20
%End
bool removeAllAuthenticationConfigs();

View File

@ -33,6 +33,13 @@ Widget for editing authentication configurations directly in database
void toggleTitleVisibility( bool visible );
%Docstring
Hide the widget's title, e.g. when embedding
%End
QStringList selectedAuthenticationConfigIds() const;
%Docstring
Returns the list of selected authentication configuration IDs
.. versionadded:: 3.20
%End
public slots:

View File

@ -15,6 +15,8 @@
***************************************************************************/
#include "qgsauthconfig.h"
#include "qgsauthcertutils.h"
#include "qgsxmlutils.h"
#include <QtCrypto>
@ -23,8 +25,6 @@
#include <QCryptographicHash>
#include <QUrl>
#include "qgsauthcertutils.h"
//////////////////////////////////////////////
// QgsAuthMethodConfig
@ -157,6 +157,50 @@ bool QgsAuthMethodConfig::uriToResource( const QString &accessurl, QString *reso
}
bool QgsAuthMethodConfig::writeXml( QDomElement &parentElement, QDomDocument &document )
{
QDomElement element = document.createElement( QStringLiteral( "AuthMethodConfig" ) );
element.setAttribute( QStringLiteral( "method" ), mMethod );
element.setAttribute( QStringLiteral( "id" ), mId );
element.setAttribute( QStringLiteral( "name" ), mName );
element.setAttribute( QStringLiteral( "version" ), QString::number( mVersion ) );
element.setAttribute( QStringLiteral( "uri" ), mUri );
QDomElement configElements = document.createElement( QStringLiteral( "Config" ) );
QgsStringMap::const_iterator i = mConfigMap.constBegin();
while ( i != mConfigMap.constEnd() )
{
configElements.setAttribute( i.key(), i.value() );
++i;
}
element.appendChild( configElements );
parentElement.appendChild( element );
return true;
}
bool QgsAuthMethodConfig::readXml( const QDomElement &element )
{
if ( element.nodeName() != QLatin1String( "AuthMethodConfig" ) )
return false;
mMethod = element.attribute( QStringLiteral( "method" ) );
mId = element.attribute( QStringLiteral( "id" ) );
mName = element.attribute( QStringLiteral( "name" ) );
mVersion = element.attribute( QStringLiteral( "version" ) ).toInt();
mUri = element.attribute( QStringLiteral( "uri" ) );
clearConfigMap();
QDomNamedNodeMap configAttributes = element.firstChildElement().attributes();
for ( int i = 0; i < configAttributes.length(); i++ )
{
QDomAttr configAttribute = configAttributes.item( i ).toAttr();
setConfig( configAttribute.name(), configAttribute.value() );
}
return true;
}
#ifndef QT_NO_SSL
//////////////////////////////////////////////////////

View File

@ -18,8 +18,11 @@
#define QGSAUTHCONFIG_H
#include "qgis_core.h"
#include <QHash>
#include <QString>
#include <QDomElement>
#include <QDomDocument>
#ifndef QT_NO_SSL
#include <QSslCertificate>
@ -160,6 +163,22 @@ class CORE_EXPORT QgsAuthMethodConfig
*/
static bool uriToResource( const QString &accessurl, QString *resource, bool withpath = false );
/**
* Stores the configuration in a DOM
* \param parentElement parent DOM element
* \param document DOM document
* \see readXml()
* \since QGIS 3.20
*/
bool writeXml( QDomElement &parentElement, QDomDocument &document );
/**
* from a DOM element.
* \param element is the DOM node corresponding to item (e.g. 'LayoutItem' element)
* \since QGIS 3.20
*/
bool readXml( const QDomElement &element );
private:
QString mId;
QString mName;

View File

@ -31,6 +31,8 @@
#include <QTimer>
#include <QVariant>
#include <QSqlDriver>
#include <QDomElement>
#include <QDomDocument>
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
#include <QRandomGenerator>
@ -1323,6 +1325,116 @@ bool QgsAuthManager::removeAuthenticationConfig( const QString &authcfg )
return true;
}
bool QgsAuthManager::exportAuthenticationConfigsToXml( const QString &filename, const QStringList &authcfgs, const QString &password )
{
if ( filename.isEmpty() )
return false;
QDomDocument document( QStringLiteral( "qgis_authentication" ) );
QDomElement root = document.createElement( QStringLiteral( "qgis_authentication" ) );
document.appendChild( root );
QString civ;
if ( !password.isEmpty() )
{
QString salt;
QString hash;
QgsAuthCrypto::passwordKeyHash( password, &salt, &hash, &civ );
root.setAttribute( QStringLiteral( "salt" ), salt );
root.setAttribute( QStringLiteral( "hash" ), hash );
root.setAttribute( QStringLiteral( "civ" ), civ );
}
QDomElement configurations = document.createElement( QStringLiteral( "configurations" ) );
for ( const QString &authcfg : authcfgs )
{
QgsAuthMethodConfig authMethodConfig;
bool ok = loadAuthenticationConfig( authcfg, authMethodConfig, true );
if ( ok )
{
authMethodConfig.writeXml( configurations, document );
}
}
if ( !password.isEmpty() )
{
QString configurationsString;
QTextStream ts( &configurationsString );
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
ts.setCodec( "UTF-8" );
#endif
configurations.save( ts, 2 );
root.appendChild( document.createTextNode( QgsAuthCrypto::encrypt( password, civ, configurationsString ) ) );
}
else
{
root.appendChild( configurations );
}
QFile file( filename );
if ( !file.open( QFile::WriteOnly | QIODevice::Truncate ) )
return false;
QTextStream ts( &file );
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
ts.setCodec( "UTF-8" );
#endif
document.save( ts, 2 );
file.close();
return true;
}
bool QgsAuthManager::importAuthenticationConfigsFromXml( const QString &filename, const QString &password )
{
QFile file( filename );
if ( !file.open( QFile::ReadOnly ) )
{
return false;
}
QDomDocument document( QStringLiteral( "qgis_authentication" ) );
if ( !document.setContent( &file ) )
{
file.close();
return false;
}
file.close();
QDomElement root = document.documentElement();
if ( root.tagName() != QLatin1String( "qgis_authentication" ) )
{
return false;
}
QDomElement configurations;
if ( root.hasAttribute( QStringLiteral( "salt" ) ) )
{
QString salt = root.attribute( QStringLiteral( "salt" ) );
QString hash = root.attribute( QStringLiteral( "hash" ) );
QString civ = root.attribute( QStringLiteral( "civ" ) );
if ( !QgsAuthCrypto::verifyPasswordKeyHash( password, salt, hash ) )
return false;
document.setContent( QgsAuthCrypto::decrypt( password, civ, root.text() ) );
configurations = document.firstChild().toElement();
}
else
{
configurations = root.firstChildElement( QStringLiteral( "configurations" ) );
}
QDomElement configuration = configurations.firstChildElement();
while ( !configuration.isNull() )
{
QgsAuthMethodConfig authMethodConfig;
authMethodConfig.readXml( configuration );
storeAuthenticationConfig( authMethodConfig );
configuration = configuration.nextSiblingElement();
}
return true;
}
bool QgsAuthManager::removeAllAuthenticationConfigs()
{
QMutexLocker locker( mMutex.get() );

View File

@ -300,6 +300,23 @@ class CORE_EXPORT QgsAuthManager : public QObject
*/
bool removeAuthenticationConfig( const QString &authcfg );
/**
* Export authentication configurations to an XML file
* \param filename The file path to save the XML content to
* \param authcfgs The list of configuration IDs to export
* \param password A password string to encrypt the XML content
* \since QGIS 3.20
*/
bool exportAuthenticationConfigsToXml( const QString &filename, const QStringList &authcfgs, const QString &password = QString() );
/**
* Import authentication configurations from an XML file
* \param filename The file path from which the XML content will be read
* \param password A password string to decrypt the XML content
* \since QGIS 3.20
*/
bool importAuthenticationConfigsFromXml( const QString &filename, const QString &password = QString() );
/**
* Clear all authentication configs from table in database and from provider caches
* \returns Whether operation succeeded

View File

@ -90,6 +90,8 @@ QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, b
checkSelection();
// set up utility actions menu
mActionImportAuthenticationConfigs = new QAction( tr( "Import authentication configurations from file" ), this );
mActionExportSelectedAuthenticationConfigs = new QAction( tr( "Export selected authentication configurations to file" ), this );
mActionSetMasterPassword = new QAction( QStringLiteral( "Input master password" ), this );
mActionClearCachedMasterPassword = new QAction( QStringLiteral( "Clear cached master password" ), this );
mActionResetMasterPassword = new QAction( QStringLiteral( "Reset master password" ), this );
@ -97,6 +99,8 @@ QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, b
mActionRemoveAuthConfigs = new QAction( QStringLiteral( "Remove all authentication configurations" ), this );
mActionEraseAuthDatabase = new QAction( QStringLiteral( "Erase authentication database" ), this );
connect( mActionImportAuthenticationConfigs, &QAction::triggered, this, &QgsAuthConfigEditor::importAuthenticationConfigs );
connect( mActionExportSelectedAuthenticationConfigs, &QAction::triggered, this, &QgsAuthConfigEditor::exportSelectedAuthenticationConfigs );
connect( mActionSetMasterPassword, &QAction::triggered, this, &QgsAuthConfigEditor::setMasterPassword );
connect( mActionClearCachedMasterPassword, &QAction::triggered, this, &QgsAuthConfigEditor::clearCachedMasterPassword );
connect( mActionResetMasterPassword, &QAction::triggered, this, &QgsAuthConfigEditor::resetMasterPassword );
@ -112,6 +116,9 @@ QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, b
mAuthUtilitiesMenu->addAction( mActionClearCachedAuthConfigs );
mAuthUtilitiesMenu->addAction( mActionRemoveAuthConfigs );
mAuthUtilitiesMenu->addSeparator();
mAuthUtilitiesMenu->addAction( mActionImportAuthenticationConfigs );
mAuthUtilitiesMenu->addAction( mActionExportSelectedAuthenticationConfigs );
mAuthUtilitiesMenu->addSeparator();
mAuthUtilitiesMenu->addAction( mActionEraseAuthDatabase );
btnAuthUtilities->setMenu( mAuthUtilitiesMenu );
@ -119,6 +126,16 @@ QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, b
}
}
void QgsAuthConfigEditor::importAuthenticationConfigs()
{
QgsAuthGuiUtils::importAuthenticationConfigs( messageBar() );
}
void QgsAuthConfigEditor::exportSelectedAuthenticationConfigs()
{
QgsAuthGuiUtils::exportSelectedAuthenticationConfigs( selectedAuthenticationConfigIds(), messageBar() );
}
void QgsAuthConfigEditor::setMasterPassword()
{
QgsAuthGuiUtils::setMasterPassword( messageBar() );
@ -163,6 +180,17 @@ void QgsAuthConfigEditor::toggleTitleVisibility( bool visible )
}
}
QStringList QgsAuthConfigEditor::selectedAuthenticationConfigIds() const
{
QStringList ids;
QModelIndexList selection = tableViewConfigs->selectionModel()->selectedRows( 0 );
for ( QModelIndex index : selection )
{
ids << index.sibling( index.row(), 0 ).data().toString();
}
return ids;
}
void QgsAuthConfigEditor::setShowUtilitiesButton( bool show )
{
if ( !mDisabled )
@ -255,16 +283,18 @@ void QgsAuthConfigEditor::btnRemoveConfig_clicked()
if ( selection.empty() )
return;
QModelIndex indx = selection.at( 0 );
QString name = indx.sibling( indx.row(), 1 ).data().toString();
if ( QMessageBox::warning( this, tr( "Remove Configuration" ),
tr( "Are you sure you want to remove '%1'?\n\n"
"Operation can NOT be undone!" ).arg( name ),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Cancel ) == QMessageBox::Ok )
for ( QModelIndex index : selection )
{
mConfigModel->removeRow( indx.row() );
QString name = index.sibling( index.row(), 1 ).data().toString();
if ( QMessageBox::warning( this, tr( "Remove Configuration" ),
tr( "Are you sure you want to remove '%1'?\n\n"
"Operation can NOT be undone!" ).arg( name ),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Cancel ) == QMessageBox::Ok )
{
mConfigModel->removeRow( index.row() );
}
}
}

View File

@ -48,6 +48,12 @@ class GUI_EXPORT QgsAuthConfigEditor : public QWidget, private Ui::QgsAuthConfig
//! Hide the widget's title, e.g. when embedding
void toggleTitleVisibility( bool visible );
/**
* Returns the list of selected authentication configuration IDs
* \since QGIS 3.20
*/
QStringList selectedAuthenticationConfigIds() const;
public slots:
//! Sets whether to show the widget's utilities button, e.g. when embedding
void setShowUtilitiesButton( bool show = true );
@ -59,6 +65,12 @@ class GUI_EXPORT QgsAuthConfigEditor : public QWidget, private Ui::QgsAuthConfig
//! Repopulate the view with table contents
void refreshTableView();
//! Import authentication configurations from a XML file
void importAuthenticationConfigs();
//! Exports selected authentication configurations to a XML file
void exportSelectedAuthenticationConfigs();
//! Sets the cached master password (and verifies it if its hash is in authentication database)
void setMasterPassword();
@ -100,6 +112,8 @@ class GUI_EXPORT QgsAuthConfigEditor : public QWidget, private Ui::QgsAuthConfig
QSqlTableModel *mConfigModel = nullptr;
QMenu *mAuthUtilitiesMenu = nullptr;
QAction *mActionImportAuthenticationConfigs = nullptr;
QAction *mActionExportSelectedAuthenticationConfigs = nullptr;
QAction *mActionSetMasterPassword = nullptr;
QAction *mActionClearCachedMasterPassword = nullptr;
QAction *mActionResetMasterPassword = nullptr;

View File

@ -142,6 +142,8 @@ void QgsAuthEditorWidgets::setupUtilitiesMenu()
this, &QgsAuthEditorWidgets::authMessageOut );
// set up utility actions menu
mActionImportAuthenticationConfigs = new QAction( tr( "Import authentication configurations from file" ), this );
mActionExportSelectedAuthenticationConfigs = new QAction( tr( "Export selected authentication configurations to file" ), this );
mActionSetMasterPassword = new QAction( tr( "Input master password" ), this );
mActionClearCachedMasterPassword = new QAction( tr( "Clear cached master password" ), this );
mActionResetMasterPassword = new QAction( tr( "Reset master password" ), this );
@ -168,6 +170,8 @@ void QgsAuthEditorWidgets::setupUtilitiesMenu()
mActionPasswordHelperLoggingEnable->setCheckable( true );
mActionPasswordHelperLoggingEnable->setChecked( QgsApplication::authManager()->passwordHelperLoggingEnabled() );
connect( mActionImportAuthenticationConfigs, &QAction::triggered, this, &QgsAuthEditorWidgets::importAuthenticationConfigs );
connect( mActionExportSelectedAuthenticationConfigs, &QAction::triggered, this, &QgsAuthEditorWidgets::exportSelectedAuthenticationConfigs );
connect( mActionSetMasterPassword, &QAction::triggered, this, &QgsAuthEditorWidgets::setMasterPassword );
connect( mActionClearCachedMasterPassword, &QAction::triggered, this, &QgsAuthEditorWidgets::clearCachedMasterPassword );
connect( mActionResetMasterPassword, &QAction::triggered, this, &QgsAuthEditorWidgets::resetMasterPassword );
@ -206,11 +210,27 @@ void QgsAuthEditorWidgets::setupUtilitiesMenu()
mAuthUtilitiesMenu->addAction( mActionClearCachedAuthConfigs );
mAuthUtilitiesMenu->addAction( mActionRemoveAuthConfigs );
mAuthUtilitiesMenu->addSeparator();
mAuthUtilitiesMenu->addAction( mActionImportAuthenticationConfigs );
mAuthUtilitiesMenu->addAction( mActionExportSelectedAuthenticationConfigs );
mAuthUtilitiesMenu->addSeparator();
mAuthUtilitiesMenu->addAction( mActionEraseAuthDatabase );
btnAuthUtilities->setMenu( mAuthUtilitiesMenu );
}
void QgsAuthEditorWidgets::importAuthenticationConfigs()
{
QgsAuthGuiUtils::importAuthenticationConfigs( messageBar() );
}
void QgsAuthEditorWidgets::exportSelectedAuthenticationConfigs()
{
if ( !wdgtConfigEditor )
return;
QgsAuthGuiUtils::exportSelectedAuthenticationConfigs( wdgtConfigEditor->selectedAuthenticationConfigIds(), messageBar() );
}
void QgsAuthEditorWidgets::setMasterPassword()
{
QgsAuthGuiUtils::setMasterPassword( messageBar() );

View File

@ -70,6 +70,12 @@ class GUI_EXPORT QgsAuthEditorWidgets : public QWidget, private Ui::QgsAuthEdito
void btnCertManager_clicked();
void btnAuthPlugins_clicked();
//! Import authentication configurations from a XML file
void importAuthenticationConfigs();
//! Exports selected authentication configurations to a XML file
void exportSelectedAuthenticationConfigs();
//! Sets the cached master password (and verifies it if its hash is in authentication database)
void setMasterPassword();
@ -109,6 +115,8 @@ class GUI_EXPORT QgsAuthEditorWidgets : public QWidget, private Ui::QgsAuthEdito
QgsMessageBar *messageBar();
QMenu *mAuthUtilitiesMenu = nullptr;
QAction *mActionExportSelectedAuthenticationConfigs = nullptr;
QAction *mActionImportAuthenticationConfigs = nullptr;
QAction *mActionSetMasterPassword = nullptr;
QAction *mActionClearCachedMasterPassword = nullptr;
QAction *mActionResetMasterPassword = nullptr;
@ -121,6 +129,7 @@ class GUI_EXPORT QgsAuthEditorWidgets : public QWidget, private Ui::QgsAuthEdito
QAction *mActionPasswordHelperLoggingEnable = nullptr;
QAction *mActionClearAccessCacheNow = nullptr;
QAction *mActionAutoClearAccessCache = nullptr;
};
#endif // QGSAUTHEDITORWIDGETS_H

View File

@ -17,6 +17,7 @@
#include "qgsauthguiutils.h"
#include <QFileDialog>
#include <QInputDialog>
#include <QLineEdit>
#include <QMessageBox>
@ -75,6 +76,81 @@ bool QgsAuthGuiUtils::isDisabled( QgsMessageBar *msgbar )
return false;
}
void QgsAuthGuiUtils::exportSelectedAuthenticationConfigs( QStringList authenticationConfigIds, QgsMessageBar *msgbar )
{
QString password = QInputDialog::getText( msgbar, QObject::tr( "Export Authentication Configurations" ),
QObject::tr( "Enter a password encrypt the configuration file:" ), QLineEdit::Password );
if ( password.isEmpty() )
{
if ( QMessageBox::warning( msgbar,
QObject::tr( "Export Authentication Configurations" ),
QObject::tr( "Exporting authentication configurations with a blank password will result in a plain text file which may contain sensitive information. Are you sure you want to do this?" ),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Cancel ) == QMessageBox::Cancel )
{
return;
}
}
QString filename = QFileDialog::getSaveFileName( msgbar, QObject::tr( "Export Authentication Configurations" ), QDir::homePath(),
QObject::tr( "XML files (*.xml *.XML)" ) );
if ( filename.isEmpty() )
return;
bool ok = QgsApplication::authManager()->exportAuthenticationConfigsToXml( filename, authenticationConfigIds, password );
if ( !ok )
{
msgbar->pushMessage( QgsApplication::authManager()->authManTag(),
QObject::tr( "Export of authentication configurations failed." ),
Qgis::Critical );
}
}
void QgsAuthGuiUtils::importAuthenticationConfigs( QgsMessageBar *msgbar )
{
QString filename = QFileDialog::getOpenFileName( msgbar, QObject::tr( "Export Authentication Configurations" ), QDir::homePath(),
QObject::tr( "XML files (*.xml *.XML)" ) );
if ( filename.isEmpty() )
return;
QFile file( filename );
if ( !file.open( QFile::ReadOnly ) )
{
return;
}
QDomDocument document( QStringLiteral( "qgis_authentication" ) );
if ( !document.setContent( &file ) )
{
file.close();
return;
}
file.close();
QDomElement root = document.documentElement();
if ( root.tagName() != QLatin1String( "qgis_authentication" ) )
{
return;
}
QString password;
if ( root.hasAttribute( QStringLiteral( "salt" ) ) )
{
password = QInputDialog::getText( msgbar, QObject::tr( "Import Authentication Configurations" ),
QObject::tr( "Enter the password to decrypt the configurations file:" ), QLineEdit::Password );
}
bool ok = QgsApplication::authManager()->importAuthenticationConfigsFromXml( filename, password );
if ( !ok )
{
msgbar->pushMessage( QgsApplication::authManager()->authManTag(),
QObject::tr( "Import of authentication configurations failed." ),
Qgis::Critical );
}
}
void QgsAuthGuiUtils::setMasterPassword( QgsMessageBar *msgbar )
{
if ( QgsAuthGuiUtils::isDisabled( msgbar ) )

View File

@ -60,6 +60,18 @@ class GUI_EXPORT QgsAuthGuiUtils
//! Verify the authentication system is active, else notify user
static bool isDisabled( QgsMessageBar *msgbar );
/**
* Import authentication configurations from a XML file
* \since QGIS 3.20
*/
static void importAuthenticationConfigs( QgsMessageBar *msgbar );
/**
* Exports selected authentication configurations to a XML file
* \since QGIS 3.20
*/
static void exportSelectedAuthenticationConfigs( QStringList authenticationConfigIds, QgsMessageBar *msgbar );
//! Sets the cached master password (and verifies it if its hash is in authentication database)
static void setMasterPassword( QgsMessageBar *msgbar );

View File

@ -128,7 +128,7 @@
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>

View File

@ -323,6 +323,21 @@ void TestQgsAuthManager::testAuthConfigs()
QVERIFY( authm->storeAuthenticationConfig( config ) );
idcfgmap.insert( config.id(), config );
}
QCOMPARE( authm->availableAuthMethodConfigs().size(), 3 );
// Password-less export / import
QVERIFY( authm->exportAuthenticationConfigsToXml( mTempDir + QStringLiteral( "/configs.xml" ), idcfgmap.keys() ) );
QVERIFY( authm->removeAllAuthenticationConfigs() );
QVERIFY( authm->importAuthenticationConfigsFromXml( mTempDir + QStringLiteral( "/configs.xml" ) ) );
QCOMPARE( authm->availableAuthMethodConfigs().size(), 3 );
// Password-protected export / import
QVERIFY( authm->exportAuthenticationConfigsToXml( mTempDir + QStringLiteral( "/configs.xml" ), idcfgmap.keys(), QStringLiteral( "1234" ) ) );
QVERIFY( authm->removeAllAuthenticationConfigs() );
QVERIFY( authm->importAuthenticationConfigsFromXml( mTempDir + QStringLiteral( "/configs.xml" ), QStringLiteral( "1234" ) ) );
QgsAuthMethodConfigsMap authmap( authm->availableAuthMethodConfigs() );
QCOMPARE( authmap.size(), 3 );