mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Merge pull request #5526 from boundlessgeo/ogr_authconfig_2
[auth][needs-docs] Authentication configuration support in OGR provider
This commit is contained in:
commit
8dd70c1dab
@ -33,3 +33,4 @@ PyQgsServerAccessControl
|
||||
# Need a local postgres installation
|
||||
PyQgsAuthManagerPKIPostgresTest
|
||||
PyQgsAuthManagerPasswordPostgresTest
|
||||
PyQgsAuthManagerOgrPostgresTest
|
||||
|
@ -42,6 +42,7 @@ QgsAuthBasicMethod::QgsAuthBasicMethod()
|
||||
<< QStringLiteral( "wfs" ) // convert to lowercase
|
||||
<< QStringLiteral( "wcs" )
|
||||
<< QStringLiteral( "wms" )
|
||||
<< QStringLiteral( "ogr" )
|
||||
<< QStringLiteral( "proxy" ) );
|
||||
}
|
||||
|
||||
@ -85,7 +86,6 @@ bool QgsAuthBasicMethod::updateNetworkRequest( QNetworkRequest &request, const Q
|
||||
bool QgsAuthBasicMethod::updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
|
||||
const QString &dataprovider )
|
||||
{
|
||||
Q_UNUSED( dataprovider )
|
||||
QgsAuthMethodConfig mconfig = getMethodConfig( authcfg );
|
||||
if ( !mconfig.isValid() )
|
||||
{
|
||||
@ -102,29 +102,8 @@ bool QgsAuthBasicMethod::updateDataSourceUriItems( QStringList &connectionItems,
|
||||
return false;
|
||||
}
|
||||
|
||||
QString userparam = "user='" + escapeUserPass( username ) + '\'';
|
||||
int userindx = connectionItems.indexOf( QRegExp( "^user='.*" ) );
|
||||
if ( userindx != -1 )
|
||||
{
|
||||
connectionItems.replace( userindx, userparam );
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionItems.append( userparam );
|
||||
}
|
||||
|
||||
QString passparam = "password='" + escapeUserPass( password ) + '\'';
|
||||
int passindx = connectionItems.indexOf( QRegExp( "^password='.*" ) );
|
||||
if ( passindx != -1 )
|
||||
{
|
||||
connectionItems.replace( passindx, passparam );
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionItems.append( passparam );
|
||||
}
|
||||
|
||||
// add extra CAs
|
||||
// SSL Extra CAs
|
||||
QString caparam;
|
||||
QList<QSslCertificate> cas;
|
||||
cas = QgsApplication::authManager()->trustedCaCerts();
|
||||
// save CAs to temp file
|
||||
@ -134,18 +113,164 @@ bool QgsAuthBasicMethod::updateDataSourceUriItems( QStringList &connectionItems,
|
||||
QgsAuthCertUtils::certsToPemText( cas ) );
|
||||
if ( ! caFilePath.isEmpty() )
|
||||
{
|
||||
QString caparam = "sslrootcert='" + caFilePath + "'";
|
||||
int sslcaindx = connectionItems.indexOf( QRegExp( "^sslrootcert='.*" ) );
|
||||
if ( sslcaindx != -1 )
|
||||
caparam = "sslrootcert='" + caFilePath + "'";
|
||||
}
|
||||
|
||||
// Branch for OGR
|
||||
if ( dataprovider == QStringLiteral( "ogr" ) )
|
||||
{
|
||||
if ( ! password.isEmpty() )
|
||||
{
|
||||
connectionItems.replace( sslcaindx, caparam );
|
||||
QString fullUri( connectionItems.first() );
|
||||
QString uri( fullUri );
|
||||
// Handle sub-layers
|
||||
if ( fullUri.contains( '|' ) )
|
||||
{
|
||||
uri = uri.left( uri.indexOf( '|' ) );
|
||||
}
|
||||
// At least username must be set... password can be empty
|
||||
if ( ! username.isEmpty() )
|
||||
{
|
||||
// Inject credentials
|
||||
if ( uri.startsWith( QStringLiteral( "PG:" ) ) )
|
||||
{
|
||||
bool chopped = false;
|
||||
if ( uri.endsWith( '"' ) )
|
||||
{
|
||||
uri.chop( 1 );
|
||||
chopped = true;
|
||||
}
|
||||
if ( !username.isEmpty() )
|
||||
{
|
||||
uri += QStringLiteral( " user='%1'" ).arg( username );
|
||||
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( " password='%1'" ).arg( password );
|
||||
}
|
||||
// add extra CAs
|
||||
if ( ! caparam.isEmpty() )
|
||||
{
|
||||
uri += ' ' + caparam;
|
||||
}
|
||||
if ( chopped )
|
||||
uri += '"';
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "SDE:" ) ) )
|
||||
{
|
||||
uri = uri.replace( QRegExp( ",$" ), QStringLiteral( ",%1,%2" ).arg( username, password ) );
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "IDB" ) ) )
|
||||
{
|
||||
bool chopped = false;
|
||||
if ( uri.endsWith( '"' ) )
|
||||
{
|
||||
uri.chop( 1 );
|
||||
chopped = true;
|
||||
}
|
||||
uri += QStringLiteral( " user=%1" ).arg( username );
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( " pass=%1" ).arg( password );
|
||||
if ( chopped )
|
||||
uri += '"';
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "@driver=ingres" ) ) )
|
||||
{
|
||||
uri += QStringLiteral( ",userid=%1" ).arg( username );
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( ",password=%1" ).arg( password );
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "MySQL:" ) ) )
|
||||
{
|
||||
uri += QStringLiteral( ",user=%1" ).arg( username );
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( ",password=%1" ).arg( password );
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "MSSQL:" ) ) )
|
||||
{
|
||||
uri += QStringLiteral( ";uid=%1" ).arg( username );
|
||||
uri = uri.replace( QLatin1String( ";trusted_connection=yes" ), QString() );
|
||||
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( ";pwd=%1" ).arg( password );
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "OCI:" ) ) )
|
||||
{
|
||||
// OCI:userid/password@database_instance:table,table
|
||||
uri = uri.replace( QStringLiteral( "OCI:/" ), QStringLiteral( "OCI:%1/%2" ).arg( username, password ) );
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "ODBC:" ) ) )
|
||||
{
|
||||
if ( password.isEmpty() )
|
||||
{
|
||||
uri = uri.replace( QRegExp( "^ODBC:@?" ), "ODBC:" + username + '@' );
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = uri.replace( QRegExp( "^ODBC:@?" ), "ODBC:" + username + '/' + password + '@' );
|
||||
}
|
||||
}
|
||||
else if ( uri.startsWith( QStringLiteral( "couchdb" ) )
|
||||
|| uri.startsWith( QStringLiteral( "DODS" ) )
|
||||
|| uri.startsWith( "http://" )
|
||||
|| uri.startsWith( "https://" )
|
||||
|| uri.startsWith( "ftp://" ) // not really sure that this is supported ...
|
||||
)
|
||||
{
|
||||
uri = uri.replace( QStringLiteral( "://" ), QStringLiteral( "://%1:%2@" ).arg( username, password ) );
|
||||
}
|
||||
}
|
||||
// Handle sub-layers
|
||||
if ( fullUri.contains( '|' ) )
|
||||
{
|
||||
uri += '|' + fullUri.right( fullUri.length() - fullUri.lastIndexOf( '|' ) - 1 );
|
||||
}
|
||||
connectionItems.replace( 0, uri );
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionItems.append( caparam );
|
||||
QgsDebugMsg( QString( "Update URI items FAILED for authcfg: %1: password empty" ).arg( authcfg ) );
|
||||
}
|
||||
|
||||
}
|
||||
else // Not-ogr
|
||||
{
|
||||
QString userparam = "user='" + escapeUserPass( username ) + '\'';
|
||||
int userindx = connectionItems.indexOf( QRegExp( "^user='.*" ) );
|
||||
if ( userindx != -1 )
|
||||
{
|
||||
connectionItems.replace( userindx, userparam );
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionItems.append( userparam );
|
||||
}
|
||||
|
||||
QString passparam = "password='" + escapeUserPass( password ) + '\'';
|
||||
int passindx = connectionItems.indexOf( QRegExp( "^password='.*" ) );
|
||||
if ( passindx != -1 )
|
||||
{
|
||||
connectionItems.replace( passindx, passparam );
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionItems.append( passparam );
|
||||
}
|
||||
// add extra CAs
|
||||
if ( ! caparam.isEmpty() )
|
||||
{
|
||||
int sslcaindx = connectionItems.indexOf( QRegExp( "^sslrootcert='.*" ) );
|
||||
if ( sslcaindx != -1 )
|
||||
{
|
||||
connectionItems.replace( sslcaindx, caparam );
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionItems.append( caparam );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -57,18 +57,25 @@ QgsNewOgrConnection::QgsNewOgrConnection( QWidget *parent, const QString &connTy
|
||||
txtDatabase->setText( settings.value( key + "/database" ).toString() );
|
||||
QString port = settings.value( key + "/port" ).toString();
|
||||
txtPort->setText( port );
|
||||
txtUsername->setText( settings.value( key + "/username" ).toString() );
|
||||
if ( settings.value( key + "/save" ).toString() == QLatin1String( "true" ) )
|
||||
if ( settings.value( key + "/store_username" ).toString() == QLatin1String( "true" ) )
|
||||
{
|
||||
txtPassword->setText( settings.value( key + "/password" ).toString() );
|
||||
chkStorePassword->setChecked( true );
|
||||
mAuthSettingsDatabase->setUsername( settings.value( key + "/username" ).toString() );
|
||||
mAuthSettingsDatabase->setStoreUsernameChecked( true );
|
||||
}
|
||||
if ( settings.value( key + "/store_password" ).toString() == QLatin1String( "true" ) )
|
||||
{
|
||||
mAuthSettingsDatabase->setPassword( settings.value( key + "/password" ).toString() );
|
||||
mAuthSettingsDatabase->setStorePasswordChecked( true );
|
||||
}
|
||||
mAuthSettingsDatabase->setConfigId( settings.value( key + "/configid" ).toString() );
|
||||
cmbDatabaseTypes->setCurrentIndex( cmbDatabaseTypes->findText( connType ) );
|
||||
txtName->setText( connName );
|
||||
txtName->setEnabled( false );
|
||||
cmbDatabaseTypes->setEnabled( false );
|
||||
}
|
||||
txtName->setValidator( new QRegExpValidator( QRegExp( "[^\\/]+" ), txtName ) );
|
||||
mAuthSettingsDatabase->setDataprovider( QStringLiteral( "ogr" ) );
|
||||
mAuthSettingsDatabase->showStoreCheckboxes( true );
|
||||
}
|
||||
|
||||
QgsNewOgrConnection::~QgsNewOgrConnection()
|
||||
@ -80,9 +87,14 @@ QgsNewOgrConnection::~QgsNewOgrConnection()
|
||||
void QgsNewOgrConnection::testConnection()
|
||||
{
|
||||
QString uri;
|
||||
uri = createDatabaseURI( cmbDatabaseTypes->currentText(), txtHost->text(),
|
||||
txtDatabase->text(), txtPort->text(),
|
||||
txtUsername->text(), txtPassword->text() );
|
||||
uri = createDatabaseURI( cmbDatabaseTypes->currentText(),
|
||||
txtHost->text(),
|
||||
txtDatabase->text(),
|
||||
txtPort->text(),
|
||||
mAuthSettingsDatabase->configId(),
|
||||
mAuthSettingsDatabase->username(),
|
||||
mAuthSettingsDatabase->password(),
|
||||
true );
|
||||
QgsDebugMsg( "Connecting using uri = " + uri );
|
||||
OGRRegisterAll();
|
||||
OGRDataSourceH poDS;
|
||||
@ -133,9 +145,11 @@ void QgsNewOgrConnection::accept()
|
||||
settings.setValue( baseKey + "/host", txtHost->text() );
|
||||
settings.setValue( baseKey + "/database", txtDatabase->text() );
|
||||
settings.setValue( baseKey + "/port", txtPort->text() );
|
||||
settings.setValue( baseKey + "/username", txtUsername->text() );
|
||||
settings.setValue( baseKey + "/password", chkStorePassword->isChecked() ? txtPassword->text() : QLatin1String( "" ) );
|
||||
settings.setValue( baseKey + "/save", chkStorePassword->isChecked() ? "true" : "false" );
|
||||
settings.setValue( baseKey + "/username", mAuthSettingsDatabase->storeUsernameIsChecked() ? mAuthSettingsDatabase->username() : QLatin1String( "" ) );
|
||||
settings.setValue( baseKey + "/password", mAuthSettingsDatabase->storePasswordIsChecked() ? mAuthSettingsDatabase->password() : QLatin1String( "" ) );
|
||||
settings.setValue( baseKey + "/store_username", mAuthSettingsDatabase->storeUsernameIsChecked() ? "true" : "false" );
|
||||
settings.setValue( baseKey + "/store_password", mAuthSettingsDatabase->storePasswordIsChecked() ? "true" : "false" );
|
||||
settings.setValue( baseKey + "/configid", mAuthSettingsDatabase->configId() );
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
@ -18,12 +18,24 @@
|
||||
|
||||
#include "qgsogrhelperfunctions.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsauthmanager.h"
|
||||
#include <QRegExp>
|
||||
|
||||
QString createDatabaseURI( const QString &connectionType, const QString &host, const QString &database, QString port, const QString &user, const QString &password )
|
||||
QString createDatabaseURI( const QString &connectionType, const QString &host, const QString &database, QString port, const QString &configId, QString username, QString password, bool expandAuthConfig )
|
||||
{
|
||||
QString uri;
|
||||
|
||||
// If an auth configuration is set, override username and password
|
||||
// Note that only Basic auth (username/password) is for now supported for OGR connections
|
||||
if ( ! configId.isEmpty() )
|
||||
{
|
||||
// Blank credentials: we are using authcfg!
|
||||
username = QString();
|
||||
password = QString();
|
||||
// append authcfg is at the end, because we want to append the authcfg as last argument
|
||||
}
|
||||
|
||||
//todo:add default ports for all kind of databases
|
||||
if ( connectionType == QLatin1String( "ESRI Personal GeoDatabase" ) )
|
||||
{
|
||||
@ -34,7 +46,7 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
if ( port.isEmpty() )
|
||||
port = QStringLiteral( "5151" );
|
||||
|
||||
uri = "SDE:" + host + ",PORT:" + port + ',' + database + ',' + user + ',' + password;
|
||||
uri = "SDE:" + host + ",PORT:" + port + ',' + database + ',' + username + ',' + password;
|
||||
}
|
||||
else if ( connectionType == QLatin1String( "Informix DataBlade" ) )
|
||||
{
|
||||
@ -44,9 +56,9 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
if ( !host.isEmpty() )
|
||||
uri += QStringLiteral( " server=%1" ).arg( host );
|
||||
|
||||
if ( !user.isEmpty() )
|
||||
if ( !username.isEmpty() )
|
||||
{
|
||||
uri += QStringLiteral( " user=%1" ).arg( user );
|
||||
uri += QStringLiteral( " user=%1" ).arg( username );
|
||||
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( " pass=%1" ).arg( password );
|
||||
@ -56,9 +68,9 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
{
|
||||
//not tested
|
||||
uri = "@driver=ingres,dbname=" + database;
|
||||
if ( !user.isEmpty() )
|
||||
if ( !username.isEmpty() )
|
||||
{
|
||||
uri += QStringLiteral( ",userid=%1" ).arg( user );
|
||||
uri += QStringLiteral( ",userid=%1" ).arg( username );
|
||||
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( ",password=%1" ).arg( password );
|
||||
@ -76,9 +88,9 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
uri += QStringLiteral( ",port=%1" ).arg( port );
|
||||
}
|
||||
|
||||
if ( !user.isEmpty() )
|
||||
if ( !username.isEmpty() )
|
||||
{
|
||||
uri += QStringLiteral( ",user=%1" ).arg( user );
|
||||
uri += QStringLiteral( ",user=%1" ).arg( username );
|
||||
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( ",password=%1" ).arg( password );
|
||||
@ -96,9 +108,9 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
uri += QStringLiteral( ",%1" ).arg( port );
|
||||
}
|
||||
|
||||
if ( !user.isEmpty() )
|
||||
if ( !username.isEmpty() )
|
||||
{
|
||||
uri += QStringLiteral( ";uid=%1" ).arg( user );
|
||||
uri += QStringLiteral( ";uid=%1" ).arg( username );
|
||||
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( ";pwd=%1" ).arg( password );
|
||||
@ -111,10 +123,10 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
}
|
||||
else if ( connectionType == QLatin1String( "Oracle Spatial" ) )
|
||||
{
|
||||
uri = "OCI:" + user;
|
||||
uri = "OCI:" + username;
|
||||
|
||||
if ( ( !user.isEmpty() && !password.isEmpty() ) ||
|
||||
( user.isEmpty() && password.isEmpty() ) )
|
||||
if ( ( !username.isEmpty() && !password.isEmpty() ) ||
|
||||
( username.isEmpty() && password.isEmpty() ) )
|
||||
{
|
||||
uri += '/';
|
||||
if ( !password.isEmpty() )
|
||||
@ -142,15 +154,15 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
}
|
||||
else if ( connectionType == QLatin1String( "ODBC" ) )
|
||||
{
|
||||
if ( !user.isEmpty() )
|
||||
if ( !username.isEmpty() )
|
||||
{
|
||||
if ( password.isEmpty() )
|
||||
{
|
||||
uri = "ODBC:" + user + '@' + database;
|
||||
uri = "ODBC:" + username + '@' + database;
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = "ODBC:" + user + '/' + password + '@' + database;
|
||||
uri = "ODBC:" + username + '/' + password + '@' + database;
|
||||
}
|
||||
|
||||
}
|
||||
@ -174,9 +186,9 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
uri += QStringLiteral( " port='%1'" ).arg( port );
|
||||
}
|
||||
|
||||
if ( !user.isEmpty() )
|
||||
if ( !username.isEmpty() )
|
||||
{
|
||||
uri += QStringLiteral( " user='%1'" ).arg( user );
|
||||
uri += QStringLiteral( " user='%1'" ).arg( username );
|
||||
|
||||
if ( !password.isEmpty() )
|
||||
uri += QStringLiteral( " password='%1'" ).arg( password );
|
||||
@ -184,13 +196,29 @@ QString createDatabaseURI( const QString &connectionType, const QString &host, c
|
||||
|
||||
uri += ' ';
|
||||
}
|
||||
|
||||
// Append authentication configuration to the URI
|
||||
if ( !( configId.isEmpty() ) )
|
||||
{
|
||||
if ( ! expandAuthConfig )
|
||||
{
|
||||
uri += QStringLiteral( " authcfg='%1'" ).arg( configId );
|
||||
}
|
||||
else
|
||||
{
|
||||
QStringList connectionItems;
|
||||
connectionItems << uri;
|
||||
if ( QgsApplication::authManager()->updateDataSourceUriItems( connectionItems, configId, QStringLiteral( "ogr" ) ) )
|
||||
{
|
||||
uri = connectionItems.join( QString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
QgsDebugMsg( "Connection type is=" + connectionType + " and uri=" + uri );
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
||||
QString createProtocolURI( const QString &type, const QString &url )
|
||||
QString createProtocolURI( const QString &type, const QString &url, const QString &configId, const QString &username, const QString &password, bool expandAuthConfig )
|
||||
{
|
||||
QString uri;
|
||||
if ( type == QLatin1String( "GeoJSON" ) )
|
||||
@ -206,5 +234,26 @@ QString createProtocolURI( const QString &type, const QString &url )
|
||||
uri = QStringLiteral( "DODS:%1" ).arg( url );
|
||||
}
|
||||
QgsDebugMsg( "Connection type is=" + type + " and uri=" + uri );
|
||||
// Update URI with authentication information
|
||||
if ( ! configId.isEmpty() )
|
||||
{
|
||||
if ( expandAuthConfig )
|
||||
{
|
||||
QStringList connectionItems;
|
||||
connectionItems << uri;
|
||||
if ( QgsApplication::authManager()->updateDataSourceUriItems( connectionItems, configId, QStringLiteral( "ogr" ) ) )
|
||||
{
|
||||
uri = connectionItems.join( QString() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uri += QStringLiteral( " authcfg='%1'" ).arg( configId );
|
||||
}
|
||||
}
|
||||
else if ( !( username.isEmpty() || password.isEmpty( ) ) )
|
||||
{
|
||||
uri.replace( QStringLiteral( "://" ), QStringLiteral( "://%1:%2@" ).arg( username, password ) );
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
@ -26,11 +26,11 @@
|
||||
* \brief Create database uri from connection parameters
|
||||
* \note not available in python bindings
|
||||
*/
|
||||
QString GUI_EXPORT createDatabaseURI( const QString &connectionType, const QString &host, const QString &database, QString port, const QString &user, const QString &password );
|
||||
QString GUI_EXPORT createDatabaseURI( const QString &connectionType, const QString &host, const QString &database, QString port, const QString &configId, QString username, QString password, bool expandAuthConfig = false );
|
||||
|
||||
/**
|
||||
* CreateProtocolURI
|
||||
* \brief Create protocol uri from connection parameters
|
||||
* \note not available in python bindings
|
||||
*/
|
||||
QString GUI_EXPORT createProtocolURI( const QString &type, const QString &url );
|
||||
QString GUI_EXPORT createProtocolURI( const QString &type, const QString &url, const QString &configId, const QString &username, const QString &password, bool expandAuthConfig = false );
|
||||
|
@ -422,7 +422,7 @@ bool QgsOgrFeatureIterator::readFeature( gdal::ogr_feature_unique_ptr fet, QgsFe
|
||||
|
||||
|
||||
QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider *p )
|
||||
: mDataSource( p->dataSourceUri() )
|
||||
: mDataSource( p->dataSourceUri( true ) )
|
||||
, mLayerName( p->layerName() )
|
||||
, mLayerIndex( p->layerIndex() )
|
||||
, mSubsetString( p->mSubsetString )
|
||||
|
@ -469,21 +469,33 @@ QgsOgrProvider::QgsOgrProvider( QString const &uri )
|
||||
|
||||
setNativeTypes( nativeTypes );
|
||||
|
||||
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
}
|
||||
|
||||
QgsOgrProvider::~QgsOgrProvider()
|
||||
{
|
||||
QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
// We must also make sure to flush unusef cached connections so that
|
||||
// the file can be removed (#15137)
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
|
||||
// Do that as last step for final cleanup that might be prevented by
|
||||
// still opened datasets.
|
||||
close();
|
||||
}
|
||||
|
||||
QString QgsOgrProvider::dataSourceUri( bool expandAuthConfig ) const
|
||||
{
|
||||
if ( expandAuthConfig && QgsDataProvider::dataSourceUri( ).contains( QLatin1String( "authcfg" ) ) )
|
||||
{
|
||||
return QgsOgrProviderUtils::expandAuthConfig( QgsDataProvider::dataSourceUri( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return QgsDataProvider::dataSourceUri( );
|
||||
}
|
||||
}
|
||||
|
||||
QgsAbstractFeatureSource *QgsOgrProvider::featureSource() const
|
||||
{
|
||||
return new QgsOgrFeatureSource( this );
|
||||
@ -549,9 +561,9 @@ bool QgsOgrProvider::setSubsetString( const QString &theSQL, bool updateFeatureC
|
||||
|
||||
if ( uri != dataSourceUri() )
|
||||
{
|
||||
QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
setDataSourceUri( uri );
|
||||
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
}
|
||||
|
||||
mOgrLayer->ResetReading();
|
||||
@ -901,7 +913,7 @@ OGRwkbGeometryType QgsOgrProvider::getOgrGeomType( OGRLayerH ogrLayer )
|
||||
|
||||
void QgsOgrProvider::loadFields()
|
||||
{
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
//the attribute fields need to be read again when the encoding changes
|
||||
mAttributeFields.clear();
|
||||
mDefaultValues.clear();
|
||||
@ -1482,7 +1494,7 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
|
||||
{
|
||||
// adding attributes in mapinfo requires to be able to delete the .dat file
|
||||
// so drop any cached connections.
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
}
|
||||
|
||||
bool returnvalue = true;
|
||||
@ -1825,7 +1837,7 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_
|
||||
{
|
||||
pushError( tr( "OGR error syncing to disk: %1" ).arg( CPLGetLastErrorMsg() ) );
|
||||
}
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1901,7 +1913,7 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
|
||||
commitTransaction();
|
||||
}
|
||||
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
return syncToDisc();
|
||||
}
|
||||
|
||||
@ -3294,7 +3306,7 @@ QByteArray QgsOgrProvider::quotedIdentifier( const QByteArray &field ) const
|
||||
|
||||
void QgsOgrProvider::forceReload()
|
||||
{
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
}
|
||||
|
||||
QString QgsOgrProviderUtils::connectionPoolId( const QString &dataSourceURI )
|
||||
@ -3576,7 +3588,7 @@ QString QgsOgrProviderUtils::quotedValue( const QVariant &value )
|
||||
bool QgsOgrProvider::syncToDisc()
|
||||
{
|
||||
//for shapefiles, remove spatial index files and create a new index
|
||||
QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
bool shapeIndex = false;
|
||||
if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
|
||||
{
|
||||
@ -3591,7 +3603,7 @@ bool QgsOgrProvider::syncToDisc()
|
||||
{
|
||||
shapeIndex = true;
|
||||
close();
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
QFile::remove( sbnIndexFile );
|
||||
open( OpenModeSameAsCurrent );
|
||||
if ( !mValid )
|
||||
@ -3615,7 +3627,7 @@ bool QgsOgrProvider::syncToDisc()
|
||||
}
|
||||
#endif
|
||||
|
||||
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
if ( shapeIndex )
|
||||
{
|
||||
return createSpatialIndex();
|
||||
@ -3677,7 +3689,7 @@ void QgsOgrProvider::recalculateFeatureCount()
|
||||
mOgrLayer->SetSpatialFilter( filter );
|
||||
}
|
||||
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri() ) );
|
||||
QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ) ) );
|
||||
}
|
||||
|
||||
bool QgsOgrProvider::doesStrictFeatureTypeCheck() const
|
||||
@ -4091,7 +4103,7 @@ static GDALDatasetH OpenHelper( const QString &dsName,
|
||||
option.toUtf8().constData() );
|
||||
}
|
||||
GDALDatasetH hDS = QgsOgrProviderUtils::GDALOpenWrapper(
|
||||
dsName.toUtf8().constData(), updateMode, papszOpenOptions, nullptr );
|
||||
QgsOgrProviderUtils::expandAuthConfig( dsName ).toUtf8().constData(), updateMode, papszOpenOptions, nullptr );
|
||||
CSLDestroy( papszOpenOptions );
|
||||
return hDS;
|
||||
}
|
||||
@ -4241,6 +4253,7 @@ QgsOgrLayer *QgsOgrProviderUtils::getLayer( const QString &dsName,
|
||||
QString &errCause )
|
||||
{
|
||||
QMutexLocker locker( &globalMutex );
|
||||
|
||||
for ( auto iter = mapSharedDS.begin(); iter != mapSharedDS.end(); ++iter )
|
||||
{
|
||||
if ( iter.key().dsName == dsName )
|
||||
@ -4290,6 +4303,26 @@ static QDateTime getLastModified( const QString &dsName )
|
||||
return QFileInfo( dsName ).lastModified();
|
||||
}
|
||||
|
||||
QString QgsOgrProviderUtils::expandAuthConfig( const QString &dsName )
|
||||
{
|
||||
QString uri( dsName );
|
||||
// Check for authcfg
|
||||
QRegularExpression authcfgRe( " authcfg='([^']+)'" );
|
||||
QRegularExpressionMatch match;
|
||||
if ( uri.contains( authcfgRe, &match ) )
|
||||
{
|
||||
uri = uri.replace( match.captured( 0 ), QString() );
|
||||
QString configId( match.captured( 1 ) );
|
||||
QStringList connectionItems;
|
||||
connectionItems << uri;
|
||||
if ( QgsApplication::authManager()->updateDataSourceUriItems( connectionItems, configId, QStringLiteral( "ogr" ) ) )
|
||||
{
|
||||
uri = connectionItems.first( );
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
// Must be called under the globalMutex
|
||||
bool QgsOgrProviderUtils::canUseOpenedDatasets( const QString &dsName )
|
||||
{
|
||||
|
@ -64,6 +64,18 @@ class QgsOgrProvider : public QgsVectorDataProvider
|
||||
|
||||
virtual ~QgsOgrProvider();
|
||||
|
||||
/**
|
||||
* Get the data source specification. This may be a path or database
|
||||
* connection string
|
||||
* \param expandAuthConfig Whether to expand any assigned authentication configuration
|
||||
* \returns data source specification
|
||||
* \note The default authentication configuration expansion is FALSE. This keeps credentials
|
||||
* out of layer data source URIs and project files. Expansion should be specifically done
|
||||
* only when needed within a provider
|
||||
*/
|
||||
QString dataSourceUri( bool expandAuthConfig = false ) const override;
|
||||
|
||||
|
||||
virtual QgsAbstractFeatureSource *featureSource() const override;
|
||||
|
||||
virtual QgsCoordinateReferenceSystem crs() const override;
|
||||
@ -325,6 +337,10 @@ class QgsOgrProviderUtils
|
||||
static bool canUseOpenedDatasets( const QString &dsName );
|
||||
|
||||
public:
|
||||
|
||||
//! Inject credentials into the dsName (if any)
|
||||
static QString expandAuthConfig( const QString &dsName );
|
||||
|
||||
static void setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes, bool firstAttrIsFid );
|
||||
static OGRLayerH setSubsetString( OGRLayerH layer, GDALDatasetH ds, QTextCodec *encoding, const QString &subsetString, bool &origFidAdded );
|
||||
static QByteArray quotedIdentifier( QByteArray field, const QString &driverName );
|
||||
|
@ -123,6 +123,8 @@ QgsOgrSourceSelect::QgsOgrSourceSelect( QWidget *parent, Qt::WindowFlags fl, Qgs
|
||||
if ( radioSrcProtocol->isChecked() )
|
||||
emit enableButtons( !text.isEmpty() );
|
||||
} );
|
||||
// Set filter for ogr compatible auth methods
|
||||
mAuthSettingsProtocol->setDataprovider( QStringLiteral( "ogr" ) );
|
||||
}
|
||||
|
||||
QgsOgrSourceSelect::~QgsOgrSourceSelect()
|
||||
@ -179,6 +181,7 @@ void QgsOgrSourceSelect::deleteConnection()
|
||||
settings.remove( key + "/password" );
|
||||
settings.remove( key + "/port" );
|
||||
settings.remove( key + "/save" );
|
||||
settings.remove( key + "/autchcfg" );
|
||||
settings.remove( key );
|
||||
cmbConnections->removeItem( cmbConnections->currentIndex() ); // populateConnectionList();
|
||||
setConnectionListPosition();
|
||||
@ -285,9 +288,10 @@ void QgsOgrSourceSelect::addButtonClicked()
|
||||
QString port = settings.value( baseKey + "/port" ).toString();
|
||||
QString user = settings.value( baseKey + "/username" ).toString();
|
||||
QString pass = settings.value( baseKey + "/password" ).toString();
|
||||
QString configid = settings.value( baseKey + "/configid" ).toString();
|
||||
|
||||
bool makeConnection = false;
|
||||
if ( pass.isEmpty() )
|
||||
if ( pass.isEmpty() && configid.isEmpty( ) )
|
||||
{
|
||||
if ( cmbDatabaseTypes->currentText() == QLatin1String( "MSSQL" ) )
|
||||
makeConnection = true;
|
||||
@ -299,13 +303,14 @@ void QgsOgrSourceSelect::addButtonClicked()
|
||||
&makeConnection );
|
||||
}
|
||||
|
||||
if ( makeConnection || !pass.isEmpty() )
|
||||
if ( makeConnection || !( pass.isEmpty() && configid.isEmpty( ) ) )
|
||||
{
|
||||
mDataSources << createDatabaseURI(
|
||||
cmbDatabaseTypes->currentText(),
|
||||
host,
|
||||
database,
|
||||
port,
|
||||
configid,
|
||||
user,
|
||||
pass
|
||||
);
|
||||
@ -321,7 +326,11 @@ void QgsOgrSourceSelect::addButtonClicked()
|
||||
return;
|
||||
}
|
||||
|
||||
mDataSources << createProtocolURI( cmbProtocolTypes->currentText(), protocolURI->text() );
|
||||
mDataSources << createProtocolURI( cmbProtocolTypes->currentText(),
|
||||
protocolURI->text(),
|
||||
mAuthSettingsProtocol->configId(),
|
||||
mAuthSettingsProtocol->username(),
|
||||
mAuthSettingsProtocol->password() );
|
||||
}
|
||||
else if ( radioSrcFile->isChecked() )
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>404</width>
|
||||
<height>348</height>
|
||||
<height>386</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -25,7 +25,7 @@
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<layout class="QGridLayout" name="grLayout1">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
@ -41,42 +41,77 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="GroupBox1">
|
||||
<property name="title">
|
||||
<string>Connection Information</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_1">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="8" column="0" colspan="4">
|
||||
<widget class="QPushButton" name="btnConnect">
|
||||
<property name="text">
|
||||
<string>&Test Connection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" rowspan="3" colspan="4">
|
||||
<widget class="QGroupBox" name="mAuthGroupBox">
|
||||
<property name="title">
|
||||
<string>Authentication</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QgsAuthSettingsWidget" name="mAuthSettingsDatabase" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
<string>&Type</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>cmbDatabaseTypes</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="cmbDatabaseTypes"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="TextLabel1_2">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
<string>&Name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="txtName">
|
||||
<property name="toolTip">
|
||||
<string>Name of the new connection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="TextLabel1">
|
||||
<property name="text">
|
||||
@ -87,22 +122,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="txtHost"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="TextLabel2">
|
||||
<property name="text">
|
||||
<string>Database</string>
|
||||
<string>&Database</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtDatabase</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="txtDatabase"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="TextLabel2_2">
|
||||
<property name="text">
|
||||
@ -113,87 +142,48 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="2">
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="txtName">
|
||||
<property name="toolTip">
|
||||
<string>Name of the new connection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="cmbDatabaseTypes"/>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="txtHost"/>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="txtDatabase"/>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="txtPort">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="TextLabel3">
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtUsername</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="txtUsername"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="TextLabel3_2">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>txtPassword</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="2">
|
||||
<widget class="QgsPasswordLineEdit" name="txtPassword">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="chkStorePassword">
|
||||
<property name="text">
|
||||
<string>Save Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QPushButton" name="btnConnect">
|
||||
<property name="text">
|
||||
<string>&Test Connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsPasswordLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>qgspasswordlineedit.h</header>
|
||||
<class>QgsAuthSettingsWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>auth/qgsauthsettingswidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>cmbDatabaseTypes</tabstop>
|
||||
<tabstop>txtName</tabstop>
|
||||
<tabstop>txtHost</tabstop>
|
||||
<tabstop>txtDatabase</tabstop>
|
||||
<tabstop>txtPort</tabstop>
|
||||
<tabstop>txtUsername</tabstop>
|
||||
<tabstop>txtPassword</tabstop>
|
||||
<tabstop>chkStorePassword</tabstop>
|
||||
<tabstop>btnConnect</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
@ -10,7 +10,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>450</width>
|
||||
<height>575</height>
|
||||
<height>658</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -26,15 +26,76 @@
|
||||
<iconset>
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="5" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Help</set>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="srcGroupBox_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Source type</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcFile">
|
||||
<property name="text">
|
||||
<string>F&ile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcDirectory">
|
||||
<property name="text">
|
||||
<string>&Directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcDatabase">
|
||||
<property name="text">
|
||||
<string>Da&tabase</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcProtocol">
|
||||
<property name="text">
|
||||
<string>Protoco&l</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Encoding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cmbEncodings">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>341</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="protocolGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
@ -52,19 +113,19 @@
|
||||
<string>Protocol</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="1" rowspan="2">
|
||||
<widget class="QComboBox" name="cmbProtocolTypes"/>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>URI</string>
|
||||
<string>&URI</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>protocolURI</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" rowspan="2">
|
||||
<widget class="QComboBox" name="cmbProtocolTypes"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="protocolURI"/>
|
||||
</item>
|
||||
@ -75,10 +136,41 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="mAuthGroupBox">
|
||||
<property name="title">
|
||||
<string>Authentication</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QgsAuthSettingsWidget" name="mAuthSettingsProtocol" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="fileGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -124,12 +216,12 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsFileWidget" name="mFileWidget"/>
|
||||
<widget class="QgsFileWidget" name="mFileWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="dbGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -206,75 +298,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="srcGroupBox_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Source type</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcFile">
|
||||
<property name="text">
|
||||
<string>File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcDirectory">
|
||||
<property name="text">
|
||||
<string>Directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcDatabase">
|
||||
<property name="text">
|
||||
<string>Database</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioSrcProtocol">
|
||||
<property name="text">
|
||||
<string>Protocol</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Encoding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cmbEncodings">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>341</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@ -287,6 +311,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Help</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
@ -296,6 +327,12 @@
|
||||
<extends>QWidget</extends>
|
||||
<header>qgsfilewidget.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsAuthSettingsWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>auth/qgsauthsettingswidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>radioSrcFile</tabstop>
|
||||
@ -316,38 +353,6 @@
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>QgsOgrSourceSelectDialogBase</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>451</x>
|
||||
<y>699</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>481</x>
|
||||
<y>297</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>QgsOgrSourceSelectDialogBase</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>392</x>
|
||||
<y>699</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>281</x>
|
||||
<y>339</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>radioSrcDatabase</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
|
@ -196,6 +196,7 @@ ADD_PYTHON_TEST(PyQgsSourceSelectProvider test_qgssourceselectprovider.py)
|
||||
ADD_PYTHON_TEST(PyQgsAuthManagerProxy test_authmanager_proxy.py)
|
||||
ADD_PYTHON_TEST(PyQgsAuthSettingsWidget test_authsettingswidget.py)
|
||||
ADD_PYTHON_TEST(PyQgsAuxiliaryStorage test_qgsauxiliarystorage.py)
|
||||
ADD_PYTHON_TEST(PyQgsAuthManagerOgr test_authmanager_ogr.py)
|
||||
|
||||
IF (NOT WIN32)
|
||||
ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py)
|
||||
@ -216,7 +217,8 @@ IF (ENABLE_PGTEST)
|
||||
ADD_PYTHON_TEST(PyQgsVectorLayerTools test_qgsvectorlayertools.py)
|
||||
ADD_PYTHON_TEST(PyQgsAuthManagerPKIPostgresTest test_authmanager_pki_postgres.py)
|
||||
ADD_PYTHON_TEST(PyQgsAuthManagerPasswordPostgresTest test_authmanager_password_postgres.py)
|
||||
ENDIF (ENABLE_PGTEST)
|
||||
ADD_PYTHON_TEST(PyQgsAuthManagerOgrPostgresTest test_authmanager_ogr_postgres.py)
|
||||
ENDIF (ENABLE_PGTEST)
|
||||
|
||||
IF (ENABLE_MSSQLTEST)
|
||||
ADD_PYTHON_TEST(PyQgsMssqlProvider test_provider_mssql.py)
|
||||
|
@ -90,6 +90,16 @@ if os.environ.get('QGIS_SERVER_HTTP_BASIC_AUTH') is not None:
|
||||
|
||||
class HTTPBasicFilter(QgsServerFilter):
|
||||
|
||||
def requestReady(self):
|
||||
handler = self.serverInterface().requestHandler()
|
||||
auth = self.serverInterface().requestHandler().requestHeader('HTTP_AUTHORIZATION')
|
||||
if auth:
|
||||
username, password = base64.b64decode(auth[6:]).split(b':')
|
||||
if (username.decode('utf-8') == os.environ.get('QGIS_SERVER_USERNAME', 'username') and
|
||||
password.decode('utf-8') == os.environ.get('QGIS_SERVER_PASSWORD', 'password')):
|
||||
return
|
||||
handler.setParameter('SERVICE', 'ACCESS_DENIED')
|
||||
|
||||
def responseComplete(self):
|
||||
handler = self.serverInterface().requestHandler()
|
||||
auth = self.serverInterface().requestHandler().requestHeader('HTTP_AUTHORIZATION')
|
||||
|
112
tests/src/python/test_authmanager_ogr.py
Normal file
112
tests/src/python/test_authmanager_ogr.py
Normal file
@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for auth manager Basic Auth OGR connection credentials injection
|
||||
|
||||
From build dir, run: ctest -R PyQgsAuthManagerOgr -V
|
||||
|
||||
.. note:: 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
|
||||
|
||||
from qgis.core import (
|
||||
QgsApplication,
|
||||
QgsAuthManager,
|
||||
QgsAuthMethodConfig,
|
||||
QgsDataSourceUri,
|
||||
QgsProviderRegistry,
|
||||
)
|
||||
|
||||
from qgis.testing import (
|
||||
start_app,
|
||||
unittest,
|
||||
)
|
||||
|
||||
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '14/11/2017'
|
||||
__copyright__ = 'Copyright 2017, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
qgis_app = start_app()
|
||||
|
||||
# Note: value is checked with "in" because some drivers may need additional arguments,
|
||||
# like temporary paths with rootcerts for PG
|
||||
TEST_URIS = {
|
||||
"http://mysite.com/geojson authcfg='%s'": "http://username:password@mysite.com/geojson",
|
||||
"PG:\"dbname='databasename' host='addr' port='5432' authcfg='%s'\"": "PG:\"dbname='databasename' host='addr' port='5432' user='username' password='password'",
|
||||
'SDE:127.0.0.1,12345,dbname, authcfg=\'%s\'': 'SDE:127.0.0.1,12345,dbname,username,password',
|
||||
'IDB:"server=demo_on user=informix dbname=frames authcfg=\'%s\'"': 'IDB:"server=demo_on user=informix dbname=frames user=username pass=password"',
|
||||
'@driver=ingres,dbname=test,tables=usa/canada authcfg=\'%s\'': '@driver=ingres,dbname=test,tables=usa/canada,userid=username,password=password',
|
||||
'MySQL:westholland,port=3306,tables=bedrijven authcfg=\'%s\'': 'MySQL:westholland,port=3306,tables=bedrijven,user=username,password=password',
|
||||
'MSSQL:server=.\MSSQLSERVER2008;database=dbname;trusted_connection=yes authcfg=\'%s\'': 'MSSQL:server=.\MSSQLSERVER2008;database=dbname;uid=username;pwd=password',
|
||||
'OCI:/@database_instance:table,table authcfg=\'%s\'': 'OCI:username/password@database_instance:table,table',
|
||||
'ODBC:database_instance authcfg=\'%s\'': 'ODBC:username/password@database_instance',
|
||||
'couchdb://myconnection authcfg=\'%s\'': 'couchdb://username:password@myconnection',
|
||||
'http://www.myconnection.com/geojson authcfg=\'%s\'': 'http://username:password@www.myconnection.com/geojson',
|
||||
'https://www.myconnection.com/geojson authcfg=\'%s\'': 'https://username:password@www.myconnection.com/geojson',
|
||||
'ftp://www.myconnection.com/geojson authcfg=\'%s\'': 'ftp://username:password@www.myconnection.com/geojson',
|
||||
'DODS://www.myconnection.com/geojson authcfg=\'%s\'': 'DODS://username:password@www.myconnection.com/geojson',
|
||||
}
|
||||
|
||||
|
||||
class TestAuthManager(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpAuth(cls):
|
||||
"""Run before all tests and set up authentication"""
|
||||
authm = QgsApplication.authManager()
|
||||
assert (authm.setMasterPassword('masterpassword', True))
|
||||
# Client side
|
||||
cls.auth_config = QgsAuthMethodConfig("Basic")
|
||||
cls.auth_config.setConfig('username', cls.username)
|
||||
cls.auth_config.setConfig('password', cls.password)
|
||||
cls.auth_config.setName('test_basic_auth_config')
|
||||
assert (authm.storeAuthenticationConfig(cls.auth_config)[0])
|
||||
assert cls.auth_config.isValid()
|
||||
cls.authcfg = cls.auth_config.id()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run before all tests:
|
||||
Creates an auth configuration"""
|
||||
cls.username = 'username'
|
||||
cls.password = 'password'
|
||||
cls.dbname = 'test_basic'
|
||||
cls.hostname = 'localhost'
|
||||
cls.setUpAuth()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Run after all tests"""
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
"""Run before each test."""
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
"""Run after each test."""
|
||||
pass
|
||||
|
||||
def testConnections(self):
|
||||
"""
|
||||
Test credentials injection
|
||||
"""
|
||||
pr = QgsProviderRegistry.instance().createProvider('ogr', '')
|
||||
for uri, expanded in TEST_URIS.items():
|
||||
pr.setDataSourceUri(uri % self.authcfg)
|
||||
self.assertTrue(expanded in pr.dataSourceUri(True), "%s != %s" % (expanded, pr.dataSourceUri(True)))
|
||||
|
||||
# Test sublayers
|
||||
for uri, expanded in TEST_URIS.items():
|
||||
pr.setDataSourceUri((uri + '|sublayer1') % self.authcfg)
|
||||
self.assertEqual(pr.dataSourceUri(True).split('|')[1], "sublayer1", pr.dataSourceUri(True))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
246
tests/src/python/test_authmanager_ogr_postgres.py
Normal file
246
tests/src/python/test_authmanager_ogr_postgres.py
Normal file
@ -0,0 +1,246 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for auth manager Basic Auth access to postgres.
|
||||
|
||||
This is an integration test for QGIS Desktop Auth Manager postgres provider that
|
||||
checks if QGIS can use a stored auth manager auth configuration to access
|
||||
a username/password protected postgres.
|
||||
|
||||
Configuration from the environment:
|
||||
|
||||
* QGIS_POSTGRES_SERVER_PORT (default: 55432)
|
||||
* QGIS_POSTGRES_EXECUTABLE_PATH (default: /usr/lib/postgresql/9.4/bin)
|
||||
|
||||
|
||||
From build dir, run: ctest -R PyQgsAuthManagerOgrPostgresTest -V
|
||||
|
||||
or, if your PostgreSQL path differs from the default:
|
||||
|
||||
QGIS_POSTGRES_EXECUTABLE_PATH=/usr/lib/postgresql/<your_version_goes_here>/bin \
|
||||
ctest -R PyQgsAuthManagerOgrPostgresTest -V
|
||||
|
||||
.. note:: 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import signal
|
||||
import stat
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from shutil import rmtree
|
||||
|
||||
from utilities import unitTestDataPath
|
||||
from qgis.core import (
|
||||
QgsApplication,
|
||||
QgsAuthManager,
|
||||
QgsAuthMethodConfig,
|
||||
QgsVectorLayer,
|
||||
QgsDataSourceUri,
|
||||
QgsWkbTypes,
|
||||
)
|
||||
|
||||
from qgis.PyQt.QtNetwork import QSslCertificate
|
||||
|
||||
from qgis.testing import (
|
||||
start_app,
|
||||
unittest,
|
||||
)
|
||||
|
||||
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '03/11/2017'
|
||||
__copyright__ = 'Copyright 2017, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
QGIS_POSTGRES_SERVER_PORT = os.environ.get('QGIS_POSTGRES_SERVER_PORT', '55432')
|
||||
QGIS_POSTGRES_EXECUTABLE_PATH = os.environ.get('QGIS_POSTGRES_EXECUTABLE_PATH', '/usr/lib/postgresql/9.4/bin')
|
||||
|
||||
assert os.path.exists(QGIS_POSTGRES_EXECUTABLE_PATH)
|
||||
|
||||
QGIS_AUTH_DB_DIR_PATH = tempfile.mkdtemp()
|
||||
|
||||
# Postgres test path
|
||||
QGIS_PG_TEST_PATH = tempfile.mkdtemp()
|
||||
|
||||
os.environ['QGIS_AUTH_DB_DIR_PATH'] = QGIS_AUTH_DB_DIR_PATH
|
||||
|
||||
qgis_app = start_app()
|
||||
|
||||
QGIS_POSTGRES_CONF_TEMPLATE = """
|
||||
hba_file = '%(tempfolder)s/pg_hba.conf'
|
||||
listen_addresses = '*'
|
||||
port = %(port)s
|
||||
max_connections = 100
|
||||
unix_socket_directories = '%(tempfolder)s'
|
||||
ssl = true
|
||||
ssl_ciphers = 'DEFAULT:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers
|
||||
ssl_cert_file = '%(server_cert)s'
|
||||
ssl_key_file = '%(server_key)s'
|
||||
ssl_ca_file = '%(sslrootcert_path)s'
|
||||
password_encryption = on
|
||||
"""
|
||||
|
||||
QGIS_POSTGRES_HBA_TEMPLATE = """
|
||||
hostssl all all 0.0.0.0/0 md5
|
||||
hostssl all all ::1/0 md5
|
||||
host all all 127.0.0.1/32 trust
|
||||
host all all ::1/32 trust
|
||||
|
||||
host all all 0.0.0.0/0 trust
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class TestAuthManager(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpAuth(cls):
|
||||
"""Run before all tests and set up authentication"""
|
||||
authm = QgsApplication.authManager()
|
||||
assert (authm.setMasterPassword('masterpassword', True))
|
||||
cls.pg_conf = os.path.join(cls.tempfolder, 'postgresql.conf')
|
||||
cls.pg_hba = os.path.join(cls.tempfolder, 'pg_hba.conf')
|
||||
# Client side
|
||||
cls.sslrootcert_path = os.path.join(cls.certsdata_path, 'chains_subissuer-issuer-root_issuer2-root2.pem')
|
||||
assert os.path.isfile(cls.sslrootcert_path)
|
||||
os.chmod(cls.sslrootcert_path, stat.S_IRUSR)
|
||||
cls.auth_config = QgsAuthMethodConfig("Basic")
|
||||
cls.auth_config.setConfig('username', cls.username)
|
||||
cls.auth_config.setConfig('password', cls.password)
|
||||
cls.auth_config.setName('test_basic_auth_config')
|
||||
cls.sslrootcert = QSslCertificate.fromPath(cls.sslrootcert_path)
|
||||
assert cls.sslrootcert is not None
|
||||
authm.storeCertAuthorities(cls.sslrootcert)
|
||||
authm.rebuildCaCertsCache()
|
||||
authm.rebuildTrustedCaCertsCache()
|
||||
authm.rebuildCertTrustCache()
|
||||
assert (authm.storeAuthenticationConfig(cls.auth_config)[0])
|
||||
assert cls.auth_config.isValid()
|
||||
cls.authcfg = cls.auth_config.id()
|
||||
|
||||
# Server side
|
||||
cls.server_cert = os.path.join(cls.certsdata_path, 'localhost_ssl_cert.pem')
|
||||
cls.server_key = os.path.join(cls.certsdata_path, 'localhost_ssl_key.pem')
|
||||
cls.server_rootcert = cls.sslrootcert_path
|
||||
os.chmod(cls.server_cert, stat.S_IRUSR)
|
||||
os.chmod(cls.server_key, stat.S_IRUSR)
|
||||
os.chmod(cls.server_rootcert, stat.S_IRUSR)
|
||||
|
||||
# Place conf in the data folder
|
||||
with open(cls.pg_conf, 'w+') as f:
|
||||
f.write(QGIS_POSTGRES_CONF_TEMPLATE % {
|
||||
'port': cls.port,
|
||||
'tempfolder': cls.tempfolder,
|
||||
'server_cert': cls.server_cert,
|
||||
'server_key': cls.server_key,
|
||||
'sslrootcert_path': cls.sslrootcert_path,
|
||||
})
|
||||
|
||||
with open(cls.pg_hba, 'w+') as f:
|
||||
f.write(QGIS_POSTGRES_HBA_TEMPLATE)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run before all tests:
|
||||
Creates an auth configuration"""
|
||||
cls.port = QGIS_POSTGRES_SERVER_PORT
|
||||
cls.username = 'username'
|
||||
cls.password = 'password'
|
||||
cls.dbname = 'test_basic'
|
||||
cls.tempfolder = QGIS_PG_TEST_PATH
|
||||
cls.certsdata_path = os.path.join(unitTestDataPath('auth_system'), 'certs_keys')
|
||||
cls.hostname = 'localhost'
|
||||
cls.data_path = os.path.join(cls.tempfolder, 'data')
|
||||
os.mkdir(cls.data_path)
|
||||
|
||||
cls.setUpAuth()
|
||||
subprocess.check_call([os.path.join(QGIS_POSTGRES_EXECUTABLE_PATH, 'initdb'), '-D', cls.data_path])
|
||||
|
||||
# Disable SSL verification for setup operations
|
||||
env = dict(os.environ)
|
||||
env['PGSSLMODE'] = 'disable'
|
||||
|
||||
cls.server = subprocess.Popen([os.path.join(QGIS_POSTGRES_EXECUTABLE_PATH, 'postgres'), '-D',
|
||||
cls.data_path, '-c',
|
||||
"config_file=%s" % cls.pg_conf],
|
||||
env=env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
# Wait max 10 secs for the server to start
|
||||
end = time.time() + 10
|
||||
while True:
|
||||
line = cls.server.stderr.readline()
|
||||
print(line)
|
||||
if line.find(b"database system is ready to accept") != -1:
|
||||
break
|
||||
if time.time() > end:
|
||||
raise Exception("Timeout connecting to PostgreSQL")
|
||||
# Create a DB
|
||||
subprocess.check_call([os.path.join(QGIS_POSTGRES_EXECUTABLE_PATH, 'createdb'), '-h', 'localhost', '-p', cls.port, 'test_basic'], env=env)
|
||||
# Inject test SQL from test path
|
||||
test_sql = os.path.join(unitTestDataPath('provider'), 'testdata_pg.sql')
|
||||
subprocess.check_call([os.path.join(QGIS_POSTGRES_EXECUTABLE_PATH, 'psql'), '-h', 'localhost', '-p', cls.port, '-f', test_sql, cls.dbname], env=env)
|
||||
# Create a role
|
||||
subprocess.check_call([os.path.join(QGIS_POSTGRES_EXECUTABLE_PATH, 'psql'), '-h', 'localhost', '-p', cls.port, '-c', 'CREATE ROLE "%s" WITH SUPERUSER LOGIN PASSWORD \'%s\'' % (cls.username, cls.password), cls.dbname], env=env)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Run after all tests"""
|
||||
cls.server.terminate()
|
||||
os.kill(cls.server.pid, signal.SIGABRT)
|
||||
del cls.server
|
||||
time.sleep(2)
|
||||
rmtree(QGIS_AUTH_DB_DIR_PATH)
|
||||
rmtree(cls.tempfolder)
|
||||
|
||||
def setUp(self):
|
||||
"""Run before each test."""
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
"""Run after each test."""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _getPostGISLayer(cls, type_name, layer_name=None, authcfg=''):
|
||||
"""
|
||||
PG layer factory
|
||||
"""
|
||||
if layer_name is None:
|
||||
layer_name = 'pg_' + type_name
|
||||
|
||||
# Warning: OGR needs the schema if it's not the default, so qgis_test.someData
|
||||
connstring = "PG:dbname='%(dbname)s' host='%(hostname)s' port='%(port)s' sslmode='verify-full' sslrootcert='%(sslrootcert)s'%(authcfg)s|layername=qgis_test.someData" % (
|
||||
{
|
||||
'dbname': cls.dbname,
|
||||
'hostname': cls.hostname,
|
||||
'port': cls.port,
|
||||
'authcfg': ' authcfg=\'%s\'' % authcfg if authcfg else '',
|
||||
'sslrootcert': cls.sslrootcert_path,
|
||||
}
|
||||
)
|
||||
layer = QgsVectorLayer(connstring, layer_name, 'ogr')
|
||||
return layer
|
||||
|
||||
def testValidAuthAccess(self):
|
||||
"""
|
||||
Access the protected layer with valid credentials
|
||||
"""
|
||||
pg_layer = self._getPostGISLayer('testlayer_èé', authcfg=self.auth_config.id())
|
||||
self.assertTrue(pg_layer.isValid())
|
||||
|
||||
def testInvalidAuthAccess(self):
|
||||
"""
|
||||
Access the protected layer with not valid credentials
|
||||
"""
|
||||
pg_layer = self._getPostGISLayer('testlayer_èé')
|
||||
self.assertFalse(pg_layer.isValid())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -170,6 +170,19 @@ class TestAuthManager(unittest.TestCase):
|
||||
wms_layer = QgsRasterLayer(uri, layer_name, 'wms')
|
||||
return wms_layer
|
||||
|
||||
@classmethod
|
||||
def _getGeoJsonLayer(cls, type_name, layer_name=None, authcfg=None):
|
||||
"""
|
||||
OGR layer factory
|
||||
"""
|
||||
if layer_name is None:
|
||||
layer_name = 'geojson_' + type_name
|
||||
uri = '%s://%s:%s/?MAP=%s&SERVICE=WFS&REQUEST=GetFeature&TYPENAME=%s&VERSION=2.0.0&OUTPUTFORMAT=geojson' % (cls.protocol, cls.hostname, cls.port, cls.project_path, urllib.parse.quote(type_name))
|
||||
if authcfg is not None:
|
||||
uri += " authcfg='%s'" % authcfg
|
||||
geojson_layer = QgsVectorLayer(uri, layer_name, 'ogr')
|
||||
return geojson_layer
|
||||
|
||||
def testValidAuthAccess(self):
|
||||
"""
|
||||
Access the HTTP Basic protected layer with valid credentials
|
||||
@ -178,6 +191,8 @@ class TestAuthManager(unittest.TestCase):
|
||||
self.assertTrue(wfs_layer.isValid())
|
||||
wms_layer = self._getWMSLayer('testlayer_èé', authcfg=self.auth_config.id())
|
||||
self.assertTrue(wms_layer.isValid())
|
||||
geojson_layer = self._getGeoJsonLayer('testlayer_èé', authcfg=self.auth_config.id())
|
||||
self.assertTrue(geojson_layer.isValid())
|
||||
|
||||
def testInvalidAuthAccess(self):
|
||||
"""
|
||||
@ -187,6 +202,8 @@ class TestAuthManager(unittest.TestCase):
|
||||
self.assertFalse(wfs_layer.isValid())
|
||||
wms_layer = self._getWMSLayer('testlayer_èé')
|
||||
self.assertFalse(wms_layer.isValid())
|
||||
geojson_layer = self._getGeoJsonLayer('testlayer_èé')
|
||||
self.assertFalse(geojson_layer.isValid())
|
||||
|
||||
def testInvalidAuthFileDownload(self):
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user