Skip to content
Permalink
Browse files

[auth][feature][ogr] Hide credentials from URI, use authcfg

  • Loading branch information
elpaso committed Nov 2, 2017
1 parent 83ec9de commit 58fdd70a5404235b36d22ad83a7c0ebd5d2e8e47
@@ -102,66 +102,133 @@ bool QgsAuthBasicMethod::updateDataSourceUriItems( QStringList &connectionItems,
return false;
}

// Branch for OGR protocol:
// Branch for OGR
if ( dataprovider == QStringLiteral( "ogr" ) )
{
if ( ! password.isEmpty() )
{
// inject username and password into the URL
connectionItems.replaceInStrings( QStringLiteral( "://" ), QStringLiteral( "://%1:%2@" ).arg( username, password ) );
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:" ) ) )
{
if ( !username.isEmpty() )
{
uri += QStringLiteral( " user='%1'" ).arg( username );

if ( !password.isEmpty() )
uri += QStringLiteral( " password='%1'" ).arg( password );
}
}
else if ( uri.startsWith( QStringLiteral( "SDE:" ) ) )
{
uri = uri.replace( QRegExp( ",$" ), QStringLiteral( ",%1,%2" ).arg( username, password ) );
}
else if ( uri.startsWith( QStringLiteral( "IDB" ) ) )
{
uri += QStringLiteral( " user=%1" ).arg( username );
if ( !password.isEmpty() )
uri += QStringLiteral( " pass=%1" ).arg( password );
}
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( ",userid=%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( "MSSQL:" ) ) )
{
uri += QStringLiteral( ";uid=%1" ).arg( username );
if ( !password.isEmpty() )
uri += QStringLiteral( ";pwd=%1" ).arg( 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.indexOf( '|' ) );
}
connectionItems.replace( 0, uri );
}
else
{
QgsDebugMsg( QString( "Update URI items FAILED for authcfg: %1: password empty" ).arg( authcfg ) );
}
}

// OGR database might use the standard URI way, we need to process this part in all cases
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
else // Not-ogr
{
connectionItems.append( passparam );
}
QString userparam = "user='" + escapeUserPass( username ) + '\'';
int userindx = connectionItems.indexOf( QRegExp( "^user='.*" ) );
if ( userindx != -1 )
{
connectionItems.replace( userindx, userparam );
}
else
{
connectionItems.append( userparam );
}

// add extra CAs
QList<QSslCertificate> cas;
cas = QgsApplication::authManager()->trustedCaCerts();
// save CAs to temp file
QString tempFileBase = QStringLiteral( "tmp_basic_%1.pem" );
QString caFilePath = QgsAuthCertUtils::pemTextToTempFile(
tempFileBase.arg( QUuid::createUuid().toString() ),
QgsAuthCertUtils::certsToPemText( cas ) );
if ( ! caFilePath.isEmpty() )
{
QString caparam = "sslrootcert='" + caFilePath + "'";
int sslcaindx = connectionItems.indexOf( QRegExp( "^sslrootcert='.*" ) );
if ( sslcaindx != -1 )
QString passparam = "password='" + escapeUserPass( password ) + '\'';
int passindx = connectionItems.indexOf( QRegExp( "^password='.*" ) );
if ( passindx != -1 )
{
connectionItems.replace( sslcaindx, caparam );
connectionItems.replace( passindx, passparam );
}
else
{
connectionItems.append( caparam );
connectionItems.append( passparam );
}
}

return true;
return true;
}

bool QgsAuthBasicMethod::updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider )
@@ -93,7 +93,8 @@ void QgsNewOgrConnection::testConnection()
txtPort->text(),
mAuthSettingsDatabase->configId(),
mAuthSettingsDatabase->username(),
mAuthSettingsDatabase->password() );
mAuthSettingsDatabase->password(),
true );
QgsDebugMsg( "Connecting using uri = " + uri );
OGRRegisterAll();
OGRDataSourceH poDS;
@@ -22,25 +22,18 @@
#include "qgsauthmanager.h"
#include <QRegExp>

QString createDatabaseURI( const QString &connectionType, const QString &host, const QString &database, QString port, const QString &configId, QString username, 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() )
{
// Pass to updateDataSourceUriItems empty user/password in the format it likes
QStringList connectionItems;
connectionItems << QStringLiteral( "user=''" ) << QStringLiteral( "password=''" );
if ( QgsApplication::authManager()->updateDataSourceUriItems( connectionItems, configId, QStringLiteral( "ogr" ) ) )
{
QRegExp userRe( "^user='([^']+)'" );
QRegExp passRe( "^password='([^']+)'" );
// Extracts the username and password
username = QString( connectionItems.at( 0 ) ).replace( userRe, "\\1" );
password = QString( connectionItems.at( 1 ) ).replace( passRe, "\\1" );
}
// 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
@@ -203,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, const QString &configId, const QString &username, const QString &password )
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" ) )
@@ -228,11 +237,18 @@ QString createProtocolURI( const QString &type, const QString &url, const QStri
// Update URI with authentication information
if ( ! configId.isEmpty() )
{
QStringList connectionItems;
connectionItems << uri;
if ( QgsApplication::authManager()->updateDataSourceUriItems( connectionItems, configId, QStringLiteral( "ogr" ) ) )
if ( expandAuthConfig )
{
QStringList connectionItems;
connectionItems << uri;
if ( QgsApplication::authManager()->updateDataSourceUriItems( connectionItems, configId, QStringLiteral( "ogr" ) ) )
{
uri = connectionItems.join( QString() );
}
}
else
{
uri = connectionItems.join( QString() );
uri += QStringLiteral( " authcfg='%1'" ).arg( configId );
}
}
else if ( !( username.isEmpty() || password.isEmpty( ) ) )
@@ -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 &configId, QString username, 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, const QString &configId, const QString &username, const QString &password );
QString GUI_EXPORT createProtocolURI( const QString &type, const QString &url, const QString &configId, const QString &username, const QString &password, bool expandAuthConfig = false );
@@ -48,7 +48,7 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool
, mFilterFidsIt( mFilterFids.constBegin() )
{
//QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection");
mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource ) );
mConn = QgsOgrConnPool::instance()->acquireConnection( mSource->mDataSource );
if ( !mConn->ds )
{
return;
@@ -422,7 +422,7 @@ bool QgsOgrFeatureIterator::readFeature( gdal::ogr_feature_unique_ptr fet, QgsFe


QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider *p )
: mDataSource( p->dataSourceUri() )
: mDataSource( QgsOgrProviderUtils::expandAuthConfig( p->dataSourceUri() ) )
, mLayerName( p->layerName() )
, mLayerIndex( p->layerIndex() )
, mSubsetString( p->mSubsetString )
@@ -3753,6 +3753,9 @@ void QgsOgrProvider::open( OpenMode mode )
Q_ASSERT( !mOgrLayer );
Q_ASSERT( !mOgrOrigLayer );

// Expand authentication
setDataSourceUri( QgsOgrProviderUtils::expandAuthConfig( mFilePath ) );

// Try to open using VSIFileHandler
// see http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
QString vsiPrefix = QgsZipItem::vsiPrefix( dataSourceUri() );
@@ -4054,7 +4057,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;
}
@@ -4204,6 +4207,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 )
@@ -4253,6 +4257,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 )
{
@@ -325,6 +325,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 );
@@ -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>
@@ -95,7 +95,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Type</string>
<string>&amp;Type</string>
</property>
<property name="buddy">
<cstring>cmbDatabaseTypes</cstring>
@@ -125,7 +125,7 @@
<item row="3" column="0">
<widget class="QLabel" name="TextLabel2">
<property name="text">
<string>Database</string>
<string>&amp;Database</string>
</property>
<property name="buddy">
<cstring>txtDatabase</cstring>

0 comments on commit 58fdd70

Please sign in to comment.
You can’t perform that action at this time.