Skip to content
Permalink
Browse files

[server] Make restoration of original layer subsetStrings much more

robust (there were many code paths where the original filters
were not being correctly restored)
  • Loading branch information
nyalldawson committed Feb 22, 2016
1 parent a46dca5 commit f264799f08d0c664fc9cd94add41fb2a3b17b4e9
Showing with 78 additions and 43 deletions.
  1. +1 −1 src/server/qgsowsserver.cpp
  2. +32 −3 src/server/qgsowsserver.h
  3. +8 −13 src/server/qgswfsserver.cpp
  4. +28 −24 src/server/qgswmsserver.cpp
  5. +9 −2 src/server/qgswmsserver.h
@@ -49,7 +49,7 @@ void QgsOWSServer::applyAccessControlLayerFilters( QgsMapLayer* mapLayer, QHash<
#endif

/** Restore layer filter as original */
void QgsOWSServer::restoreLayerFilters( const QHash<QgsMapLayer*, QString>& filterMap ) const
void QgsOWSServer::restoreLayerFilters( const QHash<QgsMapLayer*, QString>& filterMap )
{
QHash<QgsMapLayer*, QString>::const_iterator filterIt = filterMap.constBegin();
for ( ; filterIt != filterMap.constEnd(); ++filterIt )
@@ -23,6 +23,8 @@
#include "qgsaccesscontrol.h"
#endif

#include <QHash>

class QgsOWSServer
{
public:
@@ -45,6 +47,11 @@ class QgsOWSServer

virtual void executeRequest() = 0;

/** Restores the original layer filters
* @param filterMap the original layer filter
*/
static void restoreLayerFilters( const QHash < QgsMapLayer*, QString >& filterMap );

private:
QgsOWSServer() {}

@@ -64,10 +71,32 @@ class QgsOWSServer
void applyAccessControlLayerFilters( QgsMapLayer* layer, QHash<QgsMapLayer*, QString>& originalLayerFilters ) const;
#endif

/** Restores the original layer filters
* @param filterMap the original layer filter
};

/** RAII class to restore layer filters on destruction
*/
class QgsOWSServerFilterRestorer
{
public:

QgsOWSServerFilterRestorer() {}

//! Destructor. When object is destroyed all original layer filters will be restored.
~QgsOWSServerFilterRestorer()
{
QgsOWSServer::restoreLayerFilters( mOriginalLayerFilters );
}

/** Returns a reference to the object's hash of layers to original subsetString filters.
* Original layer subsetString filters MUST be inserted into this hash before modifying them.
*/
void restoreLayerFilters( const QHash < QgsMapLayer*, QString >& filterMap ) const;
QHash<QgsMapLayer*, QString>& originalFilters() { return mOriginalLayerFilters; }

private:
QHash<QgsMapLayer*, QString> mOriginalLayerFilters;

QgsOWSServerFilterRestorer( const QgsOWSServerFilterRestorer& rh );
QgsOWSServerFilterRestorer& operator=( const QgsOWSServerFilterRestorer& rh );
};

#endif // QGSOWSSERVER_H
@@ -422,7 +422,11 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format

QDomDocument doc;
QString errorMsg;
QHash<QgsMapLayer*, QString> originalLayerFilters;

//scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope
//there's LOTS of potential exit paths here, so we avoid having to restore the filters manually
QScopedPointer< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );

if ( doc.setContent( mParameters.value( "REQUEST_BODY" ), true, &errorMsg ) )
{
QDomElement docElem = doc.documentElement();
@@ -466,10 +470,9 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( currentLayer ) )
{
restoreLayerFilters( originalLayerFilters );
throw QgsMapServiceException( "Security", "Feature access permission denied" );
}
applyAccessControlLayerFilters( currentLayer, originalLayerFilters );
applyAccessControlLayerFilters( currentLayer, filterRestorer->originalFilters() );
#endif

expressionContext << QgsExpressionContextUtils::layerScope( layer );
@@ -646,9 +649,6 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
{
if ( filter->hasParserError() )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
restoreLayerFilters( originalLayerFilters );
#endif
throw QgsMapServiceException( "RequestNotWellFormed", filter->parserErrorString() );
}
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
@@ -658,10 +658,6 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
QVariant res = filter->evaluate( &expressionContext );
if ( filter->hasEvalError() )
{

#ifdef HAVE_SERVER_PYTHON_PLUGINS
restoreLayerFilters( originalLayerFilters );
#endif
throw QgsMapServiceException( "RequestNotWellFormed", filter->evalErrorString() );
}
if ( res.toInt() != 0 )
@@ -703,9 +699,8 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format

}

#ifdef HAVE_SERVER_PYTHON_PLUGINS
restoreLayerFilters( originalLayerFilters );
#endif
//force restoration of original layer filters
filterRestorer.reset();

QgsMessageLog::logMessage( mErrors.join( "\n" ) );

@@ -1239,7 +1239,6 @@ QDomDocument QgsWMSServer::describeLayer()
QByteArray* QgsWMSServer::getPrint( const QString& formatString )
{
QStringList layersList, stylesList, layerIdList;
QString dummyFormat;
QImage* theImage = initializeRendering( layersList, stylesList, layerIdList );
if ( !theImage )
{
@@ -1257,18 +1256,21 @@ QByteArray* QgsWMSServer::getPrint( const QString& formatString )
}
#endif

QHash<QgsMapLayer*, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
//scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope
//there's LOTS of potential exit paths here, so we avoid having to restore the filters manually
QScopedPointer< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );

applyRequestedLayerFilters( layersList, filterRestorer->originalFilters() );

#ifdef HAVE_SERVER_PYTHON_PLUGINS
applyAccessControlLayersFilters( layersList, originalLayerFilters );
applyAccessControlLayersFilters( layersList, filterRestorer->originalFilters() );
#endif

QStringList selectedLayerIdList = applyFeatureSelections( layersList );

//GetPrint request needs a template parameter
if ( !mParameters.contains( "TEMPLATE" ) )
{
restoreLayerFilters( originalLayerFilters );
clearFeatureSelections( selectedLayerIdList );
throw QgsMapServiceException( "ParameterMissing", "The TEMPLATE parameter is required for the GetPrint request" );
}
@@ -1284,7 +1286,6 @@ QByteArray* QgsWMSServer::getPrint( const QString& formatString )
QgsComposition* c = mConfigParser->createPrintComposition( mParameters[ "TEMPLATE" ], mMapRenderer, QMap<QString, QString>( mParameters ) );
if ( !c )
{
restoreLayerFilters( originalLayerFilters );
clearFeatureSelections( selectedLayerIdList );
return nullptr;
}
@@ -1333,7 +1334,6 @@ QByteArray* QgsWMSServer::getPrint( const QString& formatString )
if ( !tempFile.open() )
{
delete c;
restoreLayerFilters( originalLayerFilters );
clearFeatureSelections( selectedLayerIdList );
return nullptr;
}
@@ -1344,13 +1344,11 @@ QByteArray* QgsWMSServer::getPrint( const QString& formatString )
}
else //unknown format
{
restoreLayerFilters( originalLayerFilters );
clearFeatureSelections( selectedLayerIdList );
throw QgsMapServiceException( "InvalidFormat", "Output format '" + formatString + "' is not supported in the GetPrint request" );
}

restoreOpacities( bkVectorRenderers, bkRasterRenderers, labelTransparencies, labelBufferTransparencies );
restoreLayerFilters( originalLayerFilters );
clearFeatureSelections( selectedLayerIdList );

delete c;
@@ -1397,10 +1395,14 @@ QImage* QgsWMSServer::getMap( HitTest* hitTest )
}
#endif

QHash<QgsMapLayer*, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
//scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope
//there's LOTS of potential exit paths here, so we avoid having to restore the filters manually
QScopedPointer< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );

applyRequestedLayerFilters( layersList, filterRestorer->originalFilters() );

#ifdef HAVE_SERVER_PYTHON_PLUGINS
applyAccessControlLayersFilters( layersList, originalLayerFilters );
applyAccessControlLayersFilters( layersList, filterRestorer->originalFilters() );
#endif

QStringList selectedLayerIdList = applyFeatureSelections( layersList );
@@ -1426,7 +1428,6 @@ QImage* QgsWMSServer::getMap( HitTest* hitTest )
}

restoreOpacities( bkVectorRenderers, bkRasterRenderers, labelTransparencies, labelBufferTransparencies );
restoreLayerFilters( originalLayerFilters );
clearFeatureSelections( selectedLayerIdList );

// QgsMessageLog::logMessage( "clearing filters" );
@@ -1625,10 +1626,15 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result, const QString& version )
}

//get the layer registered in QgsMapLayerRegistry and apply possible filters
QStringList layerIds = layerSet( layersList, stylesList, mMapRenderer->destinationCrs() );
QHash<QgsMapLayer*, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
( void )layerSet( layersList, stylesList, mMapRenderer->destinationCrs() );

//scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope
//there's LOTS of potential exit paths here, so we avoid having to restore the filters manually
QScopedPointer< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );
applyRequestedLayerFilters( layersList, filterRestorer->originalFilters() );

#ifdef HAVE_SERVER_PYTHON_PLUGINS
applyAccessControlLayersFilters( layersList, originalLayerFilters );
applyAccessControlLayersFilters( layersList, filterRestorer->originalFilters() );
#endif

QDomElement getFeatureInfoElement;
@@ -1710,7 +1716,6 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result, const QString& version )
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( currentLayer ) )
{
restoreLayerFilters( originalLayerFilters );
throw QgsMapServiceException( "Security", "You are not allowed to access to the layer: " + currentLayer->name() );
}
#endif
@@ -1831,7 +1836,9 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result, const QString& version )
convertFeatureInfoToSIA2045( result );
}

restoreLayerFilters( originalLayerFilters );
//force restoration of original filters
filterRestorer.reset();

QgsMapLayerRegistry::instance()->removeAllMapLayers();
delete featuresRect;
return 0;
@@ -2495,13 +2502,11 @@ QStringList QgsWMSServer::layerSet( const QStringList &layersList,
}


QHash<QgsMapLayer*, QString> QgsWMSServer::applyRequestedLayerFilters( const QStringList& layerList ) const
void QgsWMSServer::applyRequestedLayerFilters( const QStringList& layerList , QHash<QgsMapLayer*, QString>& originalFilters ) const
{
QHash<QgsMapLayer*, QString> filterMap;

if ( layerList.isEmpty() )
{
return filterMap;
return;
}

QString filterParameter = mParameters.value( "FILTER" );
@@ -2548,7 +2553,7 @@ QHash<QgsMapLayer*, QString> QgsWMSServer::applyRequestedLayerFilters( const QSt
QgsVectorLayer* filteredLayer = dynamic_cast<QgsVectorLayer*>( filter );
if ( filteredLayer )
{
filterMap.insert( filteredLayer, filteredLayer->subsetString() );
originalFilters.insert( filteredLayer, filteredLayer->subsetString() );
QString newSubsetString = eqSplit.at( 1 );
if ( !filteredLayer->subsetString().isEmpty() )
{
@@ -2565,8 +2570,8 @@ QHash<QgsMapLayer*, QString> QgsWMSServer::applyRequestedLayerFilters( const QSt
if ( mMapRenderer && mMapRenderer->extent().isEmpty() )
{
QgsRectangle filterExtent;
QHash<QgsMapLayer*, QString>::const_iterator filterIt = filterMap.constBegin();
for ( ; filterIt != filterMap.constEnd(); ++filterIt )
QHash<QgsMapLayer*, QString>::const_iterator filterIt = originalFilters.constBegin();
for ( ; filterIt != originalFilters.constEnd(); ++filterIt )
{
QgsMapLayer* mapLayer = filterIt.key();
if ( !mapLayer )
@@ -2587,7 +2592,6 @@ QHash<QgsMapLayer*, QString> QgsWMSServer::applyRequestedLayerFilters( const QSt
mMapRenderer->setExtent( filterExtent );
}
}
return filterMap;
}

#ifdef HAVE_SERVER_PYTHON_PLUGINS
@@ -187,15 +187,22 @@ class QgsWMSServer: public QgsOWSServer
#endif

/** Apply filter (subset) strings from the request to the layers. Example: '&FILTER=<layer1>:"AND property > 100",<layer2>:"AND bla = 'hallo!'" '
@return a map with the original filters ( layer id / filter string )*/
QHash<QgsMapLayer*, QString> applyRequestedLayerFilters( const QStringList& layerList ) const;
* @param layerList list of layer IDs to filter
* @param originalFilters hash of layer ID to original filter string
* @note It is strongly recommended that this method be called alongside use of QgsOWSServerFilterRestorer
* to ensure that the original filters are always correctly restored, regardless of whether exceptions
* are thrown or functions are terminated early.
*/
void applyRequestedLayerFilters( const QStringList& layerList, QHash<QgsMapLayer*, QString>& originalFilters ) const;

#ifdef HAVE_SERVER_PYTHON_PLUGINS
/** Apply filter strings from the access control to the layers.
* @param layerList layers to filter
* @param originalLayerFilters the original layers filter dictionary
*/
void applyAccessControlLayersFilters( const QStringList& layerList, QHash<QgsMapLayer*, QString>& originalLayerFilters ) const;
#endif

/** Tests if a filter sql string is allowed (safe)
@return true in case of success, false if string seems unsafe*/
bool testFilterStringSafety( const QString& filter ) const;

0 comments on commit f264799

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