Skip to content

Commit

Permalink
Server WFS3 simple transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso committed Nov 11, 2019
1 parent 956c468 commit 814d5be
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 57 deletions.
16 changes: 16 additions & 0 deletions python/server/auto_generated/qgsserverapiutils.sip.in
Expand Up @@ -115,6 +115,22 @@ This method takes into account the ACL restrictions provided by QGIS Server Acce
%End


static const QVector<QgsMapLayer *> editableWfsLayers( const QgsProject *project, QgsServerRequest::Method &method );
%Docstring
Returns the list of layers accessible to the service in edit mode for a given ``project``.

:param method: represents the operation to be performed (POST, PUT, PATCH, DELETE)

This method takes into account the ACL restrictions provided by QGIS Server Access Control plugins.

.. note::

project must not be NULL

.. versionadded:: 3.12
%End


static QString sanitizedFieldValue( const QString &value );
%Docstring
Sanitizes the input ``value`` by removing URL encoding and checking for malicious content.
Expand Down
14 changes: 13 additions & 1 deletion python/server/auto_generated/qgsserverrequest.sip.in
Expand Up @@ -16,6 +16,9 @@ class QgsServerRequest
%TypeHeaderCode
#include "qgsserverrequest.h"
%End
public:
static const QMetaObject staticMetaObject;

public:

typedef QMap<QString, QString> Parameters;
Expand All @@ -27,7 +30,8 @@ class QgsServerRequest
PutMethod,
GetMethod,
PostMethod,
DeleteMethod
DeleteMethod,
PatchMethod
};


Expand Down Expand Up @@ -56,6 +60,14 @@ Constructor

virtual ~QgsServerRequest();

static QString methodToString( const Method &method );
%Docstring
methodToString returns a string representation of an HTTP request ``method``

.. versionadded:: 3.12
%End


QUrl url() const;
%Docstring

Expand Down
4 changes: 4 additions & 0 deletions src/server/qgsfcgiserverrequest.cpp
Expand Up @@ -106,6 +106,10 @@ QgsFcgiServerRequest::QgsFcgiServerRequest()
{
method = HeadMethod;
}
else if ( strcmp( me, "PATCH" ) == 0 )
{
method = PatchMethod;
}
}

if ( method == PostMethod || method == PutMethod )
Expand Down
107 changes: 57 additions & 50 deletions src/server/qgsrequesthandler.cpp
Expand Up @@ -189,70 +189,77 @@ void QgsRequestHandler::setupParameters()

void QgsRequestHandler::parseInput()
{
if ( mRequest.method() == QgsServerRequest::PostMethod )
if ( mRequest.method() == QgsServerRequest::PostMethod ||
mRequest.method() == QgsServerRequest::PutMethod ||
mRequest.method() == QgsServerRequest::PatchMethod )
{
QString inputString( mRequest.data() );

QDomDocument doc;
QString errorMsg;
int line = -1;
int column = -1;
if ( !doc.setContent( inputString, true, &errorMsg, &line, &column ) )
if ( mRequest.header( QStringLiteral( "Content-Type" ) ).contains( QStringLiteral( "json" ) ) )
{
// XXX Output error but continue processing request ?
QgsMessageLog::logMessage( QStringLiteral( "Warning: error parsing post data as XML: at line %1, column %2: %3. Assuming urlencoded query string sent in the post body." )
.arg( line ).arg( column ).arg( errorMsg ) );

// Process input string as a simple query text

typedef QPair<QString, QString> pair_t;
QUrlQuery query( inputString );
QList<pair_t> items = query.queryItems();
for ( pair_t pair : items )
{
// QUrl::fromPercentEncoding doesn't replace '+' with space
const QString key = QUrl::fromPercentEncoding( pair.first.replace( '+', ' ' ).toUtf8() );
const QString value = QUrl::fromPercentEncoding( pair.second.replace( '+', ' ' ).toUtf8() );
mRequest.setParameter( key.toUpper(), value );
}
setupParameters();
}
else
{
// we have an XML document

setupParameters();

QDomElement docElem = doc.documentElement();
// the document element tag name is the request
mRequest.setParameter( QStringLiteral( "REQUEST" ), docElem.tagName() );
// loop through the attributes which are the parameters
// excepting the attributes started by xmlns or xsi
QDomNamedNodeMap map = docElem.attributes();
for ( int i = 0 ; i < map.length() ; ++i )
QString inputString( mRequest.data() );
QDomDocument doc;
QString errorMsg;
int line = -1;
int column = -1;
if ( !doc.setContent( inputString, true, &errorMsg, &line, &column ) )
{
if ( map.item( i ).isNull() )
continue;

const QDomNode attrNode = map.item( i );
const QDomAttr attr = attrNode.toAttr();
if ( attr.isNull() )
continue;

const QString attrName = attr.name();
if ( attrName.startsWith( "xmlns" ) || attrName.startsWith( "xsi:" ) )
continue;

mRequest.setParameter( attrName.toUpper(), attr.value() );
// XXX Output error but continue processing request ?
QgsMessageLog::logMessage( QStringLiteral( "Warning: error parsing post data as XML: at line %1, column %2: %3. Assuming urlencoded query string sent in the post body." )
.arg( line ).arg( column ).arg( errorMsg ) );

// Process input string as a simple query text

typedef QPair<QString, QString> pair_t;
QUrlQuery query( inputString );
QList<pair_t> items = query.queryItems();
for ( pair_t pair : items )
{
// QUrl::fromPercentEncoding doesn't replace '+' with space
const QString key = QUrl::fromPercentEncoding( pair.first.replace( '+', ' ' ).toUtf8() );
const QString value = QUrl::fromPercentEncoding( pair.second.replace( '+', ' ' ).toUtf8() );
mRequest.setParameter( key.toUpper(), value );
}
setupParameters();
}
else
{
// we have an XML document

setupParameters();

QDomElement docElem = doc.documentElement();
// the document element tag name is the request
mRequest.setParameter( QStringLiteral( "REQUEST" ), docElem.tagName() );
// loop through the attributes which are the parameters
// excepting the attributes started by xmlns or xsi
QDomNamedNodeMap map = docElem.attributes();
for ( int i = 0 ; i < map.length() ; ++i )
{
if ( map.item( i ).isNull() )
continue;

const QDomNode attrNode = map.item( i );
const QDomAttr attr = attrNode.toAttr();
if ( attr.isNull() )
continue;

const QString attrName = attr.name();
if ( attrName.startsWith( "xmlns" ) || attrName.startsWith( "xsi:" ) )
continue;

mRequest.setParameter( attrName.toUpper(), attr.value() );
}
mRequest.setParameter( QStringLiteral( "REQUEST_BODY" ), inputString );
}
mRequest.setParameter( QStringLiteral( "REQUEST_BODY" ), inputString );
}
}
else
{
setupParameters();
}

}

void QgsRequestHandler::setParameter( const QString &key, const QString &value )
Expand Down
54 changes: 54 additions & 0 deletions src/server/qgsserverapiutils.h
Expand Up @@ -201,6 +201,60 @@ class SERVER_EXPORT QgsServerApiUtils
return result;
}

#endif

/**
* Returns the list of layers accessible to the service in edit mode for a given \a project.
* \param method represents the operation to be performed (POST, PUT, PATCH, DELETE)
*
* This method takes into account the ACL restrictions provided by QGIS Server Access Control plugins.
*
* \note project must not be NULL
* \since QGIS 3.12
*/
static const QVector<QgsMapLayer *> editableWfsLayers( const QgsProject *project, QgsServerRequest::Method &method );

#ifndef SIP_RUN

/**
* Returns the list of layers of type T accessible to the WFS service for a given \a project.
*
* Example:
*
* QVector<QgsVectorLayer*> vectorLayers = publishedLayers<QgsVectorLayer>();
*
* \note not available in Python bindings
*/
template <typename T>
static const QVector<const T *> editableWfsLayers( const QgsServerApiContext &context, QgsServerRequest::Method &method )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsAccessControl *accessControl = context.serverInterface()->accessControls();
#endif
const QgsProject *project = context.project();
QVector<const T *> result;
if ( project )
{
const QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
const auto constLayers { project->layers<T *>() };
for ( const auto &layer : constLayers )
{
if ( ! wfsLayerIds.contains( layer->id() ) )
{
continue;
}
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( accessControl && !accessControl->layerInsertPermission( layer ) )
{
continue;
}
#endif
result.push_back( layer );
}
}
return result;
}

#endif

/**
Expand Down
21 changes: 21 additions & 0 deletions src/server/qgsserverexception.h
Expand Up @@ -251,6 +251,27 @@ class SERVER_EXPORT QgsServerApiBadRequestException: public QgsServerApiExceptio
}
};


/**
* \ingroup server
* \class QgsServerApiPermissionDeniedException
* \brief Forbidden (permission denied) 403
*
* Note that this exception is associated with a default return code 403 which may be
* not appropriate in some situations.
*
* \since QGIS 3.12
*/
class SERVER_EXPORT QgsServerApiPermissionDeniedException: public QgsServerApiException
{
public:
//! Construction
QgsServerApiPermissionDeniedException( const QString &message, const QString &mimeType = QStringLiteral( "application/json" ), int responseCode = 403 )
: QgsServerApiException( QStringLiteral( "Forbidden" ), message, mimeType, responseCode )
{
}
};

/**
* \ingroup server
* \class QgsServerApiImproperlyConfiguredException
Expand Down
6 changes: 6 additions & 0 deletions src/server/qgsserverrequest.cpp
Expand Up @@ -34,6 +34,12 @@ QgsServerRequest::QgsServerRequest( const QUrl &url, Method method, const Header
mParams.load( QUrlQuery( url ) );
}

QString QgsServerRequest::methodToString( const QgsServerRequest::Method &method )
{
static QMetaEnum metaEnum = QMetaEnum::fromType<QgsServerRequest::Method>();
return QString( metaEnum.valueToKey( method ) ).remove( QStringLiteral( "Method" ) ).toUpper( );
}

QString QgsServerRequest::header( const QString &name ) const
{
return mHeaders.value( name );
Expand Down
13 changes: 12 additions & 1 deletion src/server/qgsserverrequest.h
Expand Up @@ -37,6 +37,8 @@

class SERVER_EXPORT QgsServerRequest
{
Q_GADGET

public:

typedef QMap<QString, QString> Parameters;
Expand All @@ -51,8 +53,10 @@ class SERVER_EXPORT QgsServerRequest
PutMethod,
GetMethod,
PostMethod,
DeleteMethod
DeleteMethod,
PatchMethod
};
Q_ENUM( Method )


/**
Expand Down Expand Up @@ -81,6 +85,13 @@ class SERVER_EXPORT QgsServerRequest
//! destructor
virtual ~QgsServerRequest() = default;

/**
* methodToString returns a string representation of an HTTP request \a method
* \since QGIS 3.12
*/
static QString methodToString( const Method &method );


/**
* \returns the request url as seen by QGIS server
*
Expand Down

0 comments on commit 814d5be

Please sign in to comment.