The plugin define the following methods:

* layerFilterExpression
  Return an additional filter, used in
  WMS/GetMap, WMS/GetFeatureInfo, WFS/GetFeature to filter the features
* layerFilterSubsetString
  Return an additional the subset string (typically SQL) filter.
  Faster than the layerFilterExpression but not supported on all the
  type of layer
* layerPermissions
  Change the rights on the layer per user (known by the plugin)
  Concern rights: publish, insert, update, delete.
  Mostly used in WFS/Transaction, and the publish in all requests.
* authorizedLayerAttributes
  Be able to show some attributes only for a subset of user
  Used in: WMS/GetFeatureInfo, WFS/GetFeature
* allowToEdit
  Be able to don't allow to edit a particular feature, in our case base
  on the Geometry
  Used in: WFS/Transaction
* cacheKey
  Cache key to used to create the capabilities cache, "" for no cache,
  shouldn't contains any "-", default to ""
This commit is contained in:
Stéphane Brunner 2015-05-19 15:53:51 +02:00
parent b1743dc8a8
commit c9f0d83aaf
70 changed files with 5674 additions and 101 deletions

View File

@ -44,6 +44,7 @@ sudo apt-get install --force-yes --no-install-recommends --no-install-suggests \
python-qt4-sql \
python-sip \
python-sip-dev \
python-gdal \
spawn-fcgi \
txt2tags \
xauth \

View File

@ -1,4 +1,3 @@
printf "[qgis_test]\nhost=localhost\ndbname=qgis_test\nuser=postgres" > ~/.pg_service.conf
psql -c 'CREATE DATABASE qgis_test;' -U postgres
psql -f $TRAVIS_BUILD_DIR/tests/testdata/provider/testdata.sql -U postgres -d qgis_test

View File

@ -1,2 +1 @@
xvfb-run ctest -V -E 'qgis_openstreetmaptest|qgis_wcsprovidertest' -S ./qgis-test-travis.ctest --output-on-failure

View File

@ -604,6 +604,7 @@ INPUT = @CMAKE_SOURCE_DIR@/doc \
@CMAKE_SOURCE_DIR@/src/server/qgsmapserviceexception.h \
@CMAKE_SOURCE_DIR@/src/server/qgsrequesthandler.h \
@CMAKE_SOURCE_DIR@/src/server/qgsserverfilter.h \
@CMAKE_SOURCE_DIR@/src/server/qgsaccesscontrolfilter.h \
@CMAKE_SOURCE_DIR@/src/server/qgsserverinterface.h
# This tag can be used to specify the character encoding of the source files

View File

@ -47,6 +47,7 @@
%Include qgsfeature.sip
%Include qgsfeatureiterator.sip
%Include qgsfeaturerequest.sip
%Include qgsfeaturefilterprovider.sip
%Include qgsfield.sip
%Include qgsgeometryvalidator.sip
%Include qgsgeometrysimplifier.sip

View File

@ -0,0 +1,24 @@
/**
* Interface used by class that will filter the features of a layer.
* The only method `filterFeatures` fill the `QgsFeatureRequest` to get only the
* wanted features.
**/
class QgsFeatureFilterProvider
{
%TypeHeaderCode
#include <qgsfeaturefilterprovider.h>
%End
public:
/** Add some filter to the feature request to don't have the unauthorized (unauthorised) features
* @param layer the layer to filter
* @param featureRequest the feature request to update
* @note not available in Python bindings
*/
virtual void filterFeatures( const QgsVectorLayer* layer, QgsFeatureRequest& featureRequest ) const = 0;
/** Create a clone of the feature filter provider
* @return a new clone
*/
virtual QgsFeatureFilterProvider* clone() const = 0;
};

View File

@ -288,6 +288,10 @@ class QgsMapRenderer : QObject
*/
bool splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent /In,Out/, QgsRectangle& r2 /Out/ );
/** Set a feature filter provider to filter the features
* @param ffp the feature filter provider
*/
void setFeatureFilterProvider( const QgsFeatureFilterProvider* ffp );
signals:
//! @deprecated in 2.4 - not emitted anymore

View File

@ -0,0 +1,33 @@
/***************************************************************************
qgsaccesscontrol.sip
--------------------
Access control helper for Qgis Server plugins
begin : 2015-05-19
copyright : (C) 2015 by Stéphane Brunner
email : stephane dot brunner at camptocamp dot org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/**
* \class QgsAccessControl
* \brief Class defining access control helper for QGIS Server.
*/
class QgsAccessControl : QgsFeatureFilterProvider
{
%TypeHeaderCode
#include "qgsaccesscontrol.h"
#include "qgsaccesscontrolfilter.h"
#include <QMultiMap>
%End
};

View File

@ -0,0 +1,77 @@
/***************************************************************************
qgsaccesscontrolfilter.sip
--------------------------
Access control interface for Qgis Server plugins
begin : 2015-05-19
copyright : (C) 2015 by Stéphane Brunner
email : stephane dot brunner at camptocamp dot org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/**
* \class QgsAccessControlFilter
* \brief Class defining access control interface for QGIS Server.
*
* Security can define any (or none) of the following method:
* * layerFilterExpression()
* * layerFilterSubsetString()
* * layerPermissions()
* * authorizedLayerAttributes()
* * allowToEdit()
* * cacheKey()
*/
class QgsAccessControlFilter
{
%TypeHeaderCode
#include "qgsaccesscontrolfilter.h"
#include "qgsserverinterface.h"
#include "qgsfeature.h"
#include "qgsmaplayer.h"
%End
public:
/** Constructor
* QgsServerInterface passed to plugins constructors
* and must be passed to QgsAccessControlPlugin instances.
*/
QgsAccessControlFilter( const QgsServerInterface* serverInterface );
/** Destructor */
virtual ~QgsAccessControlFilter();
/** Describe the layer permission */
struct LayerPermissions
{
bool canRead;
bool canUpdate;
bool canInsert;
bool canDelete;
};
/** Return the QgsServerInterface instance*/
const QgsServerInterface* serverInterface() const;
/** Return an additional expression filter */
virtual const QString layerFilterExpression( const QgsVectorLayer* layer /Transfer/ ) const;
/** Return an additional the subset string (typically SQL) filter.
Faster than the layerFilterExpression but not supported on all the type of layer */
virtual const QString layerFilterSubsetString( const QgsVectorLayer* layer /Transfer/ ) const;
/** Return the layer permissions */
virtual const LayerPermissions layerPermissions( const QgsMapLayer* layer /Transfer/ ) const;
/** Return the authorized layer attributes */
virtual const QStringList* authorizedLayerAttributes( const QgsVectorLayer* layer /Transfer/, const QStringList& attributes ) const;
/** Are we authorize to modify the following geometry */
virtual bool allowToEdit( const QgsVectorLayer* layer /Transfer/, const QgsFeature& feature /Transfer/ ) const;
/** Cache key to used to create the capabilities cache, "" for no cache, shouldn't any contains "-", default to "" */
virtual const QString cacheKey() const;
};
typedef QMultiMap<int, QgsAccessControlFilter*> QgsAccessControlFilterMap;

View File

@ -25,15 +25,16 @@ class QgsConfigCache: QObject
{
%TypeHeaderCode
#include "qgsconfigcache.h"
#include "qgsaccesscontrolfilter.h"
%End
public:
static QgsConfigCache* instance();
~QgsConfigCache();
QgsServerProjectParser* serverConfiguration( const QString& filePath );
QgsWCSProjectParser* wcsConfiguration( const QString& filePath );
QgsWFSProjectParser* wfsConfiguration( const QString& filePath );
QgsWMSConfigParser* wmsConfiguration( const QString& filePath, const QMap<QString, QString>& parameterMap = QMap< QString, QString >() );
QgsWCSProjectParser* wcsConfiguration( const QString& filePath, const QgsAccessControl* accessControl );
QgsWFSProjectParser* wfsConfiguration( const QString& filePath, const QgsAccessControl* accessControl );
QgsWMSConfigParser* wmsConfiguration( const QString& filePath, const QgsAccessControl* accessControl, const QMap<QString, QString>& parameterMap = QMap< QString, QString >() );
private:
QgsConfigCache();

View File

@ -61,7 +61,7 @@ class QgsRequestHandler
/** Remove a request parameter*/
virtual int removeParameter( const QString &key ) = 0;
/** Return a request parameter*/
virtual QString parameter( const QString &key) const = 0;
virtual QString parameter( const QString &key ) const = 0;
/** Return the requested format string*/
QString format() const;
/** Return the mime type for the response*/

View File

@ -50,6 +50,8 @@ class QgsServerInterface
virtual void registerFilter( QgsServerFilter* filter /Transfer/, int priority = 0 ) = 0;
/** Set the filters map */
virtual void setFilters( QgsServerFiltersMap* filters /Transfer/) = 0;
/** Register a security module with the given priority.*/
virtual void registerAccessControl( QgsAccessControlFilter* accessControl /Transfer/, int priority = 0 ) = 0;
/** Return an environment variable set by FCGI*/
virtual QString getEnv(const QString& name ) const = 0;
// Commented because of problems with typedef QgsServerFiltersMap, provided

View File

@ -37,7 +37,7 @@ class QgsWCSServer: public QgsOWSServer
public:
/** Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWCSServer( const QString& configFilePath, QMap<QString, QString>& parameters, QgsWCSProjectParser* pp,
QgsRequestHandler* rh );
QgsRequestHandler* rh, const QgsAccessControl* accessControl );
~QgsWCSServer();
void executeRequest() override;

View File

@ -23,7 +23,7 @@ class QgsWCSProjectParser
%End
public:
QgsWCSProjectParser( const QString& filePath );
QgsWCSProjectParser( const QString& filePath, const QgsAccessControl* ac );
~QgsWCSProjectParser();
void serviceCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;

View File

@ -61,7 +61,7 @@ class QgsWFSServer: public QgsOWSServer
public:
/** Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWFSServer( const QString& configFilePath, QMap<QString, QString>& parameters, QgsWFSProjectParser* cp,
QgsRequestHandler* rh );
QgsRequestHandler* rh, const QgsAccessControl* accessControl );
~QgsWFSServer();
void executeRequest() override;

View File

@ -24,7 +24,7 @@ class QgsWFSProjectParser
%End
public:
QgsWFSProjectParser( const QString& filePath );
QgsWFSProjectParser( const QString& filePath, const QgsAccessControl* ac );
~QgsWFSProjectParser();
void serviceCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;

View File

@ -59,7 +59,7 @@ class QgsWMSServer: public QgsOWSServer
/** Constructor. Does _NOT_ take ownership of
QgsConfigParser, QgsCapabilitiesCache and QgsMapRenderer*/
QgsWMSServer( const QString& configFilePath, QMap<QString, QString> &parameters, QgsWMSConfigParser* cp, QgsRequestHandler* rh,
QgsMapRenderer* renderer, QgsCapabilitiesCache* capCache );
QgsMapRenderer* renderer, QgsCapabilitiesCache* capCache, const QgsAccessControl* accessControl );
~QgsWMSServer();
void executeRequest() override;

View File

@ -23,7 +23,7 @@ class QgsWMSProjectParser : public QgsWMSConfigParser
%End
public:
QgsWMSProjectParser( const QString& filePath );
QgsWMSProjectParser( const QString& filePath, const QgsAccessControl* ac );
virtual ~QgsWMSProjectParser();
/** Adds layer and style specific capabilities elements to the parent node. This includes the individual layers and styles, their description, native CRS, bounding boxes, etc.

View File

@ -14,6 +14,8 @@
%If (HAVE_SERVER_PYTHON_PLUGINS)
%Include qgsserverfilter.sip
%Include qgsserverinterface.sip
%Include qgsaccesscontrolfilter.sip
%Include qgsaccesscontrol.sip
%End
%Include qgsmapserviceexception.sip

View File

@ -0,0 +1,55 @@
/***************************************************************************
qgsfeaturefilterprovider.h
--------------------------
begin : 22-05-2015
copyright : (C) 2008 by Stéphane Brunner
email : stephane dot brunner at camptocamp dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSFEATUREFILTERPROVIDER_H
#define QGSFEATUREFILTERPROVIDER_H
#include <QtGlobal>
class QString;
class QgsVectorLayer;
class QgsFeatureRequest;
/** \ingroup core
* Interface used by class that will filter the features of a layer.
* The only method `filterFeatures` fill the `QgsFeatureRequest` to get only the
* wanted features.
**/
class CORE_EXPORT QgsFeatureFilterProvider
{
public:
/** Constructor */
QgsFeatureFilterProvider() {};
/** Destructor */
virtual ~QgsFeatureFilterProvider() {};
/** Add some filter to the feature request to don't have the unauthorized (unauthorised) features
* @param layer the layer to filter
* @param featureRequest the feature request to update
*/
virtual void filterFeatures( const QgsVectorLayer* layer, QgsFeatureRequest& featureRequest ) const = 0;
/** Create a clone of the feature filter provider
* @return a new clone
*/
virtual QgsFeatureFilterProvider* clone() const = 0;
};
#endif

View File

@ -335,6 +335,13 @@ class CORE_EXPORT QgsMapRenderer : public QObject
*/
bool splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent, QgsRectangle& r2 );
/** Set a feature filter provider to filter the features
* @param ffp the feature filter provider
*/
void setFeatureFilterProvider( const QgsFeatureFilterProvider* ffp ) {
mRenderContext.setFeatureFilterProvider( ffp );
}
signals:
//! @deprecated in 2.4 - not emitted anymore

View File

@ -19,6 +19,8 @@
#include "qgsrendercontext.h"
#include "qgsmapsettings.h"
#include "qgsexpression.h"
#include "qgsvectorlayer.h"
QgsRenderContext::QgsRenderContext()
: mFlags( DrawEditingInfo | UseAdvancedEffects | DrawSelection | UseRenderingOptimization )
@ -31,12 +33,17 @@ QgsRenderContext::QgsRenderContext()
, mLabelingEngine( NULL )
, mLabelingEngine2( 0 )
, mGeometry( 0 )
, mFeatureFilterProvider( NULL )
{
mVectorSimplifyMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification );
}
QgsRenderContext::~QgsRenderContext()
{
if ( mFeatureFilterProvider != NULL ) {
delete mFeatureFilterProvider;
mFeatureFilterProvider = NULL;
}
}
void QgsRenderContext::setFlags( const QgsRenderContext::Flags& flags )
@ -140,3 +147,15 @@ void QgsRenderContext::setUseRenderingOptimization( bool enabled )
{
setFlag( UseRenderingOptimization, enabled );
}
void QgsRenderContext::setFeatureFilterProvider( const QgsFeatureFilterProvider* ffp )
{
if ( mFeatureFilterProvider != NULL ) {
delete mFeatureFilterProvider;
mFeatureFilterProvider = NULL;
}
if ( ffp != NULL )
{
mFeatureFilterProvider = ffp->clone();
}
}

View File

@ -25,6 +25,7 @@
#include "qgsrectangle.h"
#include "qgsvectorsimplifymethod.h"
#include "qgsexpressioncontext.h"
#include "qgsfeaturefilterprovider.h"
class QPainter;
@ -32,6 +33,9 @@ class QgsAbstractGeometryV2;
class QgsLabelingEngineInterface;
class QgsLabelingEngineV2;
class QgsMapSettings;
class QgsExpression;
class QgsVectorLayer;
/** \ingroup core
* Contains information about the context of a rendering operation.
@ -195,6 +199,18 @@ class CORE_EXPORT QgsRenderContext
/** Sets pointer to original (unsegmentized) geometry*/
void setGeometry( const QgsAbstractGeometryV2* geometry ) { mGeometry = geometry; }
/** Set a filter feature provider used to filter the features
* @param ffp the filter feature provider
* @note not available in Python bindings
*/
void setFeatureFilterProvider( const QgsFeatureFilterProvider* ffp );
/** Get the filter feature provider used to filter the features
* @return the filter feature provider
* @note not available in Python bindings
*/
const QgsFeatureFilterProvider* featureFilterProvider() { return mFeatureFilterProvider; }
private:
Flags mFlags;
@ -238,6 +254,9 @@ class CORE_EXPORT QgsRenderContext
/** Pointer to the (unsegmentized) geometry*/
const QgsAbstractGeometryV2* mGeometry;
/** The feature filter provider */
const QgsFeatureFilterProvider* mFeatureFilterProvider;
};
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsRenderContext::Flags )

View File

@ -43,6 +43,7 @@
QgsVectorLayerRenderer::QgsVectorLayerRenderer( QgsVectorLayer* layer, QgsRenderContext& context )
: QgsMapLayerRenderer( layer->id() )
, mContext( context )
, mLayer( layer )
, mFields( layer->fields() )
, mRendererV2( 0 )
, mCache( 0 )
@ -152,10 +153,23 @@ bool QgsVectorLayerRenderer::render()
.setFilterRect( requestExtent )
.setSubsetOfAttributes( mAttrNames, mFields );
if ( !rendererFilter.isEmpty() && rendererFilter != "TRUE" )
const QgsFeatureFilterProvider* featureFilterProvider = mContext.featureFilterProvider();
if ( featureFilterProvider != NULL)
{
featureFilterProvider->filterFeatures( mLayer, featureRequest );
}
if ( !rendererFilter.isNull() )
{
featureRequest.setFilterExpression( rendererFilter );
featureRequest.setExpressionContext( mContext.expressionContext() );
if ( featureRequest.filterExpression() == NULL )
{
featureRequest.setFilterExpression( rendererFilter );
}
else
{
featureRequest.setFilterExpression( QString( "(%s) AND (%s)" )
.arg( rendererFilter, featureRequest.filterExpression()->expression() ) );
}
}
// enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine.

View File

@ -86,6 +86,9 @@ class QgsVectorLayerRenderer : public QgsMapLayerRenderer
QgsRenderContext& mContext;
/** The rendered layer */
QgsVectorLayer* mLayer;
QgsFields mFields; // TODO: use fields from mSource
QgsFeatureIds mSelectedFeatureIds;

View File

@ -25,6 +25,7 @@ SET ( qgis_mapserv_SRCS
qgsgetrequesthandler.cpp
qgspostrequesthandler.cpp
qgssoaprequesthandler.cpp
qgsowsserver.cpp
qgswmsserver.cpp
qgswfsserver.cpp
qgswcsserver.cpp
@ -82,6 +83,8 @@ SET(qgis_mapserv_SRCS ${qgis_mapserv_SRCS}
qgsserverplugins.cpp
qgsserverinterface.cpp
qgsserverfilter.cpp
qgsaccesscontrolfilter.cpp
qgsaccesscontrol.cpp
qgsserverinterfaceimpl.cpp
)
ENDIF (WITH_SERVER_PLUGINS)

View File

@ -0,0 +1,169 @@
/***************************************************************************
qgsaccesscontrol.cpp
--------------------
begin : 22-05-2015
copyright : (C) 2008 by Stéphane Brunner
email : stephane dot brunner at camptocamp dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsaccesscontrol.h"
#include "qgsfeaturerequest.h"
#include "qgsmaplayer.h"
#include "qgsvectorlayer.h"
#include <QStringList>
/** Filter the features of the layer */
void QgsAccessControl::filterFeatures( const QgsVectorLayer* layer, QgsFeatureRequest& featureRequest ) const
{
QStringList expressions = QStringList();
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
const QString expression = acIterator.value()->layerFilterExpression( layer );
if ( expression != NULL ) {
expressions.append( expression );
}
}
if ( !expressions.isEmpty() ) {
featureRequest.setFilterExpression( expressions.join(" AND ") );
}
}
/** Clone the object */
QgsFeatureFilterProvider* QgsAccessControl::clone() const
{
return new QgsAccessControl(*this);
}
/** Return an additional subset string (typically SQL) filter */
const QString QgsAccessControl::extraSubsetString( const QgsVectorLayer* layer ) const
{
QStringList sqls = QStringList();
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
const QString sql = acIterator.value()->layerFilterSubsetString( layer );
if ( sql != NULL ) {
sqls.append( sql );
}
}
return sqls.isEmpty() ? NULL : sqls.join(" AND ");
}
/** Return the layer read right */
bool QgsAccessControl::layerReadPermission( const QgsMapLayer* layer ) const
{
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
if ( !acIterator.value()->layerPermissions( layer ).canRead)
{
return false;
}
}
return true;
}
/** Return the layer insert right */
bool QgsAccessControl::layerInsertPermission( const QgsVectorLayer* layer ) const
{
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
if ( !acIterator.value()->layerPermissions( layer ).canInsert)
{
return false;
}
}
return true;
}
/** Return the layer update right */
bool QgsAccessControl::layerUpdatePermission( const QgsVectorLayer* layer ) const
{
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
if ( !acIterator.value()->layerPermissions( layer ).canUpdate)
{
return false;
}
}
return true;
}
/** Return the layer delete right */
bool QgsAccessControl::layerDeletePermission( const QgsVectorLayer* layer ) const
{
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
if ( !acIterator.value()->layerPermissions( layer ).canDelete)
{
return false;
}
}
return true;
}
/** Return the authorized layer attributes */
const QStringList QgsAccessControl::layerAttributes( const QgsVectorLayer* layer, const QStringList attributes ) const
{
QStringList currentAttributes( attributes );
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
const QStringList* newAttributes = acIterator.value()->authorizedLayerAttributes( layer, currentAttributes );
if (newAttributes != NULL) {
currentAttributes = *newAttributes;
}
}
return currentAttributes;
}
/** Are we authorized to modify the following geometry */
bool QgsAccessControl::allowToEdit( const QgsVectorLayer* layer, const QgsFeature& feature ) const
{
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
if ( !acIterator.value()->allowToEdit( layer, feature ) )
{
return false;
}
}
return true;
}
/** Fill the capabilities caching key */
bool QgsAccessControl::fillCacheKey( QStringList& cacheKey ) const
{
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
QString newKey = acIterator.value()->cacheKey();
if ( newKey.length() == 0 ) {
cacheKey.clear();
return false;
}
cacheKey << newKey;
}
return true;
}
/** Register a new access control filter */
void QgsAccessControl::registerAccessControl( QgsAccessControlFilter* accessControl, int priority )
{
mPluginsAccessControls->insert( priority, accessControl );
}

View File

@ -0,0 +1,125 @@
/***************************************************************************
qgsaccesscontrol.h
------------------
begin : 22-05-2015
copyright : (C) 2008 by Stéphane Brunner
email : stephane dot brunner at camptocamp dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSACCESSCONTROL_H
#define QGSACCESSCONTROL_H
#include "qgsfeaturefilterprovider.h"
#include "qgsaccesscontrolfilter.h"
#include <QMultiMap>
class QgsAccessControlPlugin;
/** \ingroup server
* \class QgsAccessControl
* \brief A helper class that centralise the restrictions given by all the
* access control filter plugins.
**/
class SERVER_EXPORT QgsAccessControl : public QgsFeatureFilterProvider
{
public:
/** Constructor */
QgsAccessControl()
{
mPluginsAccessControls = new QgsAccessControlFilterMap();
};
/** Constructor */
QgsAccessControl( const QgsAccessControl& copy )
{
mPluginsAccessControls = new QgsAccessControlFilterMap( *copy.mPluginsAccessControls );
};
/** Destructor */
~QgsAccessControl()
{
delete mPluginsAccessControls;
};
/** Filter the features of the layer
* @param layer the layer to control
* @param filterFeatures the request to fill
*/
void filterFeatures( const QgsVectorLayer* layer, QgsFeatureRequest& filterFeatures ) const;
/** Return a clone of the object
* @return A clone
*/
QgsFeatureFilterProvider* clone() const;
/** Return an additional subset string (typically SQL) filter
* @param layer the layer to control
* @return the subset string to use
*/
const QString extraSubsetString( const QgsVectorLayer* layer ) const;
/** Return the layer read right
* @param layer the layer to control
* @return true if it can be read
*/
bool layerReadPermission( const QgsMapLayer* layer ) const;
/** Return the layer insert right
* @param layer the layer to control
* @return true if we can insert on it
*/
bool layerInsertPermission( const QgsVectorLayer* layer ) const;
/** Return the layer update right
* @param layer the layer to control
* @return true if we can do an update
*/
bool layerUpdatePermission( const QgsVectorLayer* layer ) const;
/** Return the layer delete right
* @param layer the layer to control
* @return true if we can do a delete
*/
bool layerDeletePermission( const QgsVectorLayer* layer ) const;
/** Return the authorized layer attributes
* @param layer the layer to control
* @param attributes the list of attribute
* @return the list of visible attributes
*/
const QStringList layerAttributes( const QgsVectorLayer* layer, const QStringList attributes ) const;
/** Are we authorized to modify the following geometry
* @param layer the layer to control
* @param feature the concerned feature
* @return true if we are allowed to edit the feature
*/
bool allowToEdit( const QgsVectorLayer* layer, const QgsFeature& feature ) const;
/** Fill the capabilities caching key
* @param cacheKey the list to fill with a cache variant
* @return false if we cant create a cache
*/
bool fillCacheKey( QStringList& cacheKey ) const;
/** Register an access control filter
* @param accessControl the access control to add
* @priority the priority used to define the order
*/
void registerAccessControl( QgsAccessControlFilter* accessControl, int priority = 0 );
private:
/** The AccessControl plugins registry */
QgsAccessControlFilterMap* mPluginsAccessControls;
};
#endif

View File

@ -0,0 +1,87 @@
/***************************************************************************
qgsaccesscontrolplugin.cpp
--------------------------
Access control interface for Qgis Server plugins
begin : 2015-05-19
copyright : (C) 2015 by Stéphane Brunner
email : stephane dot brunner at camptocamp dot org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsaccesscontrolfilter.h"
#include "qgsmessagelog.h"
#include <QString>
#include <QStringList>
/** Constructor */
QgsAccessControlFilter::QgsAccessControlFilter( const QgsServerInterface* serverInterface ):
mServerInterface( serverInterface )
{
}
/** Destructor */
QgsAccessControlFilter::~QgsAccessControlFilter()
{
}
/** Return an additional layer expression filter */
const QString QgsAccessControlFilter::layerFilterExpression( const QgsVectorLayer* layer ) const
{
QgsMessageLog::logMessage( "QgsAccessControlFilter plugin default layerFilterExpression called", "AccessControlFilter", QgsMessageLog::INFO );
Q_UNUSED( layer );
return NULL;
}
/** Return an additional layer subset string (typically SQL) filter */
const QString QgsAccessControlFilter::layerFilterSubsetString( const QgsVectorLayer* layer ) const
{
QgsMessageLog::logMessage( "QgsAccessControlFilter plugin default layerFilterSQL called", "AccessControlFilter", QgsMessageLog::INFO );
Q_UNUSED( layer );
return NULL;
}
/** Return the layer permissions */
const QgsAccessControlFilter::LayerPermissions QgsAccessControlFilter::layerPermissions( const QgsMapLayer* layer ) const
{
QgsMessageLog::logMessage( "QgsAccessControlFilter plugin default layerPermissions called", "AccessControlFilter", QgsMessageLog::INFO );
Q_UNUSED( layer );
LayerPermissions permissions = QgsAccessControlFilter::LayerPermissions();
permissions.canRead = permissions.canUpdate = permissions.canInsert = permissions.canDelete = true;
return permissions;
}
/** Return the authorized layer attributes */
const QStringList* QgsAccessControlFilter::authorizedLayerAttributes( const QgsVectorLayer* layer, const QStringList& attributes ) const
{
Q_UNUSED( layer );
Q_UNUSED( attributes );
QgsMessageLog::logMessage( "QgsAccessControlFilter plugin default authorizedLayerAttributes called", "AccessControlFilter", QgsMessageLog::INFO );
return NULL;
}
/** Are we authorized to modify the feature */
bool QgsAccessControlFilter::allowToEdit( const QgsVectorLayer* layer, const QgsFeature& feature ) const
{
QgsMessageLog::logMessage( "QgsAccessControlFilter plugin default allowToEdit called", "AccessControlFilter", QgsMessageLog::INFO );
Q_UNUSED( layer );
Q_UNUSED( feature );
return true;
}
/** Cache key to used to create the capabilities cache, "" for no cache */
const QString QgsAccessControlFilter::cacheKey() const
{
return "";
}

View File

@ -0,0 +1,115 @@
/***************************************************************************
qgsaccesscontrolfilter.h
------------------------
begin : 2015-05-19
copyright : (C) 2015 by Stéphane Brunner
email : stephane dot brunner at camptocamp dot org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSACCESSCONTROLPLUGIN_H
#define QGSACCESSCONTROLPLUGIN_H
#include <QMultiMap>
#include <QList>
#include <QString>
class QgsServerInterface;
class QgsMapLayer;
class QgsVectorLayer;
class QgsExpression;
class QgsFeature;
/**
* \class QgsAccessControlFilter
* \brief Class defining access control interface for QGIS Server plugins.
*
* Security can define any (or none) of the following method:
* * layerFilterExpression() - To get an additional expression filter (WMS/GetMap, WMS/GetFeatureInfo, WFS/GetFeature)
* * layerFilterSQL() - To get an additional SQL filter (WMS/GetMap, WMS/GetFeatureInfo, WFS/GetFeature) for layer that support SQL
* * layerPermissions() - To give the general layer permissins (read / update / insert / delete)
* * authorizedLayerAttributes() - Tho filter the attributes (WMS/GetFeatureInfo, WFS/GetFeature)
* * allowToEdit() - (all WFS-T requests)
*/
class SERVER_EXPORT QgsAccessControlFilter
{
public:
/** Constructor
* QgsServerInterface passed to plugins constructors
* and must be passed to QgsAccessControlFilter instances.
*/
QgsAccessControlFilter( const QgsServerInterface* serverInterface );
/** Destructor */
virtual ~QgsAccessControlFilter();
/** Describe the layer permission */
struct LayerPermissions {
bool canRead;
bool canUpdate;
bool canInsert;
bool canDelete;
};
/** Return the QgsServerInterface instance */
const QgsServerInterface* serverInterface() const { return mServerInterface; }
/** Return an additional expression filter
* @param layer the layer to control
* @return the filter expression
*/
virtual const QString layerFilterExpression( const QgsVectorLayer* layer ) const;
/** Return an additional subset string (typically SQL) filter
* @param layer the layer to control
* @return the subset string
*/
virtual const QString layerFilterSubsetString( const QgsVectorLayer* layer ) const;
/** Return the layer permissions
* @param layer the layer to control
* @return the permission to use on the layer
*/
virtual const LayerPermissions layerPermissions( const QgsMapLayer* layer ) const;
/** Return the authorized layer attributes
* @param layer the layer to control
* @param attributes the current list of visible attribute
* @return the new list of visible attributes
*/
virtual const QStringList* authorizedLayerAttributes( const QgsVectorLayer* layer, const QStringList& attributes ) const;
/** Are we authorized to modify the following geometry
* @param layer the layer to control
* @param feature the concerned feature
* @return true if we are allowed to edit
*/
virtual bool allowToEdit( const QgsVectorLayer* layer, const QgsFeature& feature ) const;
/** Cache key to used to create the capabilities cache
* @return the cache key, "" for no cache
*/
virtual const QString cacheKey() const;
private:
/** The server interface */
const QgsServerInterface* mServerInterface;
};
/** The registry definition */
typedef QMultiMap<int, QgsAccessControlFilter*> QgsAccessControlFilterMap;
#endif // QGSSERVERSECURITY_H

View File

@ -28,13 +28,13 @@ QgsCapabilitiesCache::~QgsCapabilitiesCache()
{
}
const QDomDocument* QgsCapabilitiesCache::searchCapabilitiesDocument( const QString& configFilePath, const QString& version )
const QDomDocument* QgsCapabilitiesCache::searchCapabilitiesDocument( const QString& configFilePath, const QString& key )
{
QCoreApplication::processEvents(); //get updates from file system watcher
if ( mCachedCapabilities.contains( configFilePath ) && mCachedCapabilities[ configFilePath ].contains( version ) )
if ( mCachedCapabilities.contains( configFilePath ) && mCachedCapabilities[ configFilePath ].contains( key ) )
{
return &mCachedCapabilities[configFilePath][version];
return &mCachedCapabilities[ configFilePath ][ key ];
}
else
{
@ -42,7 +42,7 @@ const QDomDocument* QgsCapabilitiesCache::searchCapabilitiesDocument( const QStr
}
}
void QgsCapabilitiesCache::insertCapabilitiesDocument( const QString& configFilePath, const QString& version, const QDomDocument* doc )
void QgsCapabilitiesCache::insertCapabilitiesDocument( const QString& configFilePath, const QString& key, const QDomDocument* doc )
{
if ( mCachedCapabilities.size() > 40 )
{
@ -58,7 +58,7 @@ void QgsCapabilitiesCache::insertCapabilitiesDocument( const QString& configFile
mCachedCapabilities.insert( configFilePath, QHash<QString, QDomDocument>() );
}
mCachedCapabilities[ configFilePath ].insert( version, doc->cloneNode().toDocument() );
mCachedCapabilities[ configFilePath ].insert( key, doc->cloneNode().toDocument() );
}
void QgsCapabilitiesCache::removeChangedEntry( const QString& path )

View File

@ -31,10 +31,18 @@ class SERVER_EXPORT QgsCapabilitiesCache : public QObject
QgsCapabilitiesCache();
~QgsCapabilitiesCache();
/** Returns cached capabilities document (or 0 if document for configuration file not in cache)*/
const QDomDocument* searchCapabilitiesDocument( const QString& configFilePath, const QString& version );
/** Inserts new capabilities document (creates a copy of the document, does not take ownership)*/
void insertCapabilitiesDocument( const QString& configFilePath, const QString& version, const QDomDocument* doc );
/** Returns cached capabilities document (or 0 if document for configuration file not in cache)
* @param configFilePath the progect file path
* @param key key used to separate different version in different cache
*/
const QDomDocument* searchCapabilitiesDocument( const QString& configFilePath, const QString& key );
/** Inserts new capabilities document (creates a copy of the document, does not take ownership)
* @param configFilePath the progect file path
* @param key key used to separate different version in different cache
* @param doc the DOM document
*/
void insertCapabilitiesDocument( const QString& configFilePath, const QString& key, const QDomDocument* doc );
private:
QHash< QString, QHash< QString, QDomDocument > > mCachedCapabilities;

View File

@ -22,6 +22,7 @@
#include "qgswfsprojectparser.h"
#include "qgswmsprojectparser.h"
#include "qgssldconfigparser.h"
#include "qgsaccesscontrol.h"
#include <QFile>
@ -54,7 +55,12 @@ QgsServerProjectParser* QgsConfigCache::serverConfiguration( const QString& file
return new QgsServerProjectParser( doc, filePath );
}
QgsWCSProjectParser *QgsConfigCache::wcsConfiguration( const QString& filePath )
QgsWCSProjectParser *QgsConfigCache::wcsConfiguration(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
)
{
QgsWCSProjectParser *p = mWCSConfigCache.object( filePath );
if ( !p )
@ -64,7 +70,12 @@ QgsWCSProjectParser *QgsConfigCache::wcsConfiguration( const QString& filePath )
{
return 0;
}
p = new QgsWCSProjectParser( filePath );
p = new QgsWCSProjectParser(
filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
mWCSConfigCache.insert( filePath, p );
p = mWCSConfigCache.object( filePath );
Q_ASSERT( p );
@ -74,7 +85,12 @@ QgsWCSProjectParser *QgsConfigCache::wcsConfiguration( const QString& filePath )
return p;
}
QgsWFSProjectParser *QgsConfigCache::wfsConfiguration( const QString& filePath )
QgsWFSProjectParser *QgsConfigCache::wfsConfiguration(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
)
{
QgsWFSProjectParser *p = mWFSConfigCache.object( filePath );
if ( !p )
@ -84,7 +100,12 @@ QgsWFSProjectParser *QgsConfigCache::wfsConfiguration( const QString& filePath )
{
return 0;
}
p = new QgsWFSProjectParser( filePath );
p = new QgsWFSProjectParser(
filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
mWFSConfigCache.insert( filePath, p );
p = mWFSConfigCache.object( filePath );
Q_ASSERT( p );
@ -94,7 +115,13 @@ QgsWFSProjectParser *QgsConfigCache::wfsConfiguration( const QString& filePath )
return p;
}
QgsWMSConfigParser *QgsConfigCache::wmsConfiguration( const QString& filePath, const QMap<QString, QString>& parameterMap )
QgsWMSConfigParser *QgsConfigCache::wmsConfiguration(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
, const QMap<QString, QString>& parameterMap
)
{
QgsWMSConfigParser *p = mWMSConfigCache.object( filePath );
if ( !p )
@ -114,7 +141,12 @@ QgsWMSConfigParser *QgsConfigCache::wmsConfiguration( const QString& filePath, c
}
else
{
p = new QgsWMSProjectParser( filePath );
p = new QgsWMSProjectParser(
filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
}
mWMSConfigCache.insert( filePath, p );
p = mWMSConfigCache.object( filePath );

View File

@ -18,6 +18,8 @@
#ifndef QGSCONFIGCACHE_H
#define QGSCONFIGCACHE_H
#include "qgsconfig.h"
#include <QCache>
#include <QFileSystemWatcher>
#include <QMap>
@ -27,6 +29,7 @@ class QgsServerProjectParser;
class QgsWCSProjectParser;
class QgsWFSProjectParser;
class QgsWMSConfigParser;
class QgsAccessControl;
class QDomDocument;
@ -38,9 +41,25 @@ class SERVER_EXPORT QgsConfigCache : public QObject
~QgsConfigCache();
QgsServerProjectParser* serverConfiguration( const QString& filePath );
QgsWCSProjectParser* wcsConfiguration( const QString& filePath );
QgsWFSProjectParser* wfsConfiguration( const QString& filePath );
QgsWMSConfigParser* wmsConfiguration( const QString& filePath, const QMap<QString, QString>& parameterMap = ( QMap< QString, QString >() ) );
QgsWCSProjectParser* wcsConfiguration(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
);
QgsWFSProjectParser* wfsConfiguration(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
);
QgsWMSConfigParser* wmsConfiguration(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
, const QMap<QString, QString>& parameterMap = ( QMap< QString, QString >() )
);
private:
QgsConfigCache();

View File

@ -678,6 +678,10 @@ QString QgsHttpRequestHandler::readPostBody() const
QgsMessageLog::logMessage( "could not convert CONTENT_LENGTH to int" );
}
}
// Used by the tests
else if ( getenv( "REQUEST_BODY" ) != NULL ) {
inputString = getenv( "REQUEST_BODY" );
}
return inputString;
}

View File

@ -0,0 +1,72 @@
/***************************************************************************
qgsowsserver.cpp
-------------------
begin : February 27, 2012
copyright : (C) 2012 by René-Luc D'Hont & Marco Hugentobler
email : rldhont at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsowsserver.h"
#include "qgsmaplayerregistry.h"
#include "qgsmessagelog.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
/** Apply filter from AccessControl */
void QgsOWSServer::applyAccessControlLayerFilters( QgsMapLayer* mapLayer, QMap<QString, QString>& originalLayerFilters ) const
{
if ( QgsVectorLayer* layer = dynamic_cast<QgsVectorLayer*>( mapLayer ) )
{
if ( layer->setSubsetString( "" ) )
{
QString sql = mAccessControl->extraSubsetString( layer );
if ( sql != NULL )
{
if ( !originalLayerFilters.contains( layer->id() ) )
{
originalLayerFilters.insert( layer->id(), layer->subsetString() );
}
if ( !layer->subsetString().isEmpty() )
{
sql.prepend( " AND " );
sql.prepend( layer->subsetString() );
}
layer->setSubsetString( sql );
}
}
else
{
QgsMessageLog::logMessage( "Layer does not support Subset String" );
}
}
}
#endif
/** Restore layer filter as original */
void QgsOWSServer::restoreLayerFilters( const QMap<QString, QString>& filterMap ) const
{
QMap<QString, QString>::const_iterator filterIt = filterMap.constBegin();
for ( ; filterIt != filterMap.constEnd(); ++filterIt )
{
QgsVectorLayer* filteredLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( filterIt.key() ) );
if ( filteredLayer )
{
QgsVectorDataProvider* dp = filteredLayer->dataProvider();
if ( dp )
{
dp->setSubsetString( filterIt.value() );
}
}
}
}

View File

@ -19,12 +19,28 @@
#define QGSOWSSERVER_H
#include "qgsrequesthandler.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsaccesscontrol.h"
#endif
class QgsOWSServer
{
public:
QgsOWSServer( const QString& configFilePath, const QMap<QString, QString>& parameters, QgsRequestHandler* rh )
: mParameters( parameters ), mRequestHandler( rh ), mConfigFilePath( configFilePath ) {}
QgsOWSServer(
const QString& configFilePath
, const QMap<QString, QString>& parameters
, QgsRequestHandler* rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* ac
#endif
)
: mParameters( parameters )
, mRequestHandler( rh )
, mConfigFilePath( configFilePath )
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl( ac )
#endif
{}
virtual ~QgsOWSServer() {}
virtual void executeRequest() = 0;
@ -36,6 +52,22 @@ class QgsOWSServer
QMap<QString, QString> mParameters;
QgsRequestHandler* mRequestHandler;
QString mConfigFilePath;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
/** The access control helper */
const QgsAccessControl* mAccessControl;
/** Apply filter strings from the access control to the layers.
* @param layer the concerned layer
* @param originalLayerFilters the original layer filter
*
*/
void applyAccessControlLayerFilters( QgsMapLayer* layer, QMap<QString, QString>& originalLayerFilters ) const;
#endif
/** Restores the original layer filters
* @param filterMap the original layer filter
*/
void restoreLayerFilters( const QMap < QString, QString >& filterMap ) const;
};
#endif // QGSOWSSERVER_H

View File

@ -39,6 +39,9 @@
#include "qgsmaplayerregistry.h"
#include "qgsserverlogger.h"
#include "qgseditorwidgetregistry.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsaccesscontrolfilter.h"
#endif
#include <QDomDocument>
#include <QNetworkDiskCache>
@ -486,6 +489,10 @@ QPair<QByteArray, QByteArray> QgsServer::handleRequest( const QString& queryStri
// Copy the parameters map
QMap<QString, QString> parameterMap( theRequestHandler->parameterMap() );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
const QgsAccessControl* accessControl = NULL;
accessControl = mServerInterface->accessControls();
#endif
printRequestParameters( parameterMap, logLevel );
QMap<QString, QString>::const_iterator paramIt;
@ -520,40 +527,81 @@ QPair<QByteArray, QByteArray> QgsServer::handleRequest( const QString& queryStri
{
if ( serviceString == "WCS" )
{
QgsWCSProjectParser* p = QgsConfigCache::instance()->wcsConfiguration( configFilePath );
QgsWCSProjectParser* p = QgsConfigCache::instance()->wcsConfiguration(
configFilePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
if ( !p )
{
theRequestHandler->setServiceException( QgsMapServiceException( "Project file error", "Error reading the project file" ) );
}
else
{
QgsWCSServer wcsServer( configFilePath, parameterMap, p, theRequestHandler.data() );
QgsWCSServer wcsServer(
configFilePath
, parameterMap
, p
, theRequestHandler.data()
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
wcsServer.executeRequest();
}
}
else if ( serviceString == "WFS" )
{
QgsWFSProjectParser* p = QgsConfigCache::instance()->wfsConfiguration( configFilePath );
QgsWFSProjectParser* p = QgsConfigCache::instance()->wfsConfiguration(
configFilePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
if ( !p )
{
theRequestHandler->setServiceException( QgsMapServiceException( "Project file error", "Error reading the project file" ) );
}
else
{
QgsWFSServer wfsServer( configFilePath, parameterMap, p, theRequestHandler.data() );
QgsWFSServer wfsServer(
configFilePath
, parameterMap
, p
, theRequestHandler.data()
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
wfsServer.executeRequest();
}
}
else if ( serviceString == "WMS" )
{
QgsWMSConfigParser* p = QgsConfigCache::instance()->wmsConfiguration( configFilePath, parameterMap );
QgsWMSConfigParser* p = QgsConfigCache::instance()->wmsConfiguration(
configFilePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
if ( !p )
{
theRequestHandler->setServiceException( QgsMapServiceException( "WMS configuration error", "There was an error reading the project file or the SLD configuration" ) );
}
else
{
QgsWMSServer wmsServer( configFilePath, parameterMap, p, theRequestHandler.data(), mMapRenderer, mCapabilitiesCache );
QgsWMSServer wmsServer(
configFilePath
, parameterMap
, p
, theRequestHandler.data()
, mMapRenderer
, mCapabilitiesCache
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
);
wmsServer.executeRequest();
}
}

View File

@ -18,11 +18,13 @@
#include "qgsserverinterface.h"
/** Constructor */
QgsServerInterface::QgsServerInterface():
mConfigFilePath( QString() )
{
}
/** Destructor */
QgsServerInterface::~QgsServerInterface()
{
}

View File

@ -23,6 +23,8 @@
#include "qgscapabilitiescache.h"
#include "qgsrequesthandler.h"
#include "qgsserverfilter.h"
#include "qgsaccesscontrolfilter.h"
#include "qgsaccesscontrol.h"
/**
* \ingroup server
@ -45,6 +47,7 @@ class SERVER_EXPORT QgsServerInterface
/**
* Set the request handler
* @param requestHandler request handler
* @note not available in Python bindings
*/
virtual void setRequestHandler( QgsRequestHandler* requestHandler ) = 0;
@ -85,6 +88,13 @@ class SERVER_EXPORT QgsServerInterface
* @return QgsServerFiltersMap list of QgsServerFilter
*/
virtual QgsServerFiltersMap filters( ) = 0;
/** Register an access control filter
* @param accessControl the access control to register
* @param priority the priority used to order them
*/
virtual void registerAccessControl( QgsAccessControlFilter* accessControl, int priority = 0 ) = 0;
/** Gets the registred access control filters */
virtual const QgsAccessControl* accessControls( ) const = 0;
//! Return an enrironment variable, used to pass environment variables to python
virtual QString getEnv( const QString& name ) const = 0;

View File

@ -20,10 +20,12 @@
#include "qgsserverinterfaceimpl.h"
/** Constructor */
QgsServerInterfaceImpl::QgsServerInterfaceImpl( QgsCapabilitiesCache* capCache ) :
mCapabilitiesCache( capCache )
{
mRequestHandler = NULL;
mAccessControls = new QgsAccessControl();
}
@ -36,6 +38,7 @@ QString QgsServerInterfaceImpl::getEnv( const QString& name ) const
/** Destructor */
QgsServerInterfaceImpl::~QgsServerInterfaceImpl()
{
delete mAccessControls;
}
@ -63,3 +66,9 @@ void QgsServerInterfaceImpl::setFilters( QgsServerFiltersMap* filters )
{
mFilters = *filters;
}
/** Register a new access control filter */
void QgsServerInterfaceImpl::registerAccessControl( QgsAccessControlFilter* accessControl, int priority )
{
mAccessControls->registerAccessControl( accessControl, priority );
}

View File

@ -51,6 +51,12 @@ class QgsServerInterfaceImpl : public QgsServerInterface
QgsRequestHandler* requestHandler( ) override { return mRequestHandler; }
void registerFilter( QgsServerFilter *filter, int priority = 0 ) override;
QgsServerFiltersMap filters( ) override { return mFilters; }
/** Register an access control filter */
void registerAccessControl( QgsAccessControlFilter *accessControl, int priority = 0 ) override;
/** Gets the helper over all the registered access control filters
* @return the access control helper
*/
const QgsAccessControl* accessControls( ) const override { return mAccessControls; }
QString getEnv( const QString& name ) const override;
QString configFilePath( ) override { return mConfigFilePath; }
void setConfigFilePath( const QString& configFilePath ) override;
@ -60,6 +66,7 @@ class QgsServerInterfaceImpl : public QgsServerInterface
QString mConfigFilePath;
QgsServerFiltersMap mFilters;
QgsAccessControl* mAccessControls;
QgsCapabilitiesCache* mCapabilitiesCache;
QgsRequestHandler* mRequestHandler;

View File

@ -18,7 +18,9 @@
#ifndef QGSSERVERPROJECTPARSER_H
#define QGSSERVERPROJECTPARSER_H
#include "qgsconfig.h"
#include "qgsvectorlayer.h"
#include <QDomElement>
#include <QHash>
#include <QMap>

View File

@ -19,8 +19,22 @@
#include "qgsconfigcache.h"
#include "qgsconfigparserutils.h"
#include "qgsrasterlayer.h"
#include "qgsmapserviceexception.h"
#include "qgsmessagelog.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsaccesscontrol.h"
#endif
QgsWCSProjectParser::QgsWCSProjectParser( const QString& filePath )
QgsWCSProjectParser::QgsWCSProjectParser(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* as
#endif
)
#ifdef HAVE_SERVER_PYTHON_PLUGINS
: mAccessControl( as )
#endif
{
mProjectParser = QgsConfigCache::instance()->serverConfiguration( filePath );
}
@ -81,6 +95,12 @@ void QgsWCSProjectParser::wcsContentMetadata( QDomElement& parentElement, QDomDo
QgsMapLayer *layer = mProjectParser->createLayerFromElement( elem );
if ( layer && wcsLayersId.contains( layer->id() ) )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( layer ) )
{
continue;
}
#endif
QgsDebugMsg( QString( "add layer %1 to map" ).arg( layer->id() ) );
layerMap.insert( layer->id(), layer );
@ -201,6 +221,14 @@ void QgsWCSProjectParser::describeCoverage( const QString& aCoveName, QDomElemen
QgsRasterLayer *rLayer = dynamic_cast<QgsRasterLayer *>( mProjectParser->createLayerFromElement( elem ) );
if ( !rLayer )
continue;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( rLayer ) )
{
continue;
}
#endif
QString coveName = rLayer->name();
coveName = coveName.replace( " ", "_" );
if ( wcsLayersId.contains( rLayer->id() ) && ( aCoveName == "" || coveNameList.contains( coveName ) ) )

View File

@ -20,10 +20,19 @@
#include "qgsserverprojectparser.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
class QgsAccessControl;
#endif
class SERVER_EXPORT QgsWCSProjectParser
{
public:
QgsWCSProjectParser( const QString& filePath );
QgsWCSProjectParser(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* ac
#endif
);
~QgsWCSProjectParser();
void serviceCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
@ -36,6 +45,9 @@ class SERVER_EXPORT QgsWCSProjectParser
private:
QgsServerProjectParser* mProjectParser;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
const QgsAccessControl* mAccessControl;
#endif
};
#endif // QGSWCSPROJECTPARSER_H

View File

@ -23,6 +23,7 @@
#include "qgsrasterfilewriter.h"
#include "qgslogger.h"
#include "qgsmapserviceexception.h"
#include "qgsaccesscontrol.h"
#include <QTemporaryFile>
#include <QUrl>
@ -37,15 +38,38 @@ static const QString WCS_NAMESPACE = "http://www.opengis.net/wcs";
static const QString GML_NAMESPACE = "http://www.opengis.net/gml";
static const QString OGC_NAMESPACE = "http://www.opengis.net/ogc";
QgsWCSServer::QgsWCSServer( const QString& configFilePath, QMap<QString, QString> &parameters, QgsWCSProjectParser* pp,
QgsRequestHandler* rh )
: QgsOWSServer( configFilePath, parameters, rh )
, mConfigParser( pp )
QgsWCSServer::QgsWCSServer(
const QString& configFilePath
, QMap<QString, QString> &parameters
, QgsWCSProjectParser* pp
, QgsRequestHandler* rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
)
: QgsOWSServer(
configFilePath
, parameters
, rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
)
, mConfigParser(
pp
)
{
}
QgsWCSServer::QgsWCSServer()
: QgsOWSServer( QString(), QMap<QString, QString>(), 0 )
: QgsOWSServer(
QString()
, QMap<QString, QString>()
, 0
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, NULL
#endif
)
, mConfigParser( 0 )
{
}
@ -353,6 +377,13 @@ QByteArray* QgsWCSServer::getCoverage()
QgsRasterLayer* rLayer = dynamic_cast<QgsRasterLayer*>( layer );
if ( rLayer && wcsLayersId.contains( rLayer->id() ) )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( rLayer ) )
{
throw QgsMapServiceException( "Security", "You are not allowed to access to this coverage" );
}
#endif
// RESPONSE_CRS
QgsCoordinateReferenceSystem responseCRS = rLayer->crs();
crs = mParameters.value( "RESPONSE_CRS", "" );

View File

@ -36,8 +36,15 @@ class QgsWCSServer: public QgsOWSServer
{
public:
/** Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWCSServer( const QString& configFilePath, QMap<QString, QString>& parameters, QgsWCSProjectParser* pp,
QgsRequestHandler* rh );
QgsWCSServer(
const QString& configFilePath
, QMap<QString, QString>& parameters
, QgsWCSProjectParser* pp
, QgsRequestHandler* rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
);
~QgsWCSServer();
void executeRequest() override;

View File

@ -20,8 +20,18 @@
#include "qgsconfigparserutils.h"
#include "qgsmaplayerregistry.h"
#include "qgsvectordataprovider.h"
#include "qgsmapserviceexception.h"
#include "qgsaccesscontrol.h"
QgsWFSProjectParser::QgsWFSProjectParser( const QString& filePath )
QgsWFSProjectParser::QgsWFSProjectParser(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* ac
#endif
)
#ifdef HAVE_SERVER_PYTHON_PLUGINS
: mAccessControl( ac )
#endif
{
mProjectParser = QgsConfigCache::instance()->serverConfiguration( filePath );
}
@ -76,6 +86,12 @@ void QgsWFSProjectParser::featureTypeList( QDomElement& parentElement, QDomDocum
{
continue;
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( layer ) )
{
continue;
}
#endif
QgsDebugMsg( QString( "add layer %1 to map" ).arg( layer->id() ) );
layerMap.insert( layer->id(), layer );
@ -331,6 +347,13 @@ void QgsWFSProjectParser::describeFeatureType( const QString& aTypeName, QDomEle
if ( !layer )
continue;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( layer ) )
{
continue;
}
#endif
QString typeName = layer->name();
typeName = typeName.replace( " ", "_" );

View File

@ -20,10 +20,21 @@
#include "qgsserverprojectparser.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
class QgsAccessControl;
#endif
class SERVER_EXPORT QgsWFSProjectParser
{
public:
QgsWFSProjectParser( const QString& filePath );
QgsWFSProjectParser(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* ac
#endif
);
~QgsWFSProjectParser();
void serviceCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
@ -45,6 +56,9 @@ class SERVER_EXPORT QgsWFSProjectParser
private:
QgsServerProjectParser* mProjectParser;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
const QgsAccessControl* mAccessControl;
#endif
};
#endif // QGSWFSPROJECTPARSER_H

View File

@ -38,6 +38,7 @@
#include "qgscomposerlegenditem.h"
#include "qgsrequesthandler.h"
#include "qgsogcutils.h"
#include "qgsaccesscontrol.h"
#include <QImage>
#include <QPainter>
@ -65,16 +66,37 @@ static const QString GML_NAMESPACE = "http://www.opengis.net/gml";
static const QString OGC_NAMESPACE = "http://www.opengis.net/ogc";
static const QString QGS_NAMESPACE = "http://www.qgis.org/gml";
QgsWFSServer::QgsWFSServer( const QString& configFilePath, QMap<QString, QString> &parameters, QgsWFSProjectParser* cp,
QgsRequestHandler* rh )
: QgsOWSServer( configFilePath, parameters, rh )
QgsWFSServer::QgsWFSServer(
const QString& configFilePath
, QMap<QString, QString> &parameters
, QgsWFSProjectParser* cp
, QgsRequestHandler* rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
)
: QgsOWSServer(
configFilePath
, parameters
, rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
)
, mWithGeom( true )
, mConfigParser( cp )
{
}
QgsWFSServer::QgsWFSServer()
: QgsOWSServer( QString(), QMap<QString, QString>(), 0 )
: QgsOWSServer(
QString()
, QMap<QString, QString>()
, 0
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, NULL
#endif
)
, mWithGeom( true )
, mConfigParser( 0 )
{
@ -438,6 +460,16 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
QgsVectorLayer* layer = dynamic_cast<QgsVectorLayer*>( currentLayer );
if ( layer && wfsLayersId.contains( layer->id() ) )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( currentLayer ) )
{
throw QgsMapServiceException( "Security", "Feature access permission denied" );
}
QMap<QString, QString> originalLayerFilters;
applyAccessControlLayerFilters( currentLayer, originalLayerFilters );
#endif
expressionContext << QgsExpressionContextUtils::layerScope( layer );
//is there alias info for this vector layer?
@ -513,10 +545,21 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
, searchRect.yMaximum() + 1. / pow( 10., layerPrec ) );
layerCrs = layer->crs();
QgsFeatureIterator fit = layer->getFeatures(
QgsFeatureRequest()
.setFlags( QgsFeatureRequest::ExactIntersect | ( mWithGeom ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) )
.setSubsetOfAttributes( attrIndexes ) );
QgsFeatureRequest fReq;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
fReq.setFlags( QgsFeatureRequest::ExactIntersect | ( mWithGeom ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) );
mAccessControl->filterFeatures( layer, fReq );
QStringList attributes = QStringList();
foreach( int idx, attrIndexes ) {
attributes.append( layer->pendingFields().field(idx).name() );
}
fReq.setSubsetOfAttributes(
mAccessControl->layerAttributes( layer, attributes ),
layer->pendingFields() );
#endif
QgsFeatureIterator fit = layer->getFeatures( fReq );
long featCounter = 0;
QDomNodeList filterNodes = queryElem.elementsByTagName( "Filter" );
@ -634,6 +677,10 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
++featureCounter;
}
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
restoreLayerFilters( originalLayerFilters );
#endif
}
else
{
@ -1394,12 +1441,37 @@ QDomDocument QgsWFSServer::transaction( const QString& requestBody )
// it's a vectorlayer and defined by the administrator as a WFS layer
if ( layer && wfsLayersId.contains( layer->id() ) )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( actionName == "Insert" )
{
if ( !mAccessControl->layerInsertPermission( layer ) )
{
throw QgsMapServiceException( "Security", "Feature insert permission denied" );
}
}
else if ( actionName == "Update" )
{
if ( !mAccessControl->layerUpdatePermission( layer ) )
{
throw QgsMapServiceException( "Security", "Feature update permission denied" );
}
}
else if ( actionName == "Delete" )
{
if ( !mAccessControl->layerDeletePermission( layer ) )
{
throw QgsMapServiceException( "Security", "Feature delete permission denied" );
}
}
#endif
// Get the provider and it's capabilities
QgsVectorDataProvider* provider = layer->dataProvider();
if ( !provider )
{
continue;
}
int cap = provider->capabilities();
// Start the update transaction
@ -1459,6 +1531,18 @@ QDomDocument QgsWFSServer::transaction( const QString& requestBody )
QgsFeatureIds::const_iterator fidIt = fids.constBegin();
for ( ; fidIt != fids.constEnd(); ++fidIt )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsFeatureIterator fit = layer->getFeatures(QgsFeatureRequest( *fidIt ) );
QgsFeature feature;
while ( fit.nextFeature( feature ) )
{
if ( !mAccessControl->allowToEdit( layer, feature ) )
{
throw QgsMapServiceException( "Security", "Feature modify permission denied" );
}
}
#endif
QMap< QString, QString >::const_iterator it = propertyMap.constBegin();
for ( ; it != propertyMap.constEnd(); ++it )
{
@ -1482,6 +1566,18 @@ QDomDocument QgsWFSServer::transaction( const QString& requestBody )
if ( !layer->changeGeometry( *fidIt, QgsOgcUtils::geometryFromGML( geometryElem ) ) )
throw QgsMapServiceException( "RequestNotWellFormed", "Error in change geometry" );
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
fit = layer->getFeatures(QgsFeatureRequest( *fidIt ) );
while ( fit.nextFeature( feature ) )
{
if ( !mAccessControl->allowToEdit( layer, feature ) )
{
layer->rollBack();
throw QgsMapServiceException( "Security", "Feature modify permission denied" );
}
}
#endif
}
}
}
@ -1512,6 +1608,23 @@ QDomDocument QgsWFSServer::transaction( const QString& requestBody )
QDomElement filterElem = actionElem.firstChild().toElement();
// Get Feature Ids for the Filter element
QgsFeatureIds fids = getFeatureIdsFromFilter( filterElem, layer );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsFeatureIds::const_iterator fidIt = fids.constBegin();
for ( ; fidIt != fids.constEnd(); ++fidIt )
{
QgsFeatureIterator fit = layer->getFeatures(QgsFeatureRequest( *fidIt ) );
QgsFeature feature;
while ( fit.nextFeature( feature ) )
{
if ( !mAccessControl->allowToEdit( layer, feature ) )
{
throw QgsMapServiceException( "Security", "Feature modify permission denied" );
}
}
}
#endif
layer->setSelectedFeatures( fids );
layer->deleteSelectedFeatures();
}
@ -1595,6 +1708,18 @@ QDomDocument QgsWFSServer::transaction( const QString& requestBody )
}
}
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsFeatureList::iterator featureIt = inFeatList.begin();
while ( featureIt != inFeatList.end() )
{
if ( !mAccessControl->allowToEdit( layer, *featureIt) )
{
throw QgsMapServiceException( "Security", "Feature modify permission denied" );
}
featureIt++;
}
#endif
// add the features
if ( !provider->addFeatures( inFeatList ) )
{

View File

@ -60,8 +60,15 @@ class QgsWFSServer: public QgsOWSServer
{
public:
/** Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWFSServer( const QString& configFilePath, QMap<QString, QString>& parameters, QgsWFSProjectParser* cp,
QgsRequestHandler* rh );
QgsWFSServer(
const QString& configFilePath
, QMap<QString, QString>& parameters
, QgsWFSProjectParser* cp
, QgsRequestHandler* rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
);
~QgsWFSServer();
void executeRequest() override;

View File

@ -40,6 +40,7 @@
#include "qgscomposershape.h"
#include "qgslayertreegroup.h"
#include "qgslayertreelayer.h"
#include "qgsaccesscontrol.h"
#include <QFileInfo>
#include <QTextDocument>
@ -48,8 +49,16 @@
// this implies that a layer style called "default" will not be usable in WMS server
#define EMPTY_STYLE_NAME "default"
QgsWMSProjectParser::QgsWMSProjectParser( const QString& filePath )
QgsWMSProjectParser::QgsWMSProjectParser(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
)
: QgsWMSConfigParser()
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl( accessControl )
#endif
{
mProjectParser = QgsConfigCache::instance()->serverConfiguration( filePath );
mLegendLayerFont.fromString( mProjectParser->firstComposerLegendElement().attribute( "layerFont" ) );
@ -185,7 +194,12 @@ QList<QgsMapLayer*> QgsWMSProjectParser::mapLayerFromStyle( const QString& lName
if ( legendIt->attribute( "embedded" ) == "1" )
{
QString project = mProjectParser->convertToAbsolutePath( legendIt->attribute( "project" ) );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration( project ) );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration(
project
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl
#endif
) );
if ( p )
{
QgsServerProjectParser* pp = p->mProjectParser;
@ -229,7 +243,12 @@ void QgsWMSProjectParser::addLayersFromGroup( const QDomElement& legendGroupElem
int drawingOrder = mProjectParser->updateLegendDrawingOrder() ? legendGroupElem.attribute( "drawingOrder", "-1" ).toInt() : -1;
QString project = mProjectParser->convertToAbsolutePath( legendGroupElem.attribute( "project" ) );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration( project ) );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration(
project
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl
#endif
) );
if ( p )
{
QgsServerProjectParser* pp = p->mProjectParser;
@ -945,7 +964,12 @@ void QgsWMSProjectParser::addLayers( QDomDocument &doc,
QString project = mProjectParser->convertToAbsolutePath( currentChildElem.attribute( "project" ) );
QgsDebugMsg( QString( "Project path: %1" ).arg( project ) );
QString embeddedGroupName = currentChildElem.attribute( "name" );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration( project ) );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration(
project
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl
#endif
) );
if ( p )
{
QgsServerProjectParser* pp = p->mProjectParser;
@ -1001,6 +1025,13 @@ void QgsWMSProjectParser::addLayers( QDomDocument &doc,
{
continue;
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( currentLayer ) )
{
continue;
}
#endif
// queryable layer
if ( nonIdentifiableLayers.contains( currentLayer->id() ) )
{
@ -1273,7 +1304,12 @@ void QgsWMSProjectParser::addOWSLayers( QDomDocument &doc,
QString project = mProjectParser->convertToAbsolutePath( currentChildElem.attribute( "project" ) );
QgsDebugMsg( QString( "Project path: %1" ).arg( project ) );
QString embeddedGroupName = currentChildElem.attribute( "name" );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration( project ) );
QgsWMSProjectParser* p = dynamic_cast<QgsWMSProjectParser*>( QgsConfigCache::instance()->wmsConfiguration(
project
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl
#endif
) );
if ( p )
{
QgsServerProjectParser* pp = p->mProjectParser;
@ -1640,6 +1676,14 @@ QDomDocument QgsWMSProjectParser::describeLayer( QStringList& layerList, const Q
for ( int j = 0; j < currentLayerList.size(); j++ )
{
QgsMapLayer* currentLayer = currentLayerList.at( j );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( currentLayer ) )
{
throw QgsMapServiceException( "Security", "You are not allowed to access to this layer" );
}
#endif
QString layerTypeName = mProjectParser->useLayerIDs() ? currentLayer->id() : currentLayer->name();
// Create the NamedLayer element

View File

@ -22,13 +22,22 @@
#include "qgsserverprojectparser.h"
#include "qgslayertreegroup.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
class QgsAccessControl;
#endif
class QTextDocument;
class QSvgRenderer;
class SERVER_EXPORT QgsWMSProjectParser : public QgsWMSConfigParser
{
public:
QgsWMSProjectParser( const QString& filePath );
QgsWMSProjectParser(
const QString& filePath
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
);
virtual ~QgsWMSProjectParser();
/** Adds layer and style specific capabilities elements to the parent node. This includes the individual layers and styles, their description, native CRS, bounding boxes, etc.
@ -114,6 +123,9 @@ class SERVER_EXPORT QgsWMSProjectParser : public QgsWMSConfigParser
private:
QgsServerProjectParser* mProjectParser;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
const QgsAccessControl* mAccessControl;
#endif
mutable QFont mLegendLayerFont;
mutable QFont mLegendItemFont;

View File

@ -48,6 +48,8 @@
#include "qgsfeature.h"
#include "qgseditorwidgetregistry.h"
#include "qgsserverstreamingdevice.h"
#include "qgsaccesscontrol.h"
#include "qgsfeaturerequest.h"
#include <QImage>
#include <QPainter>
@ -64,9 +66,25 @@
#include <QUrl>
#include <QPaintEngine>
QgsWMSServer::QgsWMSServer( const QString& configFilePath, QMap<QString, QString> &parameters, QgsWMSConfigParser* cp,
QgsRequestHandler* rh, QgsMapRenderer* renderer, QgsCapabilitiesCache* capCache )
: QgsOWSServer( configFilePath, parameters, rh )
QgsWMSServer::QgsWMSServer(
const QString& configFilePath
, QMap<QString, QString> &parameters
, QgsWMSConfigParser* cp
, QgsRequestHandler* rh
, QgsMapRenderer* renderer
, QgsCapabilitiesCache* capCache
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
)
: QgsOWSServer(
configFilePath
, parameters
, rh
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, accessControl
#endif
)
, mMapRenderer( renderer )
, mCapabilitiesCache( capCache )
, mConfigParser( cp )
@ -74,10 +92,20 @@ QgsWMSServer::QgsWMSServer( const QString& configFilePath, QMap<QString, QString
, mDrawLegendLayerLabel( true )
, mDrawLegendItemLabel( true )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
mMapRenderer->setFeatureFilterProvider( mAccessControl );
#endif
}
QgsWMSServer::QgsWMSServer()
: QgsOWSServer( QString(), QMap<QString, QString>(), 0 )
: QgsOWSServer(
QString()
, QMap<QString, QString>()
, 0
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, NULL
#endif
)
, mMapRenderer( 0 )
, mCapabilitiesCache()
, mConfigParser( 0 )
@ -129,7 +157,14 @@ void QgsWMSServer::executeRequest()
//GetCapabilities
if ( request.compare( "GetCapabilities", Qt::CaseInsensitive ) == 0 || getProjectSettings )
{
const QDomDocument* capabilitiesDocument = mCapabilitiesCache->searchCapabilitiesDocument( mConfigFilePath, getProjectSettings ? "projectSettings" : version );
QStringList cacheKeyList;
cacheKeyList << (getProjectSettings ? "projectSettings" : version);
bool cache = true;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
cache = mAccessControl->fillCacheKey( cacheKeyList );
#endif
QString cacheKey = cacheKeyList.join( "-" );
const QDomDocument* capabilitiesDocument = mCapabilitiesCache->searchCapabilitiesDocument( mConfigFilePath, cacheKey );
if ( !capabilitiesDocument ) //capabilities xml not in cache. Create a new one
{
QgsMessageLog::logMessage( "Capabilities document not found in cache" );
@ -144,8 +179,16 @@ void QgsWMSServer::executeRequest()
cleanupAfterRequest();
return;
}
mCapabilitiesCache->insertCapabilitiesDocument( mConfigFilePath, getProjectSettings ? "projectSettings" : version, &doc );
capabilitiesDocument = mCapabilitiesCache->searchCapabilitiesDocument( mConfigFilePath, getProjectSettings ? "projectSettings" : version );
if ( cache )
{
mCapabilitiesCache->insertCapabilitiesDocument( mConfigFilePath, cacheKey, &doc );
capabilitiesDocument = mCapabilitiesCache->searchCapabilitiesDocument( mConfigFilePath, cacheKey );
}
else
{
doc = doc.cloneNode().toDocument();
capabilitiesDocument = &doc;
}
}
else
{
@ -857,6 +900,14 @@ QImage* QgsWMSServer::getLegendGraphics()
if ( QgsLayerTree::isLayer( node ) )
{
QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( nodeLayer->layer() ) )
{
throw QgsMapServiceException( "Security", "You are not allowed to access to the layer: " + nodeLayer->layer()->name() );
}
#endif
// layer titles - hidden or not
QgsLegendRenderer::setNodeLegendStyle( nodeLayer, mDrawLegendLayerLabel ? QgsComposerLegendStyle::Subgroup : QgsComposerLegendStyle::Hidden );
@ -1177,6 +1228,19 @@ QByteArray* QgsWMSServer::getPrint( const QString& formatString )
delete theImage;
QMap<QString, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
foreach ( QgsMapLayer *layer, QgsMapLayerRegistry::instance()->mapLayers() )
{
if ( !mAccessControl->layerReadPermission( layer ) )
{
throw QgsMapServiceException( "Security", "You are not allowed to access to the layer: " + layer->name() );
}
}
applyAccessControlLayersFilters( layersList, originalLayerFilters );
#endif
QStringList selectedLayerIdList = applyFeatureSelections( layersList );
//GetPrint request needs a template parameter
@ -1298,6 +1362,19 @@ QImage* QgsWMSServer::getMap( HitTest* hitTest )
thePainter.setRenderHint( QPainter::Antialiasing ); //make it look nicer
QMap<QString, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
foreach ( QgsMapLayer *layer, QgsMapLayerRegistry::instance()->mapLayers() )
{
if ( !mAccessControl->layerReadPermission( layer ) )
{
throw QgsMapServiceException( "Security", "You are not allowed to access to the layer: " + layer->name() );
}
}
applyAccessControlLayersFilters( layersList, originalLayerFilters );
#endif
QStringList selectedLayerIdList = applyFeatureSelections( layersList );
QList< QPair< QgsVectorLayer*, QgsFeatureRendererV2*> > bkVectorRenderers;
@ -1310,7 +1387,9 @@ QImage* QgsWMSServer::getMap( HitTest* hitTest )
if ( hitTest )
runHitTest( &thePainter, *hitTest );
else
{
mMapRenderer->render( &thePainter );
}
if ( mConfigParser )
{
@ -1518,6 +1597,9 @@ 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() );
QMap<QString, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
applyAccessControlLayersFilters( layersList, originalLayerFilters );
#endif
QDomElement getFeatureInfoElement;
QString infoFormat = mParameters.value( "INFO_FORMAT" );
@ -1595,6 +1677,13 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result, const QString& version )
currentLayer = registeredMapLayer;
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( !mAccessControl->layerReadPermission( currentLayer ) )
{
throw QgsMapServiceException( "Security", "You are not allowed to access to the layer: " + currentLayer->name() );
}
#endif
//skip layer if not visible at current map scale
bool useScaleConstraint = ( scaleDenominator > 0 && currentLayer->hasScaleBasedVisibility() );
if ( useScaleConstraint && ( currentLayer->minimumScale() > scaleDenominator || currentLayer->maximumScale() < scaleDenominator ) )
@ -2055,10 +2144,36 @@ int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer,
QgsFeatureRequest fReq;
bool hasGeometry = addWktGeometry || featureBBox;
fReq.setFlags((( hasGeometry ) ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) | QgsFeatureRequest::ExactIntersect );
if ( !searchRect.isEmpty() )
#ifdef HAVE_SERVER_PYTHON_PLUGINS
mAccessControl->filterFeatures( layer, fReq );
if ( ! searchRect.isEmpty() )
{
if ( fReq.filterExpression() != NULL )
{
fReq.setFilterExpression( QString("intersects( $geometry , geomFromWKT( '%1' ) ) AND ( %2 )").
arg( searchRect.asWktPolygon() ).arg( fReq.filterExpression()->expression() ) );
}
else
{
fReq.setFilterRect( searchRect );
}
}
QStringList attributes;
QgsField field;
foreach( field, layer->pendingFields().toList() ) {
attributes.append( field.name() );
}
attributes = mAccessControl->layerAttributes( layer, attributes );
fReq.setSubsetOfAttributes( attributes, layer->pendingFields() );
#else
if ( ! searchRect.isEmpty() )
{
fReq.setFilterRect( searchRect );
}
#endif
QgsFeatureIterator fit = layer->getFeatures( fReq );
bool featureBBoxInitialized = false;
@ -2115,7 +2230,13 @@ int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer,
{
bool withGeom = layer->wkbType() != QGis::WKBNoGeometry && addWktGeometry;
int version = infoFormat.startsWith( "application/vnd.ogc.gml/3" ) ? 3 : 2;
QDomElement elem = createFeatureGML( &feature, layer, infoDocument, outputCrs, mConfigParser && mConfigParser->useLayerIDs() ? layer->id() : layer->name(), withGeom, version );
QString typeName = mConfigParser && mConfigParser->useLayerIDs() ? layer->id() : layer->name();
QDomElement elem = createFeatureGML(
&feature, layer, infoDocument, outputCrs, typeName, withGeom, version
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, &attributes
#endif
);
QDomElement featureMemberElem = infoDocument.createElement( "gml:featureMember"/*wfs:FeatureMember*/ );
featureMemberElem.appendChild( elem );
layerElement.appendChild( featureMemberElem );
@ -2136,6 +2257,13 @@ int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer,
{
continue;
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
//skip attribute if it is excluded by access control
if ( !attributes.contains( fields[i].name() ) )
{
continue;
}
#endif
//replace attribute name if there is an attribute alias?
QString attributeName = layer->attributeDisplayName( i );
@ -2249,7 +2377,9 @@ int QgsWMSServer::featureInfoFromRasterLayer( QgsRasterLayer* layer,
QgsCoordinateReferenceSystem layerCrs = layer->crs();
int version = infoFormat.startsWith( "application/vnd.ogc.gml/3" ) ? 3 : 2;
QDomElement elem = createFeatureGML( &feature, 0, infoDocument, layerCrs, mConfigParser && mConfigParser->useLayerIDs() ? layer->id() : layer->name(), false, version );
QString typeName = mConfigParser && mConfigParser->useLayerIDs() ? layer->id() : layer->name();
QDomElement elem = createFeatureGML(
&feature, 0, infoDocument, layerCrs, typeName, false, version, NULL );
layerElement.appendChild( elem );
}
else
@ -2417,22 +2547,17 @@ QMap<QString, QString> QgsWMSServer::applyRequestedLayerFilters( const QStringLi
return filterMap;
}
void QgsWMSServer::restoreLayerFilters( const QMap < QString, QString >& filterMap ) const
#ifdef HAVE_SERVER_PYTHON_PLUGINS
void QgsWMSServer::applyAccessControlLayersFilters( const QStringList& layerList, QMap<QString, QString>& originalLayerFilters ) const
{
QMap < QString, QString >::const_iterator filterIt = filterMap.constBegin();
for ( ; filterIt != filterMap.constEnd(); ++filterIt )
{
QgsVectorLayer* filteredLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( filterIt.key() ) );
if ( filteredLayer )
{
QgsVectorDataProvider* dp = filteredLayer->dataProvider();
if ( dp )
{
dp->setSubsetString( filterIt.value() );
}
foreach ( QString layerName, layerList ) {
QList<QgsMapLayer*> mapLayers = QgsMapLayerRegistry::instance()->mapLayersByName( layerName );
foreach ( QgsMapLayer* mapLayer, mapLayers ) {
applyAccessControlLayerFilters(mapLayer, originalLayerFilters);
}
}
}
#endif
bool QgsWMSServer::testFilterStringSafety( const QString& filter ) const
{
@ -2986,7 +3111,8 @@ QDomElement QgsWMSServer::createFeatureGML(
QgsCoordinateReferenceSystem& crs,
const QString& typeName,
bool withGeom,
int version ) const
int version,
QStringList* attributes) const
{
//qgs:%TYPENAME%
QDomElement typeNameElement = doc.createElement( "qgs:" + typeName /*qgs:%TYPENAME%*/ );
@ -3085,6 +3211,12 @@ QDomElement QgsWMSServer::createFeatureGML(
{
continue;
}
//skip attribute if it is excluded by access control
if ( attributes != NULL && !attributes->contains( attributeName ) )
{
continue;
}
QDomElement fieldElem = doc.createElement( "qgs:" + attributeName.replace( QString( " " ), QString( "_" ) ) );
QString fieldTextString = featureAttributes[i].toString();
if ( layer )

View File

@ -44,6 +44,8 @@ class QgsRenderContext;
class QgsVectorLayer;
class QgsSymbol;
class QgsSymbolV2;
class QgsAccessControl;
class QColor;
class QFile;
class QFont;
@ -61,8 +63,17 @@ class QgsWMSServer: public QgsOWSServer
public:
/** Constructor. Does _NOT_ take ownership of
QgsConfigParser, QgsCapabilitiesCache and QgsMapRenderer*/
QgsWMSServer( const QString& configFilePath, QMap<QString, QString> &parameters, QgsWMSConfigParser* cp, QgsRequestHandler* rh,
QgsMapRenderer* renderer, QgsCapabilitiesCache* capCache );
QgsWMSServer(
const QString& configFilePath
, QMap<QString, QString> &parameters
, QgsWMSConfigParser* cp
, QgsRequestHandler* rh
, QgsMapRenderer* renderer
, QgsCapabilitiesCache* capCache
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, const QgsAccessControl* accessControl
#endif
);
~QgsWMSServer();
void executeRequest() override;
@ -178,8 +189,13 @@ class QgsWMSServer: public QgsOWSServer
/** 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 )*/
QMap<QString, QString> applyRequestedLayerFilters( const QStringList& layerList ) const;
/** Restores the original layer filters*/
void restoreLayerFilters( const QMap < QString, QString >& filterMap ) 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, QMap<QString, 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;
@ -236,13 +252,15 @@ class QgsWMSServer: public QgsOWSServer
bool mDrawLegendLayerLabel;
bool mDrawLegendItemLabel;
QDomElement createFeatureGML( QgsFeature* feat,
QgsVectorLayer* layer,
QDomDocument& doc,
QgsCoordinateReferenceSystem& crs,
const QString& typeName,
bool withGeom,
int version ) const;
QDomElement createFeatureGML(
QgsFeature* feat,
QgsVectorLayer* layer,
QDomDocument& doc,
QgsCoordinateReferenceSystem& crs,
const QString& typeName,
bool withGeom,
int version,
QStringList* attributes=NULL) const;
/** Replaces attribute value with ValueRelation or ValueRelation if defined. Otherwise returns the original value*/
static QString replaceValueMapAndRelation( QgsVectorLayer* vl, int idx, const QString& attributeVal );

View File

@ -93,4 +93,5 @@ ENDIF (WITH_APIDOC)
IF (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsServer test_qgsserver.py)
ADD_PYTHON_TEST(PyQgsServerAccessControl test_qgsserver_accesscontrol.py)
ENDIF (WITH_SERVER)

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,58 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis version="1.8.0-Trunk" minimumScale="0" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0">
<transparencyLevelInt>255</transparencyLevelInt>
<renderer-v2 symbollevels="0" type="singleSymbol">
<symbols>
<symbol outputUnit="MM" alpha="1" type="fill" name="0">
<layer pass="0" class="SimpleFill" locked="0">
<prop k="color" v="145,149,158,255"/>
<prop k="color_border" v="157,157,157,255"/>
<prop k="offset" v="2,2"/>
<prop k="style" v="solid"/>
<prop k="style_border" v="solid"/>
<prop k="width_border" v="0.26"/>
</layer>
<layer pass="0" class="SimpleFill" locked="0">
<prop k="color" v="0,0,255,255"/>
<prop k="color_border" v="0,0,0,255"/>
<prop k="offset" v="0,0"/>
<prop k="style" v="solid"/>
<prop k="style_border" v="solid"/>
<prop k="width_border" v="0.26"/>
</layer>
</symbol>
</symbols>
<rotation field=""/>
<sizescale field=""/>
</renderer-v2>
<customproperties/>
<displayfield>name</displayfield>
<label>0</label>
<labelattributes>
<label fieldname="" text="Label"/>
<family fieldname="" name="Ubuntu"/>
<size fieldname="" units="pt" value="12"/>
<bold fieldname="" on="0"/>
<italic fieldname="" on="0"/>
<underline fieldname="" on="0"/>
<strikeout fieldname="" on="0"/>
<color fieldname="" red="0" blue="0" green="0"/>
<x fieldname=""/>
<y fieldname=""/>
<offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
<angle fieldname="" value="0" auto="0"/>
<alignment fieldname="" value="center"/>
<buffercolor fieldname="" red="255" blue="255" green="255"/>
<buffersize fieldname="" units="pt" value="1"/>
<bufferenabled fieldname="" on=""/>
<multilineenabled fieldname="" on=""/>
<selectedonly on=""/>
</labelattributes>
<edittypes>
<edittype type="0" name="name"/>
</edittypes>
<editform>.</editform>
<editforminit></editforminit>
<annotationform>.</annotationform>
<attributeactions/>
</qgis>

View File

@ -0,0 +1,59 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis version="1.8.0-Trunk" minimumScale="0" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0">
<transparencyLevelInt>255</transparencyLevelInt>
<renderer-v2 symbollevels="0" type="singleSymbol">
<symbols>
<symbol outputUnit="MM" alpha="1" type="fill" name="0">
<layer pass="0" class="SimpleFill" locked="0">
<prop k="color" v="174,176,194,255"/>
<prop k="color_border" v="121,121,121,255"/>
<prop k="offset" v="2,2"/>
<prop k="style" v="solid"/>
<prop k="style_border" v="solid"/>
<prop k="width_border" v="0.26"/>
</layer>
<layer pass="0" class="SimpleFill" locked="0">
<prop k="color" v="224,15,183,255"/>
<prop k="color_border" v="0,0,0,255"/>
<prop k="offset" v="0,0"/>
<prop k="style" v="solid"/>
<prop k="style_border" v="solid"/>
<prop k="width_border" v="0.26"/>
</layer>
</symbol>
</symbols>
<rotation field=""/>
<sizescale field=""/>
</renderer-v2>
<customproperties/>
<displayfield>pkuid</displayfield>
<label>0</label>
<labelattributes>
<label fieldname="" text="Label"/>
<family fieldname="" name="Ubuntu"/>
<size fieldname="" units="pt" value="12"/>
<bold fieldname="" on="0"/>
<italic fieldname="" on="0"/>
<underline fieldname="" on="0"/>
<strikeout fieldname="" on="0"/>
<color fieldname="" red="0" blue="0" green="0"/>
<x fieldname=""/>
<y fieldname=""/>
<offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
<angle fieldname="" value="0" auto="0"/>
<alignment fieldname="" value="center"/>
<buffercolor fieldname="" red="255" blue="255" green="255"/>
<buffersize fieldname="" units="pt" value="1"/>
<bufferenabled fieldname="" on=""/>
<multilineenabled fieldname="" on=""/>
<selectedonly on=""/>
</labelattributes>
<edittypes>
<edittype type="0" name="colour"/>
<edittype type="0" name="pkuid"/>
</edittypes>
<editform>../projects</editform>
<editforminit></editforminit>
<annotationform>../projects</annotationform>
<attributeactions/>
</qgis>

Binary file not shown.

View File

@ -0,0 +1,20 @@
<PAMDataset>
<PAMRasterBand band="1">
<Histograms>
<HistItem>
<HistMin>-18.929</HistMin>
<HistMax>3842.929</HistMax>
<BucketCount>1000</BucketCount>
<IncludeOutOfRange>0</IncludeOutOfRange>
<Approximate>1</Approximate>
<HistCounts>0|0|0|0|3|4|6|3|4|4|5|5|2|3|2|8|3|3|4|6|4|4|1|3|8|6|2|2|6|3|6|3|3|2|2|3|5|4|2|2|5|4|2|2|2|2|1|0|4|4|0|4|3|3|0|1|3|0|0|2|1|1|4|1|0|1|3|3|2|3|1|2|3|1|2|2|0|0|3|3|0|3|2|2|3|1|2|3|2|0|3|2|2|0|2|0|0|2|1|1|2|3|1|0|2|3|1|1|2|1|2|1|0|1|1|1|1|0|2|0|1|1|2|1|2|0|0|0|3|1|1|0|1|0|2|2|1|0|2|1|3|4|4|1|0|1|1|3|3|0|2|1|0|0|1|1|1|2|0|1|0|2|2|0|2|1|0|0|1|3|0|0|1|2|2|2|1|1|0|1|1|1|4|2|1|1|1|1|1|1|2|1|1|4|0|1|0|0|1|1|0|2|3|1|4|1|0|2|0|3|1|2|2|1|1|5|0|1|2|0|1|1|3|1|0|0|1|0|2|2|2|5|0|1|0|2|0|0|3|1|0|1|2|0|1|1|0|1|0|3|1|2|0|1|0|0|1|1|1|2|0|0|0|1|2|0|0|0|1|0|0|0|0|0|0|2|1|1|0|1|0|2|1|0|2|1|1|0|1|2|0|0|0|0|2|3|2|0|2|0|1|0|0|1|1|0|1|0|2|0|1|1|0|1|1|1|2|0|0|2|1|1|0|1|0|0|1|3|1|0|0|0|1|0|0|0|1|1|1|0|0|2|2|1|0|0|0|2|0|2|0|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0|0|0|0|0|1|0|0|1|0|1|0|1|0|0|0|0|0|0|0|0|0|0|0|1|0|0|1|2|0|1|0|0|1|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|1|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|1|0|0|1|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|1|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0</HistCounts>
</HistItem>
</Histograms>
<Metadata>
<MDI key="STATISTICS_MAXIMUM">3841</MDI>
<MDI key="STATISTICS_MEAN">550.59810315778</MDI>
<MDI key="STATISTICS_MINIMUM">-17</MDI>
<MDI key="STATISTICS_STDDEV">506.70554545277</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
{
"layers": {
"Country": {
"abstract": "",
"displayInLegend": "True",
"baseLayer": "False",
"noLegendImage": "False",
"id": "country20131022151106556",
"title": "Country",
"singleTile": "False",
"groupAsLayer": "False",
"popup": "False",
"clientCacheExpiration": 300,
"link": "",
"toggled": "True",
"name": "Country",
"cached": "False",
"type": "layer",
"maxScale": 1000000000000,
"imageFormat": "image/png",
"minScale": 1
},
"Hello": {
"abstract": "",
"displayInLegend": "True",
"baseLayer": "False",
"noLegendImage": "False",
"id": "hello20131022151106574",
"title": "Hello",
"singleTile": "False",
"groupAsLayer": "False",
"popup": "False",
"clientCacheExpiration": 300,
"link": "",
"toggled": "True",
"name": "Hello",
"cached": "False",
"type": "layer",
"maxScale": 1000000000000,
"imageFormat": "image/png",
"minScale": 1
}
},
"options": {
"projection": {
"proj4": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs",
"ref": "EPSG:3857"
},
"initialExtent": [
-33626185.4981,
-13032965.1854,
33978427.7365,
21121731.156
],
"maxScale": 100000000,
"tmTimeFrameType": "seconds",
"mapScales": [
10000,
25000,
50000,
100000,
250000,
500000,
1000000,
10000000,
100000000
],
"tmTimeFrameSize": 10,
"tmAnimationFrameLength": 1000,
"bbox": [
-33626185.49808586,
-13032965.185421439,
33978427.73650816,
21121731.15603344
],
"print": "True",
"minScale": 10000
}
}