Skip to content
Permalink
Browse files

Server WMS GetPrint accesscontrol support

Cherry-picked from master c6699b2.
  • Loading branch information
elpaso authored and nyalldawson committed Apr 17, 2021
1 parent 2d1057d commit d9a1ca3fad944338fcac188d216831c2c4efaff3
Showing with 1,992 additions and 21 deletions.
  1. +20 −0 python/core/auto_generated/layout/qgslayoutrendercontext.sip.in
  2. +9 −2 python/core/auto_generated/qgsfeaturefilterprovider.sip.in
  3. +2 −1 python/server/auto_generated/qgsaccesscontrol.sip.in
  4. +3 −0 python/server/auto_generated/qgsfeaturefilter.sip.in
  5. +5 −1 python/server/auto_generated/qgsfeaturefilterprovidergroup.sip.in
  6. +7 −0 src/core/layout/qgslayoutatlas.cpp
  7. +74 −1 src/core/layout/qgslayoutitemattributetable.cpp
  8. +8 −0 src/core/layout/qgslayoutitemattributetable.h
  9. +4 −0 src/core/layout/qgslayoutitemmap.cpp
  10. +10 −0 src/core/layout/qgslayoutrendercontext.cpp
  11. +22 −0 src/core/layout/qgslayoutrendercontext.h
  12. +10 −2 src/core/qgsfeaturefilterprovider.h
  13. +1 −1 src/server/qgsaccesscontrol.cpp
  14. +2 −1 src/server/qgsaccesscontrol.h
  15. +6 −0 src/server/qgsfeaturefilter.cpp
  16. +3 −0 src/server/qgsfeaturefilter.h
  17. +11 −1 src/server/qgsfeaturefilterprovidergroup.cpp
  18. +6 −1 src/server/qgsfeaturefilterprovidergroup.h
  19. +1 −0 src/server/services/wms/qgswmsgetprint.cpp
  20. +18 −4 src/server/services/wms/qgswmsrendercontext.cpp
  21. +3 −2 src/server/services/wms/qgswmsrendercontext.h
  22. +17 −4 src/server/services/wms/qgswmsrenderer.cpp
  23. +1 −0 tests/src/python/CMakeLists.txt
  24. +385 −0 tests/src/python/test_qgsserver_accesscontrol_wms_getprint_postgres.py
  25. BIN ...tdata/control_images/qgis_server/WMS_GetPrint_postgres_print2/v1/WMS_GetPrint_postgres_print2.png
  26. BIN ...tdata/control_images/qgis_server/WMS_GetPrint_postgres_print2/v2/WMS_GetPrint_postgres_print2.png
  27. BIN ...es/qgis_server/WMS_GetPrint_postgres_print2_filtered/v1/WMS_GetPrint_postgres_print2_filtered.png
  28. BIN ...es/qgis_server/WMS_GetPrint_postgres_print2_filtered/v2/WMS_GetPrint_postgres_print2_filtered.png
  29. BIN ...images/qgis_server/WMS_GetPrint_postgres_print2_subset/v1/WMS_GetPrint_postgres_print2_subset.png
  30. BIN ...images/qgis_server/WMS_GetPrint_postgres_print2_subset/v2/WMS_GetPrint_postgres_print2_subset.png
  31. +1,364 −0 tests/testdata/qgis_server_accesscontrol/pg_multiple_pks.qgs
@@ -311,6 +311,26 @@ Returns the current list of predefined scales for use with the layout.
.. seealso:: :py:func:`setPredefinedScales`

.. versionadded:: 3.10
%End

QgsFeatureFilterProvider *featureFilterProvider() const;
%Docstring
Returns the possibly NULL feature filter provider.

A feature filter provider for filtering visible features or attributes.
It is currently used by QGIS Server Access Control Plugins.

.. versionadded:: 3.18
%End

void setFeatureFilterProvider( QgsFeatureFilterProvider *featureFilterProvider );
%Docstring
Sets feature filter provider to ``featureFilterProvider``.

A feature filter provider for filtering visible features or attributes.
It is currently used by QGIS Server Access Control Plugins.

.. versionadded:: 3.18
%End

signals:
@@ -16,10 +16,10 @@
class QgsFeatureFilterProvider
{
%Docstring
Abstract interface for use by classes that filter the features of a layer.
Abstract interface for use by classes that filter the features or attributes of a layer.

A QgsFeatureFilterProvider provides a method for modifying a QgsFeatureRequest in place to apply
additional filters to the request.
additional filters to the request, since QGIS 3.18 a method to filter allowed attributes is also available.

.. versionadded:: 2.14
%End
@@ -37,6 +37,13 @@ Derived classes must implement this method.

:param layer: the layer to filter
:param featureRequest: the feature request to update
%End

virtual QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const = 0;
%Docstring
Returns the list of visible attribute names from a list of ``attributes`` names for the given ``layer``

.. versionadded:: 3.18
%End

virtual QgsFeatureFilterProvider *clone() const = 0 /Factory/;
@@ -108,7 +108,8 @@ Returns the layer delete right
:return: ``True`` if we can do a delete
%End

QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const;
virtual QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const;

%Docstring
Returns the authorized layer attributes

@@ -37,6 +37,9 @@ Filter the features of the layer
:param filterFeatures: the request to fill
%End

virtual QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const;


virtual QgsFeatureFilterProvider *clone() const /Factory/;

%Docstring
@@ -30,12 +30,15 @@ Constructor
virtual void filterFeatures( const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures ) const;

%Docstring
Filter the features of the layer
Filter the features of the layer.

:param layer: the layer to control
:param filterFeatures: the request to fill
%End

virtual QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const;


virtual QgsFeatureFilterProvider *clone() const /Factory/;

%Docstring
@@ -53,6 +56,7 @@ Add another filter provider to the group
:return: itself
%End


};

/************************************************************************
@@ -288,6 +288,13 @@ int QgsLayoutAtlas::updateFeatures()
req.setFilterExpression( mFilterExpression );
}

#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( mLayout->renderContext().featureFilterProvider() )
{
mLayout->renderContext().featureFilterProvider()->filterFeatures( mCoverageLayer.get(), req );
}
#endif

QgsFeatureIterator fit = mCoverageLayer->getFeatures( req );

std::unique_ptr<QgsExpression> nameExpression;
@@ -29,6 +29,7 @@
#include "qgsexception.h"
#include "qgsmapsettings.h"
#include "qgsexpressioncontextutils.h"
#include "qgsexpressionnodeimpl.h"
#include "qgsgeometryengine.h"
#include "qgsconditionalstyle.h"

@@ -176,6 +177,7 @@ void QgsLayoutItemAttributeTable::resetColumns()
//rebuild columns list from vector layer fields
int idx = 0;
const QgsFields sourceFields = source->fields();

for ( const auto &field : sourceFields )
{
QString currentAlias = source->attributeDisplayName( idx );
@@ -329,8 +331,9 @@ void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields,
{
int attrIdx = layerFields.lookupField( field );
if ( attrIdx < 0 )
{
continue;

}
QString currentAlias = source->attributeDisplayName( attrIdx );
QgsLayoutTableColumn col;
col.setAttribute( layerFields.at( attrIdx ).name() );
@@ -413,6 +416,13 @@ bool QgsLayoutItemAttributeTable::getTableContents( QgsLayoutTableContents &cont
}
}

#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( mLayout->renderContext().featureFilterProvider() )
{
mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
}
#endif

QgsRectangle selectionRect;
QgsGeometry visibleRegion;
std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
@@ -541,6 +551,9 @@ bool QgsLayoutItemAttributeTable::getTableContents( QgsLayoutTableContents &cont
// We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
// corresponding option is enabled)
QVector< Cell > currentRow;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
mColumns = filteredColumns();
#endif
currentRow.reserve( mColumns.count() );
QgsLayoutTableRow rowContents;
rowContents.reserve( mColumns.count() );
@@ -693,6 +706,66 @@ QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant )
return replaced;
}

#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
{
QgsLayoutTableColumns allowedColumns { mColumns };

QgsVectorLayer *source { sourceLayer() };

if ( ! source )
{
return allowedColumns;
}

QHash<const QString, QSet<QString>> columnAttributesMap;
QSet<QString> allowedAttributes;

for ( const auto &c : qgis::as_const( allowedColumns ) )
{
if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
{
columnAttributesMap[ c.attribute() ] = QSet<QString>();
const QgsExpression columnExp { c.attribute() };
const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
for ( const auto &cref : constRefs )
{
columnAttributesMap[ c.attribute() ].insert( cref->name() );
allowedAttributes.insert( cref->name() );
}
}
}

if ( mLayout->renderContext().featureFilterProvider() )
{
const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
#else
const QSet<QString> filteredAttributesSet { filteredAttributes.toSet() };
#endif
if ( filteredAttributesSet != allowedAttributes )
{
const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
{
for ( const auto &f : qgis::as_const( forbidden ) )
{
if ( columnAttributesMap[ c.attribute() ].contains( f ) )
{
return true;
}
}
return false;
} ), allowedColumns.end() );

}
}

return allowedColumns;
}
#endif

QgsVectorLayer *QgsLayoutItemAttributeTable::sourceLayer() const
{
switch ( mSource )
@@ -390,6 +390,14 @@ class CORE_EXPORT QgsLayoutItemAttributeTable: public QgsLayoutTable
*/
QVariant replaceWrapChar( const QVariant &variant ) const;

#ifdef HAVE_SERVER_PYTHON_PLUGINS

/**
* Returns the list of visible columns filtered by the access control filter rules.
*/
QgsLayoutTableColumns filteredColumns( );
#endif

private slots:
//! Checks if this vector layer will be removed (and sets mVectorLayer to 0 if yes)
void removeLayer( const QString &layerId );
@@ -1334,6 +1334,10 @@ void QgsLayoutItemMap::drawMap( QPainter *painter, const QgsRectangle &extent, Q
}

QgsMapRendererCustomPainterJob job( ms, painter );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
job.setFeatureFilterProvider( mLayout->renderContext().featureFilterProvider() );
#endif

// Render the map in this thread. This is done because of problems
// with printing to printer on Windows (printing to PDF is fine though).
// Raster images were not displayed - see #10599
@@ -133,3 +133,13 @@ void QgsLayoutRenderContext::setPredefinedScales( const QVector<qreal> &scales )
std::sort( mPredefinedScales.begin(), mPredefinedScales.end() ); // clazy:exclude=detaching-member
emit predefinedScalesChanged();
}

QgsFeatureFilterProvider *QgsLayoutRenderContext::featureFilterProvider() const
{
return mFeatureFilterProvider;
}

void QgsLayoutRenderContext::setFeatureFilterProvider( QgsFeatureFilterProvider *featureFilterProvider )
{
mFeatureFilterProvider = featureFilterProvider;
}
@@ -297,6 +297,26 @@ class CORE_EXPORT QgsLayoutRenderContext : public QObject
*/
QVector<qreal> predefinedScales() const { return mPredefinedScales; }

/**
* Returns the possibly NULL feature filter provider.
*
* A feature filter provider for filtering visible features or attributes.
* It is currently used by QGIS Server Access Control Plugins.
*
* \since QGIS 3.18
*/
QgsFeatureFilterProvider *featureFilterProvider() const;

/**
* Sets feature filter provider to \a featureFilterProvider.
*
* A feature filter provider for filtering visible features or attributes.
* It is currently used by QGIS Server Access Control Plugins.
*
* \since QGIS 3.18
*/
void setFeatureFilterProvider( QgsFeatureFilterProvider *featureFilterProvider );

signals:

/**
@@ -342,6 +362,8 @@ class CORE_EXPORT QgsLayoutRenderContext : public QObject

QVector<qreal> mPredefinedScales;

QgsFeatureFilterProvider *mFeatureFilterProvider = nullptr;

friend class QgsLayoutExporter;
friend class TestQgsLayout;
friend class LayoutContextPreviewSettingRestorer;
@@ -19,6 +19,7 @@
#define QGSFEATUREFILTERPROVIDER_H

#include <QtGlobal>
#include <QStringList>
#include "qgis_sip.h"

#include "qgis_core.h"
@@ -31,10 +32,11 @@ class QgsFeatureRequest;
/**
* \ingroup core
* \class QgsFeatureFilterProvider
* \brief Abstract interface for use by classes that filter the features of a layer.
* \brief Abstract interface for use by classes that filter the features or attributes of a layer.
*
* A QgsFeatureFilterProvider provides a method for modifying a QgsFeatureRequest in place to apply
* additional filters to the request.
* additional filters to the request, since QGIS 3.18 a method to filter allowed attributes is also available.
*
* \since QGIS 2.14
*/

@@ -59,6 +61,12 @@ class CORE_EXPORT QgsFeatureFilterProvider
*/
virtual void filterFeatures( const QgsVectorLayer *layer, QgsFeatureRequest &featureRequest ) const = 0;

/**
* Returns the list of visible attribute names from a list of \a attributes names for the given \a layer
* \since QGIS 3.18
*/
virtual QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const = 0;

/**
* Create a clone of the feature filter provider
* \returns a new clone
@@ -64,7 +64,7 @@ void QgsAccessControl::filterFeatures( const QgsVectorLayer *layer, QgsFeatureRe

QString expression;

if ( mResolved && mFilterFeaturesExpressions.keys().contains( layer->id() ) )
if ( mResolved && mFilterFeaturesExpressions.contains( layer->id() ) )
{
expression = mFilterFeaturesExpressions[layer->id()];
}
@@ -134,7 +134,7 @@ class SERVER_EXPORT QgsAccessControl : public QgsFeatureFilterProvider
* \param attributes the list of attribute
* \returns the list of visible attributes
*/
QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const;
QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const override;

/**
* Are we authorized to modify the following geometry
@@ -166,6 +166,7 @@ class SERVER_EXPORT QgsAccessControl : public QgsFeatureFilterProvider

QMap<QString, QString> mFilterFeaturesExpressions;
bool mResolved;

};

#endif
@@ -29,6 +29,12 @@ void QgsFeatureFilter::filterFeatures( const QgsVectorLayer *layer, QgsFeatureRe
}
}

QStringList QgsFeatureFilter::layerAttributes( const QgsVectorLayer *, const QStringList &attributes ) const
{
// Do nothing
return attributes;
}

QgsFeatureFilterProvider *QgsFeatureFilter::clone() const
{
auto result = new QgsFeatureFilter();
@@ -44,6 +44,8 @@ class SERVER_EXPORT QgsFeatureFilter : public QgsFeatureFilterProvider
*/
void filterFeatures( const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures ) const override;

QStringList layerAttributes( const QgsVectorLayer *layer, const QStringList &attributes ) const override;

/**
* Returns a clone of the object
* \returns A clone
@@ -59,6 +61,7 @@ class SERVER_EXPORT QgsFeatureFilter : public QgsFeatureFilterProvider

private:
QMap<QString, QString> mFilters;

};

#endif

0 comments on commit d9a1ca3

Please sign in to comment.