[authentication] Add a pair of APIs to export and import configurations to/from XML

This commit is contained in:
nirvn 2021-05-12 14:21:09 +07:00
parent abf5c9754c
commit f916c06a8e
7 changed files with 232 additions and 0 deletions

View File

@ -11,6 +11,7 @@
class QgsAuthMethodConfig
{
%Docstring(signature="appended")
@ -184,6 +185,28 @@ 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)
:param document: DOM document
.. versionadded:: 3.20
%End
};

View File

@ -298,6 +298,23 @@ 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
%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
%End
bool removeAllAuthenticationConfigs();

View File

@ -157,6 +157,34 @@ 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 );
element.setAttribute( QStringLiteral( "config" ), configString() );
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" ) );
loadConfigString( element.attribute( QStringLiteral( "config" ) ) );
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,23 @@ 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)
* \param document DOM document
* \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,118 @@ 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 f( filename );
if ( !f.open( QFile::ReadOnly ) )
{
return false;
}
QDomDocument document( QStringLiteral( "qgis_authentication" ) );
if ( !document.setContent( &f ) )
{
f.close();
return false;
}
f.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 );
qDebug() << configuration.nodeName();
qDebug() << authMethodConfig.id();
storeAuthenticationConfig( authMethodConfig );
configuration = configuration.nextSiblingElement();
}
return true;
}
bool QgsAuthManager::removeAllAuthenticationConfigs()
{
QMutexLocker locker( mMutex.get() );

View File

@ -300,6 +300,21 @@ 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
*/
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
*/
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

@ -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 );