Merge pull request #2674 from boundlessgeo/postgis-pki-pr

[FEATURE] Postgres provider PKI authentication (plus fixes #13841)
This commit is contained in:
Larry Shaffer 2016-01-14 23:43:52 -07:00
commit 5f3ca88a84
26 changed files with 479 additions and 80 deletions

View File

@ -82,6 +82,7 @@ class QgsAuthCertUtils
const QString &bundlepass = QString(),
bool reencrypt = true );
static QString pemTextToTempFile( const QString &name, const QByteArray &pemtext );
static QString getCaSourceName( QgsAuthCertUtils::CaCertSource source , bool single = false );

View File

@ -38,10 +38,10 @@ class DBConnector:
self.connection = None
def uri(self):
return QgsDataSourceURI(self._uri.uri())
return QgsDataSourceURI(self._uri.uri(False))
def publicUri(self):
publicUri = QgsDataSourceURI.removePassword(self._uri.uri())
publicUri = QgsDataSourceURI.removePassword(self._uri.uri(False))
return QgsDataSourceURI(publicUri)
def hasSpatialSupport(self):

View File

@ -125,7 +125,7 @@ class OracleDBConnector(DBConnector):
self._checkGeometryColumnsTable()
def _connectionInfo(self):
return unicode(self._uri.connectionInfo())
return unicode(self._uri.connectionInfo(True))
def _checkSpatial(self):
"""Check whether Oracle Spatial is present in catalog."""

View File

@ -129,7 +129,7 @@ class OracleDBPlugin(DBPlugin):
max_attempts = 3
for i in range(max_attempts):
(ok, username, password) = QgsCredentials.instance().get(
uri.connectionInfo(), username, password, err)
uri.connectionInfo(False), username, password, err)
if not ok:
return False
@ -145,7 +145,7 @@ class OracleDBPlugin(DBPlugin):
continue
QgsCredentials.instance().put(
uri.connectionInfo(), username, password)
uri.connectionInfo(False), username, password)
return True
@ -207,7 +207,7 @@ class ORDatabase(Database):
if avoidSelectById:
uri.disableSelectAtId(True)
provider = self.dbplugin().providerName()
vlayer = QgsVectorLayer(uri.uri(), layerName, provider)
vlayer = QgsVectorLayer(uri.uri(False), layerName, provider)
# handling undetermined geometry type
if not vlayer.isValid():
@ -217,7 +217,7 @@ class ORDatabase(Database):
uri.setWkbType(wkbType)
if srid:
uri.setSrid(unicode(srid))
vlayer = QgsVectorLayer(uri.uri(), layerName, provider)
vlayer = QgsVectorLayer(uri.uri(False), layerName, provider)
return vlayer

View File

@ -279,8 +279,8 @@ class Database(DbItemObject):
uri.disableSelectAtId(True)
provider = self.dbplugin().providerName()
if layerType == QgsMapLayer.RasterLayer:
return QgsRasterLayer(uri.uri(), layerName, provider)
return QgsVectorLayer(uri.uri(), layerName, provider)
return QgsRasterLayer(uri.uri(False), layerName, provider)
return QgsVectorLayer(uri.uri(False), layerName, provider)
def registerAllActions(self, mainWindow):
self.registerDatabaseActions(mainWindow)
@ -668,13 +668,13 @@ class Table(DbItemObject):
def mimeUri(self):
layerType = "raster" if self.type == Table.RasterType else "vector"
return u"%s:%s:%s:%s" % (layerType, self.database().dbplugin().providerName(), self.name, self.uri().uri())
return u"%s:%s:%s:%s" % (layerType, self.database().dbplugin().providerName(), self.name, self.uri().uri(False))
def toMapLayer(self):
from qgis.core import QgsVectorLayer, QgsRasterLayer
provider = self.database().dbplugin().providerName()
uri = self.uri().uri()
uri = self.uri().uri(False)
if self.type == Table.RasterType:
return QgsRasterLayer(uri, self.name, provider)
return QgsVectorLayer(uri, self.name, provider)

View File

@ -23,7 +23,7 @@ The content of this file is based on
"""
from PyQt4.QtCore import QRegExp
from qgis.core import QgsCredentials
from qgis.core import QgsCredentials, QgsDataSourceURI
from ..connector import DBConnector
from ..plugin import ConnectionError, DbError, Table
@ -51,12 +51,13 @@ class PostGisDBConnector(DBConnector):
username = uri.username() or os.environ.get('PGUSER') or os.environ.get('USER')
password = uri.password() or os.environ.get('PGPASSWORD')
expandedConnInfo = self._connectionInfo()
try:
self.connection = psycopg2.connect(self._connectionInfo().encode('utf-8'))
self.connection = psycopg2.connect(expandedConnInfo.encode('utf-8'))
except self.connection_error_types() as e:
err = unicode(e)
uri = self.uri()
conninfo = uri.connectionInfo()
conninfo = uri.connectionInfo(False)
for i in range(3):
(ok, username, password) = QgsCredentials.instance().get(conninfo, username, password, err)
@ -69,14 +70,51 @@ class PostGisDBConnector(DBConnector):
if password:
uri.setPassword(password)
newExpandedConnInfo = uri.connectionInfo(True)
try:
self.connection = psycopg2.connect(uri.connectionInfo().encode('utf-8'))
self.connection = psycopg2.connect(newExpandedConnInfo.encode('utf-8'))
QgsCredentials.instance().put(conninfo, username, password)
except self.connection_error_types() as e:
if i == 2:
raise ConnectionError(e)
err = unicode(e)
finally:
# remove certs (if any) of the expanded connectionInfo
expandedUri = QgsDataSourceURI(newExpandedConnInfo)
sslCertFile = expandedUri.param("sslcert")
if sslCertFile:
sslCertFile = sslCertFile.replace("'", "")
os.remove(sslCertFile)
sslKeyFile = expandedUri.param("sslkey")
if sslKeyFile:
sslKeyFile = sslKeyFile.replace("'", "")
os.remove(sslKeyFile)
sslCAFile = expandedUri.param("sslrootcert")
if sslCAFile:
sslCAFile = sslCAFile.replace("'", "")
os.remove(sslCAFile)
finally:
# remove certs (if any) of the expanded connectionInfo
expandedUri = QgsDataSourceURI(expandedConnInfo)
sslCertFile = expandedUri.param("sslcert")
if sslCertFile:
sslCertFile = sslCertFile.replace("'", "")
os.remove(sslCertFile)
sslKeyFile = expandedUri.param("sslkey")
if sslKeyFile:
sslKeyFile = sslKeyFile.replace("'", "")
os.remove(sslKeyFile)
sslCAFile = expandedUri.param("sslrootcert")
if sslCAFile:
sslCAFile = sslCAFile.replace("'", "")
os.remove(sslCAFile)
self.connection.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
@ -90,7 +128,7 @@ class PostGisDBConnector(DBConnector):
self._checkRasterColumnsTable()
def _connectionInfo(self):
return unicode(self.uri().connectionInfo())
return unicode(self.uri().connectionInfo(True))
def _checkSpatial(self):
""" check whether postgis_version is present in catalog """

View File

@ -79,8 +79,8 @@ class PostGisDBPlugin(DBPlugin):
uri = QgsDataSourceURI()
settingsList = ["service", "host", "port", "database", "username", "password"]
service, host, port, database, username, password = map(lambda x: settings.value(x, "", type=str), settingsList)
settingsList = ["service", "host", "port", "database", "username", "password", "authcfg"]
service, host, port, database, username, password, authcfg = map(lambda x: settings.value(x, "", type=str), settingsList)
useEstimatedMetadata = settings.value("estimatedMetadata", False, type=bool)
sslmode = settings.value("sslmode", QgsDataSourceURI.SSLprefer, type=int)
@ -88,9 +88,9 @@ class PostGisDBPlugin(DBPlugin):
settings.endGroup()
if service:
uri.setConnection(service, database, username, password, sslmode)
uri.setConnection(service, database, username, password, sslmode, authcfg)
else:
uri.setConnection(host, port, database, username, password, sslmode)
uri.setConnection(host, port, database, username, password, sslmode, authcfg)
uri.setUseEstimatedMetadata(useEstimatedMetadata)
@ -292,7 +292,7 @@ class PGRasterTable(PGTable, RasterTable):
if not rl.isValid():
err = rl.error().summary()
uri = QgsDataSourceURI(self.database().uri())
conninfo = uri.connectionInfo()
conninfo = uri.connectionInfo(False)
username = uri.username()
password = uri.password()

View File

@ -113,7 +113,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'face', 'mbr', '', 'face_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBPolygon)
layer = QgsVectorLayer(uri.uri(), u'%s.face_mbr' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.face_mbr' % toponame, provider)
layer.loadNamedStyle(os.path.join(template_dir, 'face_mbr.qml'))
registry.addMapLayers([layer])
legend.moveLayer(layer, group)
@ -127,7 +127,7 @@ def run(item, action, mainwindow):
uri.setDataSource('', u'(%s\n)' % sql, 'geom', '', 'face_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBPolygon)
layer = QgsVectorLayer(uri.uri(), u'%s.face' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.face' % toponame, provider)
layer.setExtent(face_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'face.qml'))
registry.addMapLayers([layer])
@ -141,7 +141,7 @@ def run(item, action, mainwindow):
uri.setDataSource('', u'(%s)' % sql, 'geom', '', 'face_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBPoint)
layer = QgsVectorLayer(uri.uri(), u'%s.face_seed' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.face_seed' % toponame, provider)
layer.setExtent(face_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'face_seed.qml'))
registry.addMapLayers([layer])
@ -158,7 +158,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'node', 'geom', '', 'node_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBPoint)
layer = QgsVectorLayer(uri.uri(), u'%s.node' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.node' % toponame, provider)
layer.loadNamedStyle(os.path.join(template_dir, 'node.qml'))
registry.addMapLayers([layer])
legend.moveLayer(layer, group)
@ -170,7 +170,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'node', 'geom', '', 'node_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBPoint)
layer = QgsVectorLayer(uri.uri(), u'%s.node_id' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.node_id' % toponame, provider)
layer.setExtent(node_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'node_label.qml'))
registry.addMapLayers([layer])
@ -185,7 +185,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBLineString)
layer = QgsVectorLayer(uri.uri(), u'%s.edge' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.edge' % toponame, provider)
registry.addMapLayers([layer])
legend.moveLayer(layer, group)
legend.setLayerVisible(layer, False)
@ -196,7 +196,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBLineString)
layer = QgsVectorLayer(uri.uri(), u'%s.directed_edge' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.directed_edge' % toponame, provider)
layer.setExtent(edge_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'edge.qml'))
registry.addMapLayers([layer])
@ -208,7 +208,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBLineString)
layer = QgsVectorLayer(uri.uri(), u'%s.edge_id' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.edge_id' % toponame, provider)
layer.setExtent(edge_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'edge_label.qml'))
registry.addMapLayers([layer])
@ -220,7 +220,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBLineString)
layer = QgsVectorLayer(uri.uri(), u'%s.face_left' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.face_left' % toponame, provider)
layer.setExtent(edge_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'face_left.qml'))
registry.addMapLayers([layer])
@ -232,7 +232,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBLineString)
layer = QgsVectorLayer(uri.uri(), u'%s.face_right' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.face_right' % toponame, provider)
layer.setExtent(edge_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'face_right.qml'))
registry.addMapLayers([layer])
@ -244,7 +244,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBLineString)
layer = QgsVectorLayer(uri.uri(), u'%s.next_left' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.next_left' % toponame, provider)
layer.setExtent(edge_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'next_left.qml'))
registry.addMapLayers([layer])
@ -256,7 +256,7 @@ def run(item, action, mainwindow):
uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
uri.setSrid(toposrid)
uri.setWkbType(QGis.WKBLineString)
layer = QgsVectorLayer(uri.uri(), u'%s.next_right' % toponame, provider)
layer = QgsVectorLayer(uri.uri(False), u'%s.next_right' % toponame, provider)
layer.setExtent(edge_extent)
layer.loadNamedStyle(os.path.join(template_dir, 'next_right.qml'))
registry.addMapLayers([layer])

View File

@ -105,7 +105,7 @@ class LayerPreview(QgsMapCanvas):
uri.setDataSource("", u"(SELECT * FROM %s LIMIT 1000)" % table.quotedName(), table.geomColumn, "",
uniqueField.name)
provider = table.database().dbplugin().providerName()
vl = QgsVectorLayer(uri.uri(), table.name, provider)
vl = QgsVectorLayer(uri.uri(False), table.name, provider)
else:
vl = table.toMapLayer()

View File

@ -17,6 +17,9 @@
#include "qgsauthidentcertmethod.h"
#include "qgsauthidentcertedit.h"
#include <QDir>
#include <QFile>
#include <QUuid>
#ifndef QT_NO_OPENSSL
#include <QtCrypto>
#include <QSslConfiguration>
@ -27,7 +30,6 @@
#include "qgsauthmanager.h"
#include "qgslogger.h"
static const QString AUTH_METHOD_KEY = "Identity-Cert";
static const QString AUTH_METHOD_DESCRIPTION = "Identity certificate authentication";
@ -38,12 +40,13 @@ QgsAuthIdentCertMethod::QgsAuthIdentCertMethod()
: QgsAuthMethod()
{
setVersion( 2 );
setExpansions( QgsAuthMethod::NetworkRequest );
setExpansions( QgsAuthMethod::NetworkRequest | QgsAuthMethod::DataSourceURI );
setDataProviders( QStringList()
<< "ows"
<< "wfs" // convert to lowercase
<< "wcs"
<< "wms" );
<< "wms"
<< "postgres" );
}
QgsAuthIdentCertMethod::~QgsAuthIdentCertMethod()
@ -101,6 +104,101 @@ bool QgsAuthIdentCertMethod::updateNetworkRequest( QNetworkRequest &request, con
return true;
}
bool QgsAuthIdentCertMethod::updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
const QString &dataprovider )
{
Q_UNUSED( dataprovider )
QgsDebugMsg( QString( "Update URI items for authcfg: %1" ).arg( authcfg ) );
QgsPkiConfigBundle * pkibundle = getPkiConfigBundle( authcfg );
if ( !pkibundle || !pkibundle->isValid() )
{
QgsDebugMsg( "Update URI items FAILED: PKI bundle invalid" );
return false;
}
QgsDebugMsg( "Update URI items: PKI bundle valid" );
QString pkiTempFileBase = "tmppki_%1.pem";
// save client cert to temp file
QString certFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
pkibundle->clientCert().toPem() );
if ( certFilePath.isEmpty() )
{
return false;
}
// save client cert key to temp file
QString keyFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
pkibundle->clientCertKey().toPem() );
if ( keyFilePath.isEmpty() )
{
return false;
}
// save CAs to temp file
QString caFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
QgsAuthManager::instance()->getTrustedCaCertsPemText() );
if ( caFilePath.isEmpty() )
{
return false;
}
// get common name of the client certificate
QString commonName = QgsAuthCertUtils::resolvedCertName( pkibundle->clientCert(), false );
// add uri parameters
QString userparam = "user='" + commonName + "'";
int userindx = connectionItems.indexOf( QRegExp( "^user='.*" ) );
if ( userindx != -1 )
{
connectionItems.replace( userindx, userparam );
}
else
{
connectionItems.append( userparam );
}
QString certparam = "sslcert='" + certFilePath + "'";
int sslcertindx = connectionItems.indexOf( QRegExp( "^sslcert='.*" ) );
if ( sslcertindx != -1 )
{
connectionItems.replace( sslcertindx, certparam );
}
else
{
connectionItems.append( certparam );
}
QString keyparam = "sslkey='" + keyFilePath + "'";
int sslkeyindx = connectionItems.indexOf( QRegExp( "^sslkey='.*" ) );
if ( sslkeyindx != -1 )
{
connectionItems.replace( sslkeyindx, keyparam );
}
else
{
connectionItems.append( keyparam );
}
QString caparam = "sslrootcert='" + caFilePath + "'";
int sslcaindx = connectionItems.indexOf( QRegExp( "^sslrootcert='.*" ) );
if ( sslcaindx != -1 )
{
connectionItems.replace( sslcaindx, caparam );
}
else
{
connectionItems.append( caparam );
}
return true;
}
void QgsAuthIdentCertMethod::clearCachedConfig( const QString &authcfg )
{
removePkiConfigBundle( authcfg );

View File

@ -41,6 +41,9 @@ class QgsAuthIdentCertMethod : public QgsAuthMethod
bool updateNetworkRequest( QNetworkRequest &request, const QString &authcfg,
const QString &dataprovider = QString() ) override;
bool updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
const QString &dataprovider = QString() ) override;
void clearCachedConfig( const QString &authcfg ) override;
void updateMethodConfig( QgsAuthMethodConfig &mconfig ) override;

View File

@ -17,6 +17,9 @@
#include "qgsauthpkipathsmethod.h"
#include "qgsauthpkipathsedit.h"
#include <QDir>
#include <QFile>
#include <QUuid>
#ifndef QT_NO_OPENSSL
#include <QtCrypto>
#include <QSslConfiguration>
@ -38,12 +41,13 @@ QgsAuthPkiPathsMethod::QgsAuthPkiPathsMethod()
: QgsAuthMethod()
{
setVersion( 2 );
setExpansions( QgsAuthMethod::NetworkRequest );
setExpansions( QgsAuthMethod::NetworkRequest | QgsAuthMethod::DataSourceURI );
setDataProviders( QStringList()
<< "ows"
<< "wfs" // convert to lowercase
<< "wcs"
<< "wms" );
<< "wms"
<< "postgres" );
}
QgsAuthPkiPathsMethod::~QgsAuthPkiPathsMethod()
@ -101,6 +105,102 @@ bool QgsAuthPkiPathsMethod::updateNetworkRequest( QNetworkRequest &request, cons
return true;
}
bool QgsAuthPkiPathsMethod::updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
const QString &dataprovider )
{
Q_UNUSED( dataprovider )
QgsDebugMsg( QString( "Update URI items for authcfg: %1" ).arg( authcfg ) );
QgsPkiConfigBundle * pkibundle = getPkiConfigBundle( authcfg );
if ( !pkibundle || !pkibundle->isValid() )
{
QgsDebugMsg( "Update URI items FAILED: PKI bundle invalid" );
return false;
}
QgsDebugMsg( "Update URI items: PKI bundle valid" );
QString pkiTempFileBase = "tmppki_%1.pem";
// save client cert to temp file
QString certFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
pkibundle->clientCert().toPem() );
if ( certFilePath.isEmpty() )
{
return false;
}
// save client cert key to temp file
QString keyFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
pkibundle->clientCertKey().toPem() );
if ( keyFilePath.isEmpty() )
{
return false;
}
// save CAs to temp file
QString caFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
QgsAuthManager::instance()->getTrustedCaCertsPemText() );
if ( caFilePath.isEmpty() )
{
return false;
}
// get common name of the client certificate
QString commonName = QgsAuthCertUtils::resolvedCertName( pkibundle->clientCert(), false );
// add uri parameters
QString userparam = "user='" + commonName + "'";
int userindx = connectionItems.indexOf( QRegExp( "^user='.*" ) );
if ( userindx != -1 )
{
connectionItems.replace( userindx, userparam );
}
else
{
connectionItems.append( userparam );
}
// add uri parameters
QString certparam = "sslcert='" + certFilePath + "'";
int sslcertindx = connectionItems.indexOf( QRegExp( "^sslcert='.*" ) );
if ( sslcertindx != -1 )
{
connectionItems.replace( sslcertindx, certparam );
}
else
{
connectionItems.append( certparam );
}
QString keyparam = "sslkey='" + keyFilePath + "'";
int sslkeyindx = connectionItems.indexOf( QRegExp( "^sslkey='.*" ) );
if ( sslkeyindx != -1 )
{
connectionItems.replace( sslkeyindx, keyparam );
}
else
{
connectionItems.append( keyparam );
}
QString caparam = "sslrootcert='" + caFilePath + "'";
int sslcaindx = connectionItems.indexOf( QRegExp( "^sslrootcert='.*" ) );
if ( sslcaindx != -1 )
{
connectionItems.replace( sslcaindx, caparam );
}
else
{
connectionItems.append( caparam );
}
return true;
}
void QgsAuthPkiPathsMethod::clearCachedConfig( const QString &authcfg )
{
removePkiConfigBundle( authcfg );

View File

@ -41,6 +41,9 @@ class QgsAuthPkiPathsMethod : public QgsAuthMethod
bool updateNetworkRequest( QNetworkRequest &request, const QString &authcfg,
const QString &dataprovider = QString() ) override;
bool updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
const QString &dataprovider = QString() ) override;
void clearCachedConfig( const QString &authcfg ) override;
void updateMethodConfig( QgsAuthMethodConfig &mconfig ) override;

View File

@ -17,6 +17,9 @@
#include "qgsauthpkcs12method.h"
#include "qgsauthpkcs12edit.h"
#include <QDir>
#include <QFile>
#include <QUuid>
#ifndef QT_NO_OPENSSL
#include <QtCrypto>
#include <QSslConfiguration>
@ -38,12 +41,13 @@ QgsAuthPkcs12Method::QgsAuthPkcs12Method()
: QgsAuthMethod()
{
setVersion( 2 );
setExpansions( QgsAuthMethod::NetworkRequest );
setExpansions( QgsAuthMethod::NetworkRequest | QgsAuthMethod::DataSourceURI );
setDataProviders( QStringList()
<< "ows"
<< "wfs" // convert to lowercase
<< "wcs"
<< "wms" );
<< "wms"
<< "postgres" );
}
QgsAuthPkcs12Method::~QgsAuthPkcs12Method()
@ -101,6 +105,101 @@ bool QgsAuthPkcs12Method::updateNetworkRequest( QNetworkRequest &request, const
return true;
}
bool QgsAuthPkcs12Method::updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
const QString &dataprovider )
{
Q_UNUSED( dataprovider )
QgsDebugMsg( QString( "Update URI items for authcfg: %1" ).arg( authcfg ) );
QgsPkiConfigBundle * pkibundle = getPkiConfigBundle( authcfg );
if ( !pkibundle || !pkibundle->isValid() )
{
QgsDebugMsg( "Update URI items FAILED: PKI bundle invalid" );
return false;
}
QgsDebugMsg( "Update URI items: PKI bundle valid" );
QString pkiTempFileBase = "tmppki_%1.pem";
// save client cert to temp file
QString certFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
pkibundle->clientCert().toPem() );
if ( certFilePath.isEmpty() )
{
return false;
}
// save client cert key to temp file
QString keyFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
pkibundle->clientCertKey().toPem() );
if ( keyFilePath.isEmpty() )
{
return false;
}
// save CAs to temp file
QString caFilePath = QgsAuthCertUtils::pemTextToTempFile(
pkiTempFileBase.arg( QUuid::createUuid().toString() ),
QgsAuthManager::instance()->getTrustedCaCertsPemText() );
if ( caFilePath.isEmpty() )
{
return false;
}
// get common name of the client certificate
QString commonName = QgsAuthCertUtils::resolvedCertName( pkibundle->clientCert(), false );
// add uri parameters
QString userparam = "user='" + commonName + "'";
int userindx = connectionItems.indexOf( QRegExp( "^user='.*" ) );
if ( userindx != -1 )
{
connectionItems.replace( userindx, userparam );
}
else
{
connectionItems.append( userparam );
}
QString certparam = "sslcert='" + certFilePath + "'";
int sslcertindx = connectionItems.indexOf( QRegExp( "^sslcert='.*" ) );
if ( sslcertindx != -1 )
{
connectionItems.replace( sslcertindx, certparam );
}
else
{
connectionItems.append( certparam );
}
QString keyparam = "sslkey='" + keyFilePath + "'";
int sslkeyindx = connectionItems.indexOf( QRegExp( "^sslkey='.*" ) );
if ( sslkeyindx != -1 )
{
connectionItems.replace( sslkeyindx, keyparam );
}
else
{
connectionItems.append( keyparam );
}
QString caparam = "sslrootcert='" + caFilePath + "'";
int sslcaindx = connectionItems.indexOf( QRegExp( "^sslrootcert='.*" ) );
if ( sslcaindx != -1 )
{
connectionItems.replace( sslcaindx, caparam );
}
else
{
connectionItems.append( caparam );
}
return true;
}
void QgsAuthPkcs12Method::clearCachedConfig( const QString &authcfg )
{
removePkiConfigBundle( authcfg );

View File

@ -41,6 +41,10 @@ class QgsAuthPkcs12Method : public QgsAuthMethod
bool updateNetworkRequest( QNetworkRequest &request, const QString &authcfg,
const QString &dataprovider = QString() ) override;
bool updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
const QString &dataprovider = QString() ) override;
void clearCachedConfig( const QString &authcfg ) override;
void updateMethodConfig( QgsAuthMethodConfig &mconfig ) override;

View File

@ -17,9 +17,11 @@
#include "qgsauthcertutils.h"
#include <QColor>
#include <QDir>
#include <QFile>
#include <QObject>
#include <QSslCertificate>
#include <QUuid>
#include "qgsauthmanager.h"
#include "qgslogger.h"
@ -246,6 +248,35 @@ QStringList QgsAuthCertUtils::pkcs12BundleToPem( const QString &bundlepath,
return QStringList() << bundle.certificateChain().primary().toPEM() << bundle.privateKey().toPEM( passarray ) << algtype;
}
QString QgsAuthCertUtils::pemTextToTempFile( const QString &name, const QByteArray &pemtext )
{
QFile pemFile( QDir::tempPath() + QDir::separator() + name );
QString pemFilePath( pemFile.fileName() );
if ( pemFile.open( QIODevice::WriteOnly ) )
{
qint64 bytesWritten = pemFile.write( pemtext );
if ( bytesWritten == -1 )
{
QgsDebugMsg( QString( "FAILED to write to temp PEM file: %1" ).arg( pemFilePath ) );
pemFilePath.clear();
}
}
else
{
QgsDebugMsg( QString( "FAILED to open writing for temp PEM file: %1" ).arg( pemFilePath ) );
pemFilePath.clear();
}
if ( !pemFile.setPermissions( QFile::ReadUser ) )
{
QgsDebugMsg( QString( "FAILED to set permissions on temp PEM file: %1" ).arg( pemFilePath ) );
pemFilePath.clear();
}
return pemFilePath;
}
QString QgsAuthCertUtils::getCaSourceName( QgsAuthCertUtils::CaCertSource source, bool single )
{
switch ( source )

View File

@ -144,6 +144,13 @@ class CORE_EXPORT QgsAuthCertUtils
const QString &bundlepass = QString(),
bool reencrypt = true );
/** Write a temporary file for a PEM text of cert/key/CAs bundle component
* @param pemtext Component content as PEM text
* @param name Name of file
* @return File path to temporary file
*/
static QString pemTextToTempFile( const QString &name, const QByteArray &pemtext );
/** Get the general name for CA source enum type
* @param source The enum source type for the CA
* @param single Whether to return singular or plural description

View File

@ -53,7 +53,7 @@ QgsTransaction* QgsTransaction::create( const QStringList& layerIds )
if ( !layer )
return nullptr;
QString connStr = QgsDataSourceURI( layer->source() ).connectionInfo();
QString connStr = QgsDataSourceURI( layer->source() ).connectionInfo( false );
QString providerKey = layer->dataProvider()->name();
QgsTransaction* ts = QgsTransaction::create( connStr, providerKey );
if ( !ts )
@ -109,10 +109,10 @@ bool QgsTransaction::addLayer( QgsVectorLayer* layer )
return false;
//connection string not compatible
if ( QgsDataSourceURI( layer->source() ).connectionInfo() != mConnString )
if ( QgsDataSourceURI( layer->source() ).connectionInfo( false ) != mConnString )
{
QgsDebugMsg( QString( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
layer->id(), QgsDataSourceURI( layer->source() ).connectionInfo(), mConnString ) );
layer->id(), QgsDataSourceURI( layer->source() ).connectionInfo( false ), mConnString ) );
return false;
}

View File

@ -44,10 +44,10 @@ void QgsGeomColumnTypeThread::stop()
void QgsGeomColumnTypeThread::run()
{
QgsDataSourceURI uri = QgsPostgresConn::connUri( mName );
mConn = QgsPostgresConn::connectDb( uri.connectionInfo(), true );
mConn = QgsPostgresConn::connectDb( uri.connectionInfo( false ), true );
if ( !mConn )
{
QgsDebugMsg( "Connection failed - " + uri.connectionInfo() );
QgsDebugMsg( "Connection failed - " + uri.connectionInfo( false ) );
return;
}

View File

@ -197,9 +197,7 @@ void QgsPgNewConnection::testConnection()
mAuthConfigSelect->configId() );
}
QString conninfo = uri.connectionInfo();
QgsPostgresConn *conn = QgsPostgresConn::connectDb( conninfo, true );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo( false ), true );
if ( conn )
{

View File

@ -531,7 +531,7 @@ void QgsPgSourceSelect::on_btnConnect_clicked()
// populate the table list
QgsDataSourceURI uri = QgsPostgresConn::connUri( cmbConnections->currentText() );
QgsDebugMsg( "Connection info: " + uri.connectionInfo() );
QgsDebugMsg( "Connection info: " + uri.connectionInfo( false ) );
mDataSrcUri = uri;
mUseEstimatedMetadata = uri.useEstimatedMetadata();
@ -604,7 +604,7 @@ void QgsPgSourceSelect::setSql( const QModelIndex &index )
QModelIndex idx = mProxyModel.mapToSource( index );
QString tableName = mTableModel.itemFromIndex( idx.sibling( idx.row(), QgsPgTableModel::dbtmTable ) )->text();
QString uri = mTableModel.layerURI( idx, connectionInfo(), mUseEstimatedMetadata );
QString uri = mTableModel.layerURI( idx, connectionInfo( false ), mUseEstimatedMetadata );
if ( uri.isNull() )
{
QgsDebugMsg( "no uri" );

View File

@ -202,11 +202,29 @@ QgsPostgresConn::QgsPostgresConn( const QString& conninfo, bool readOnly, bool s
{
QgsDebugMsg( QString( "New PostgreSQL connection for " ) + conninfo );
mConn = PQconnectdb( conninfo.toLocal8Bit() ); // use what is set based on locale; after connecting, use Utf8
// expand connectionInfo
QgsDataSourceURI uri( conninfo );
QString expandedConnectionInfo = uri.connectionInfo( true );
mConn = PQconnectdb( expandedConnectionInfo.toLocal8Bit() ); // use what is set based on locale; after connecting, use Utf8
// remove temporary cert/key/CA
QgsDataSourceURI expandedUri( expandedConnectionInfo );
QString sslCertFile = expandedUri.param( "sslcert" );
sslCertFile.remove( "'" );
QFile::remove( sslCertFile );
QString sslKeyFile = expandedUri.param( "sslkey" );
sslKeyFile.remove( "'" );
QFile::remove( sslKeyFile );
QString sslCAFile = expandedUri.param( "sslrootcert" );
sslCAFile.remove( "'" );
QFile::remove( sslCAFile );
// check the connection status
if ( PQstatus() != CONNECTION_OK )
{
QgsDataSourceURI uri( conninfo );
QString username = uri.username();
QString password = uri.password();
@ -228,7 +246,7 @@ QgsPostgresConn::QgsPostgresConn( const QString& conninfo, bool readOnly, bool s
if ( !password.isEmpty() )
uri.setPassword( password );
QgsDebugMsg( "Connecting to " + uri.connectionInfo() );
QgsDebugMsg( "Connecting to " + uri.connectionInfo( false ) );
mConn = PQconnectdb( uri.connectionInfo().toLocal8Bit() );
}

View File

@ -52,17 +52,16 @@ QVector<QgsDataItem*> QgsPGConnectionItem::createChildren()
QgsDataSourceURI uri = QgsPostgresConn::connUri( mName );
// TODO: wee need to cancel somehow acquireConnection() if deleteLater() was called on this item to avoid later credential dialog if connection failed
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( uri.connectionInfo() );
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( uri.connectionInfo( false ) );
if ( !conn )
{
items.append( new QgsErrorItem( this, tr( "Connection failed" ), mPath + "/error" ) );
QgsDebugMsg( "Connection failed - " + uri.connectionInfo() );
QgsDebugMsg( "Connection failed - " + uri.connectionInfo( false ) );
return items;
}
QList<QgsPostgresSchemaProperty> schemas;
bool ok = conn->getSchemas( schemas );
QgsPostgresConnPool::instance()->releaseConnection( conn );
if ( !ok )
@ -163,7 +162,7 @@ void QgsPGConnectionItem::createSchema()
return;
QgsDataSourceURI uri = QgsPostgresConn::connUri( mName );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo( false ), false );
if ( !conn )
{
QMessageBox::warning( nullptr, tr( "Create Schema" ), tr( "Unable to create schema." ) );
@ -224,7 +223,7 @@ bool QgsPGConnectionItem::handleDrop( const QMimeData * data, QString toSchema )
if ( srcLayer->isValid() )
{
uri.setDataSource( QString(), u.name, "geom" );
QgsDebugMsg( "URI " + uri.uri() );
QgsDebugMsg( "URI " + uri.uri( false ) );
if ( !toSchema.isNull() )
{
@ -233,7 +232,7 @@ bool QgsPGConnectionItem::handleDrop( const QMimeData * data, QString toSchema )
QgsVectorLayerImport::ImportError err;
QString importError;
err = QgsVectorLayerImport::importLayer( srcLayer, uri.uri(), "postgres", &srcLayer->crs(), false, &importError, false, nullptr, progress );
err = QgsVectorLayerImport::importLayer( srcLayer, uri.uri( false ), "postgres", &srcLayer->crs(), false, &importError, false, nullptr, progress );
if ( err == QgsVectorLayerImport::NoError )
importResults.append( tr( "%1: OK!" ).arg( u.name ) );
else
@ -356,7 +355,7 @@ void QgsPGLayerItem::renameLayer()
QString newName = QgsPostgresConn::quotedIdentifier( dlg.name() );
QgsDataSourceURI dsUri( mUri );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
QMessageBox::warning( nullptr, tr( "Rename %1" ).arg( typeName ), tr( "Unable to rename %1." ).arg( lowerTypeName ) );
@ -397,7 +396,7 @@ void QgsPGLayerItem::truncateTable()
return;
QgsDataSourceURI dsUri( mUri );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
QMessageBox::warning( nullptr, tr( "Truncate Table" ), tr( "Unable to truncate table." ) );
@ -467,11 +466,11 @@ QVector<QgsDataItem*> QgsPGSchemaItem::createChildren()
QVector<QgsDataItem*>items;
QgsDataSourceURI uri = QgsPostgresConn::connUri( mConnectionName );
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( uri.connectionInfo() );
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( uri.connectionInfo( false ) );
if ( !conn )
{
items.append( new QgsErrorItem( this, tr( "Connection failed" ), mPath + "/error" ) );
QgsDebugMsg( "Connection failed - " + uri.connectionInfo() );
QgsDebugMsg( "Connection failed - " + uri.connectionInfo( false ) );
return items;
}
@ -546,7 +545,7 @@ void QgsPGSchemaItem::deleteSchema()
{
// check if schema contains tables/views
QgsDataSourceURI uri = QgsPostgresConn::connUri( mConnectionName );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo( false ), false );
if ( !conn )
{
QMessageBox::warning( nullptr, tr( "Delete Schema" ), tr( "Unable to delete schema." ) );
@ -619,7 +618,7 @@ void QgsPGSchemaItem::renameSchema()
QString schemaName = QgsPostgresConn::quotedIdentifier( mName );
QgsDataSourceURI uri = QgsPostgresConn::connUri( mConnectionName );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo( false ), false );
if ( !conn )
{
QMessageBox::warning( nullptr, tr( "Rename Schema" ), tr( "Unable to rename schema." ) );

View File

@ -734,7 +734,7 @@ void QgsPostgresFeatureIterator::getFeatureAttribute( int idx, QgsPostgresResult
// ------------------
QgsPostgresFeatureSource::QgsPostgresFeatureSource( const QgsPostgresProvider* p )
: mConnInfo( p->mUri.connectionInfo() )
: mConnInfo( p->mUri.connectionInfo( false ) )
, mGeometryColumn( p->mGeometryColumn )
, mFields( p->mAttributeFields )
, mSpatialColType( p->mSpatialColType )

View File

@ -95,7 +95,7 @@ QgsPostgresProvider::QgsPostgresProvider( QString const & uri )
mUseEstimatedMetadata = mUri.useEstimatedMetadata();
mSelectAtIdDisabled = mUri.selectAtIdDisabled();
QgsDebugMsg( QString( "Connection info is %1" ).arg( mUri.connectionInfo() ) );
QgsDebugMsg( QString( "Connection info is %1" ).arg( mUri.connectionInfo( false ) ) );
QgsDebugMsg( QString( "Geometry column is: %1" ).arg( mGeometryColumn ) );
QgsDebugMsg( QString( "Schema is: %1" ).arg( mSchemaName ) );
QgsDebugMsg( QString( "Table name is: %1" ).arg( mTableName ) );
@ -108,7 +108,7 @@ QgsPostgresProvider::QgsPostgresProvider( QString const & uri )
return;
}
mConnectionRO = QgsPostgresConn::connectDb( mUri.connectionInfo(), true );
mConnectionRO = QgsPostgresConn::connectDb( mUri.connectionInfo( false ), true );
if ( !mConnectionRO )
{
return;
@ -248,7 +248,7 @@ QgsPostgresConn* QgsPostgresProvider::connectionRW()
}
else if ( !mConnectionRW )
{
mConnectionRW = QgsPostgresConn::connectDb( mUri.connectionInfo(), false );
mConnectionRW = QgsPostgresConn::connectDb( mUri.connectionInfo( false ), false );
}
return mConnectionRW;
}
@ -3151,13 +3151,13 @@ QgsVectorLayerImport::ImportError QgsPostgresProvider::createEmptyLayer(
}
schemaTableName += quotedIdentifier( tableName );
QgsDebugMsg( QString( "Connection info is: %1" ).arg( dsUri.connectionInfo() ) );
QgsDebugMsg( QString( "Connection info is: %1" ).arg( dsUri.connectionInfo( false ) ) );
QgsDebugMsg( QString( "Geometry column is: %1" ).arg( geometryColumn ) );
QgsDebugMsg( QString( "Schema is: %1" ).arg( schemaName ) );
QgsDebugMsg( QString( "Table name is: %1" ).arg( tableName ) );
// create the table
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
if ( errorMessage )
@ -3319,7 +3319,7 @@ QgsVectorLayerImport::ImportError QgsPostgresProvider::createEmptyLayer(
// use the provider to edit the table
dsUri.setDataSource( schemaName, tableName, geometryColumn, QString(), primaryKey );
QgsPostgresProvider *provider = new QgsPostgresProvider( dsUri.uri() );
QgsPostgresProvider *provider = new QgsPostgresProvider( dsUri.uri( false ) );
if ( !provider->isValid() )
{
if ( errorMessage )
@ -3543,7 +3543,7 @@ QGISEXTERN bool deleteLayer( const QString& uri, QString& errCause )
}
schemaTableName += QgsPostgresConn::quotedIdentifier( tableName );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
errCause = QObject::tr( "Connection to database failed" );
@ -3609,7 +3609,7 @@ QGISEXTERN bool deleteSchema( const QString& schema, const QgsDataSourceURI& uri
QString schemaName = QgsPostgresConn::quotedIdentifier( schema );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo( false ), false );
if ( !conn )
{
errCause = QObject::tr( "Connection to database failed" );
@ -3640,7 +3640,7 @@ QGISEXTERN bool saveStyle( const QString& uri, const QString& qmlStyle, const QS
{
QgsDataSourceURI dsUri( uri );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
errCause = QObject::tr( "Connection to database failed" );
@ -3786,7 +3786,7 @@ QGISEXTERN QString loadStyle( const QString& uri, QString& errCause )
{
QgsDataSourceURI dsUri( uri );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
errCause = QObject::tr( "Connection to database failed" );
@ -3825,7 +3825,7 @@ QGISEXTERN int listStyles( const QString &uri, QStringList &ids, QStringList &na
{
QgsDataSourceURI dsUri( uri );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
errCause = QObject::tr( "Connection to database failed using username: %1" ).arg( dsUri.username() );
@ -3894,7 +3894,7 @@ QGISEXTERN QString getStyleById( const QString& uri, QString styleId, QString& e
{
QgsDataSourceURI dsUri( uri );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo(), false );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( dsUri.connectionInfo( false ), false );
if ( !conn )
{
errCause = QObject::tr( "Connection to database failed using username: %1" ).arg( dsUri.username() );

View File

@ -67,7 +67,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
/**
* Constructor for the provider. The uri must be in the following format:
* host=localhost user=gsherman dbname=test password=xxx table=test.alaska (the_geom)
* host=localhost dbname=test [user=gsherman [password=xxx] | authcfg=xxx] table=test.alaska (the_geom)
* @param uri String containing the required parameters to connect to the database
* and query the table.
*/