Skip to content
Permalink
Browse files

[feature][vectortiles] Allow using authentication system and setting

custom referer string for vector tile connections
  • Loading branch information
nyalldawson committed Sep 8, 2020
1 parent 6901d6f commit 142cda5ef4fe2d4c8a039cf4724b5b93f9d82f22
@@ -30,6 +30,14 @@ QString QgsVectorTileProviderConnection::encodedUri( const QgsVectorTileProvider
uri.setParam( QStringLiteral( "zmin" ), QString::number( conn.zMin ) );
if ( conn.zMax != -1 )
uri.setParam( QStringLiteral( "zmax" ), QString::number( conn.zMax ) );
if ( !conn.authCfg.isEmpty() )
uri.setAuthConfigId( conn.authCfg );
if ( !conn.username.isEmpty() )
uri.setUsername( conn.username );
if ( !conn.password.isEmpty() )
uri.setPassword( conn.password );
if ( !conn.referer.isEmpty() )
uri.setParam( QStringLiteral( "referer" ), conn.referer );

switch ( conn.serviceType )
{
@@ -53,6 +61,10 @@ QgsVectorTileProviderConnection::Data QgsVectorTileProviderConnection::decodedUr
conn.url = dsUri.param( QStringLiteral( "url" ) );
conn.zMin = dsUri.hasParam( QStringLiteral( "zmin" ) ) ? dsUri.param( QStringLiteral( "zmin" ) ).toInt() : -1;
conn.zMax = dsUri.hasParam( QStringLiteral( "zmax" ) ) ? dsUri.param( QStringLiteral( "zmax" ) ).toInt() : -1;
conn.authCfg = dsUri.authConfigId();
conn.username = dsUri.username();
conn.password = dsUri.password();
conn.referer = dsUri.param( QStringLiteral( "referer" ) );

if ( dsUri.hasParam( QStringLiteral( "serviceType" ) ) )
{
@@ -72,6 +84,14 @@ QString QgsVectorTileProviderConnection::encodedLayerUri( const QgsVectorTilePro
uri.setParam( QStringLiteral( "zmin" ), QString::number( conn.zMin ) );
if ( conn.zMax != -1 )
uri.setParam( QStringLiteral( "zmax" ), QString::number( conn.zMax ) );
if ( !conn.authCfg.isEmpty() )
uri.setAuthConfigId( conn.authCfg );
if ( !conn.username.isEmpty() )
uri.setUsername( conn.username );
if ( !conn.password.isEmpty() )
uri.setPassword( conn.password );
if ( !conn.referer.isEmpty() )
uri.setParam( QStringLiteral( "referer" ), conn.referer );

switch ( conn.serviceType )
{
@@ -107,6 +127,10 @@ QgsVectorTileProviderConnection::Data QgsVectorTileProviderConnection::connectio
conn.url = settings.value( QStringLiteral( "url" ) ).toString();
conn.zMin = settings.value( QStringLiteral( "zmin" ), -1 ).toInt();
conn.zMax = settings.value( QStringLiteral( "zmax" ), -1 ).toInt();
conn.authCfg = settings.value( QStringLiteral( "authcfg" ) ).toString();
conn.username = settings.value( QStringLiteral( "username" ) ).toString();
conn.password = settings.value( QStringLiteral( "password" ) ).toString();
conn.referer = settings.value( QStringLiteral( "referer" ) ).toString();

if ( settings.contains( QStringLiteral( "serviceType" ) ) )
{
@@ -131,6 +155,10 @@ void QgsVectorTileProviderConnection::addConnection( const QString &name, QgsVec
settings.setValue( QStringLiteral( "url" ), conn.url );
settings.setValue( QStringLiteral( "zmin" ), conn.zMin );
settings.setValue( QStringLiteral( "zmax" ), conn.zMax );
settings.setValue( QStringLiteral( "authcfg" ), conn.authCfg );
settings.setValue( QStringLiteral( "username" ), conn.username );
settings.setValue( QStringLiteral( "password" ), conn.password );
settings.setValue( QStringLiteral( "referer" ), conn.referer );

switch ( conn.serviceType )
{
@@ -54,6 +54,15 @@ class CORE_EXPORT QgsVectorTileProviderConnection : public QgsAbstractProviderCo
int zMax = -1;

ServiceType serviceType = Generic;

// Authentication configuration id
QString authCfg;
// HTTP Basic username
QString username;
// HTTP Basic password
QString password;
// Referer
QString referer;
};

//! Returns connection data encoded as a string
@@ -505,7 +505,13 @@ QByteArray QgsVectorTileLayer::getRawTile( QgsTileXYZ tileID )
{
QgsTileMatrix tileMatrix = QgsTileMatrix::fromWebMercator( tileID.zoomLevel() );
QgsTileRange tileRange( tileID.column(), tileID.column(), tileID.row(), tileID.row() );
QList<QgsVectorTileRawData> rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, tileMatrix, QPointF(), tileRange );

QgsDataSourceUri dsUri;
dsUri.setEncodedUri( mDataSource );
const QString authcfg = dsUri.authConfigId();
const QString referer = dsUri.param( QStringLiteral( "referer" ) );

QList<QgsVectorTileRawData> rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, tileMatrix, QPointF(), tileRange, authcfg, referer );
if ( rawTiles.isEmpty() )
return QByteArray();
return rawTiles.first().data;
@@ -41,6 +41,11 @@ QgsVectorTileLayerRenderer::QgsVectorTileLayerRenderer( QgsVectorTileLayer *laye
, mFeedback( new QgsFeedback )
{

QgsDataSourceUri dsUri;
dsUri.setEncodedUri( layer->source() );
mAuthCfg = dsUri.authConfigId();
mReferer = dsUri.param( QStringLiteral( "referer" ) );

if ( QgsLabelingEngine *engine = context.labelingEngine() )
{
if ( layer->labeling() )
@@ -106,13 +111,13 @@ bool QgsVectorTileLayerRenderer::render()
{
QElapsedTimer tFetch;
tFetch.start();
rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, mTileMatrix, viewCenter, mTileRange );
rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, mTileMatrix, viewCenter, mTileRange, mAuthCfg, mReferer );
QgsDebugMsgLevel( QStringLiteral( "Tile fetching time: %1" ).arg( tFetch.elapsed() / 1000. ), 2 );
QgsDebugMsgLevel( QStringLiteral( "Fetched tiles: %1" ).arg( rawTiles.count() ), 2 );
}
else
{
asyncLoader.reset( new QgsVectorTileLoader( mSourcePath, mTileMatrix, mTileRange, viewCenter, mFeedback.get() ) );
asyncLoader.reset( new QgsVectorTileLoader( mSourcePath, mTileMatrix, mTileRange, viewCenter, mAuthCfg, mReferer, mFeedback.get() ) );
QObject::connect( asyncLoader.get(), &QgsVectorTileLoader::tileRequestFinished, [this]( const QgsVectorTileRawData & rawTile )
{
QgsDebugMsgLevel( QStringLiteral( "Got tile asynchronously: " ) + rawTile.id.toString(), 2 );
@@ -56,6 +56,10 @@ class QgsVectorTileLayerRenderer : public QgsMapLayerRenderer
QString mSourceType;
//! Path/URL of the source. Format depends on source type
QString mSourcePath;

QString mAuthCfg;
QString mReferer;

//! Minimum zoom level at which source has any valid tiles (negative = unconstrained)
int mSourceMinZoom = -1;
//! Maximum zoom level at which source has any valid tiles (negative = unconstrained)
@@ -22,10 +22,15 @@
#include "qgsmbtiles.h"
#include "qgsnetworkaccessmanager.h"
#include "qgsvectortileutils.h"
#include "qgsapplication.h"
#include "qgsauthmanager.h"
#include "qgsmessagelog.h"

QgsVectorTileLoader::QgsVectorTileLoader( const QString &uri, const QgsTileMatrix &tileMatrix, const QgsTileRange &range, const QPointF &viewCenter, QgsFeedback *feedback )
QgsVectorTileLoader::QgsVectorTileLoader( const QString &uri, const QgsTileMatrix &tileMatrix, const QgsTileRange &range, const QPointF &viewCenter, const QString &authid, const QString &referer, QgsFeedback *feedback )
: mEventLoop( new QEventLoop )
, mFeedback( feedback )
, mAuthCfg( authid )
, mReferer( referer )
{
if ( feedback )
{
@@ -89,6 +94,14 @@ void QgsVectorTileLoader::loadFromNetworkAsync( const QgsTileXYZ &id, const QgsT
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );

if ( !mReferer.isEmpty() )
request.setRawHeader( "Referer", mReferer.toUtf8() );

if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
{
QgsMessageLog::logMessage( tr( "network request update failed for authentication config" ), tr( "Network" ) );
}

QNetworkReply *reply = QgsNetworkAccessManager::instance()->get( request );
connect( reply, &QNetworkReply::finished, this, &QgsVectorTileLoader::tileReplyFinished );

@@ -143,7 +156,7 @@ void QgsVectorTileLoader::canceled()

//////

QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range )
QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range, const QString &authid, const QString &referer )
{
QList<QgsVectorTileRawData> rawTiles;

@@ -159,7 +172,7 @@ QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const
QgsVectorTileUtils::sortTilesByDistanceFromCenter( tiles, viewCenter );
for ( QgsTileXYZ id : qgis::as_const( tiles ) )
{
QByteArray rawData = isUrl ? loadFromNetwork( id, tileMatrix, sourcePath ) : loadFromMBTiles( id, mbReader );
QByteArray rawData = isUrl ? loadFromNetwork( id, tileMatrix, sourcePath, authid, referer ) : loadFromMBTiles( id, mbReader );
if ( !rawData.isEmpty() )
{
rawTiles.append( QgsVectorTileRawData( id, rawData ) );
@@ -168,12 +181,17 @@ QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const
return rawTiles;
}

QByteArray QgsVectorTileLoader::loadFromNetwork( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl )
QByteArray QgsVectorTileLoader::loadFromNetwork( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl, const QString &authid, const QString &referer )
{
QString url = QgsVectorTileUtils::formatXYZUrlTemplate( requestUrl, id, tileMatrix );
QNetworkRequest nr;
nr.setUrl( QUrl( url ) );

if ( !referer.isEmpty() )
nr.setRawHeader( "Referer", referer.toUtf8() );

QgsBlockingNetworkRequest req;
req.setAuthCfg( authid );
QgsDebugMsgLevel( QStringLiteral( "Blocking request: " ) + url, 2 );
QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr );
if ( errCode != QgsBlockingNetworkRequest::NoError )
@@ -59,10 +59,20 @@ class QgsVectorTileLoader : public QObject
public:

//! Returns raw tile data for the specified range of tiles. Blocks the caller until all tiles are fetched.
static QList<QgsVectorTileRawData> blockingFetchTileRawData( const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range );
static QList<QgsVectorTileRawData> blockingFetchTileRawData( const QString &sourceType,
const QString &sourcePath,
const QgsTileMatrix &tileMatrix,
const QPointF &viewCenter,
const QgsTileRange &range,
const QString &authid,
const QString &referer );

//! Returns raw tile data for a single tile, doing a HTTP request. Block the caller until tile data are downloaded.
static QByteArray loadFromNetwork( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl );
static QByteArray loadFromNetwork( const QgsTileXYZ &id,
const QgsTileMatrix &tileMatrix,
const QString &requestUrl,
const QString &authid,
const QString &referer );
//! Returns raw tile data for a single tile loaded from MBTiles file
static QByteArray loadFromMBTiles( const QgsTileXYZ &id, QgsMbTiles &mbTileReader );

@@ -71,7 +81,8 @@ class QgsVectorTileLoader : public QObject
//

//! Constructs tile loader for doing asynchronous requests and starts network requests
QgsVectorTileLoader( const QString &uri, const QgsTileMatrix &tileMatrix, const QgsTileRange &range, const QPointF &viewCenter, QgsFeedback *feedback );
QgsVectorTileLoader( const QString &uri, const QgsTileMatrix &tileMatrix, const QgsTileRange &range, const QPointF &viewCenter,
const QString &authid, const QString &referer, QgsFeedback *feedback );
~QgsVectorTileLoader();

//! Blocks the caller until all asynchronous requests are finished (with a success or a failure)
@@ -93,6 +104,10 @@ class QgsVectorTileLoader : public QObject
std::unique_ptr<QEventLoop> mEventLoop;
//! Feedback object that allows cancellation of pending requests
QgsFeedback *mFeedback;

QString mAuthCfg;
QString mReferer;

//! Running tile requests
QList<QNetworkReply *> mReplies;

@@ -772,6 +772,10 @@ QDomDocument QgsManageConnectionsDialog::saveVectorTileConnections( const QStrin
el.setAttribute( QStringLiteral( "zmin" ), settings.value( path + "/zmin", -1 ).toInt() );
el.setAttribute( QStringLiteral( "zmax" ), settings.value( path + "/zmax", -1 ).toInt() );
el.setAttribute( QStringLiteral( "serviceType" ), settings.value( path + "/serviceType", -1 ).toInt() );
el.setAttribute( QStringLiteral( "authcfg" ), settings.value( path + "/authcfg" ).toString() );
el.setAttribute( QStringLiteral( "username" ), settings.value( path + "/username" ).toString() );
el.setAttribute( QStringLiteral( "password" ), settings.value( path + "/password" ).toString() );
el.setAttribute( QStringLiteral( "referer" ), settings.value( path + "/referer" ).toString() );

root.appendChild( el );
}
@@ -1618,6 +1622,11 @@ void QgsManageConnectionsDialog::loadVectorTileConnections( const QDomDocument &
settings.setValue( QStringLiteral( "zmin" ), child.attribute( QStringLiteral( "zmin" ) ) );
settings.setValue( QStringLiteral( "zmax" ), child.attribute( QStringLiteral( "zmax" ) ) );
settings.setValue( QStringLiteral( "serviceType" ), child.attribute( QStringLiteral( "serviceType" ) ) );
settings.setValue( QStringLiteral( "authcfg" ), child.attribute( QStringLiteral( "authcfg" ) ) );
settings.setValue( QStringLiteral( "username" ), child.attribute( QStringLiteral( "username" ) ) );
settings.setValue( QStringLiteral( "password" ), child.attribute( QStringLiteral( "password" ) ) );
settings.setValue( QStringLiteral( "referer" ), child.attribute( QStringLiteral( "referer" ) ) );

settings.endGroup();

child = child.nextSiblingElement();
@@ -39,13 +39,22 @@ void QgsArcgisVectorTileConnectionDialog::setConnection( const QString &name, co

QgsVectorTileProviderConnection::Data conn = QgsVectorTileProviderConnection::decodedUri( uri );
mEditUrl->setText( conn.url );

mAuthSettings->setUsername( conn.username );
mAuthSettings->setPassword( conn.password );
mEditReferer->setText( conn.referer );
mAuthSettings->setConfigId( conn.authCfg );
}

QString QgsArcgisVectorTileConnectionDialog::connectionUri() const
{
QgsVectorTileProviderConnection::Data conn;
conn.url = mEditUrl->text();
conn.serviceType = QgsVectorTileProviderConnection::ArcgisVectorTileService;
conn.username = mAuthSettings->username();
conn.password = mAuthSettings->password();
conn.referer = mEditReferer->text();
conn.authCfg = mAuthSettings->configId( );
return QgsVectorTileProviderConnection::encodedUri( conn );
}

@@ -47,6 +47,11 @@ void QgsVectorTileConnectionDialog::setConnection( const QString &name, const QS
mSpinZMin->setValue( conn.zMin != -1 ? conn.zMin : 0 );
mCheckBoxZMax->setChecked( conn.zMax != -1 );
mSpinZMax->setValue( conn.zMax != -1 ? conn.zMax : 14 );

mAuthSettings->setUsername( conn.username );
mAuthSettings->setPassword( conn.password );
mEditReferer->setText( conn.referer );
mAuthSettings->setConfigId( conn.authCfg );
}

QString QgsVectorTileConnectionDialog::connectionUri() const
@@ -57,6 +62,10 @@ QString QgsVectorTileConnectionDialog::connectionUri() const
conn.zMin = mSpinZMin->value();
if ( mCheckBoxZMax->isChecked() )
conn.zMax = mSpinZMax->value();
conn.username = mAuthSettings->username();
conn.password = mAuthSettings->password();
conn.referer = mEditReferer->text();
conn.authCfg = mAuthSettings->configId( );
return QgsVectorTileProviderConnection::encodedUri( conn );
}

0 comments on commit 142cda5

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