Skip to content
Permalink
Browse files
Merge pull request #43193 from nirvn/auth_imex
[authentication] Add a pair of APIs to export and import configurations to/from XML
  • Loading branch information
nirvn committed May 13, 2021
2 parents da08c8a + ad1c94c commit e3cb3e5a016046d09349aed73d2c280e9fb9bcd9
@@ -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

};
@@ -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();
@@ -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:
@@ -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

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

0 comments on commit e3cb3e5

Please sign in to comment.