mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Merge pull request #4124 from pblottiere/security
add unit tests for sql injection
This commit is contained in:
commit
9bb7681b86
@ -183,6 +183,7 @@ IF (WITH_SERVER)
|
||||
ADD_PYTHON_TEST(PyQgsServer test_qgsserver.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerSettings test_qgsserver_settings.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerProjectUtils test_qgsserver_projectutils.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerSecurity test_qgsserver_security.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerAccessControl test_qgsserver_accesscontrol.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerWFST test_qgsserver_wfst.py)
|
||||
ADD_PYTHON_TEST(PyQgsOfflineEditingWFS test_offline_editing_wfs.py)
|
||||
|
382
tests/src/python/test_qgsserver_security.py
Normal file
382
tests/src/python/test_qgsserver_security.py
Normal file
@ -0,0 +1,382 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for server security.
|
||||
|
||||
.. note:: 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.
|
||||
|
||||
"""
|
||||
__author__ = 'Paul Blottiere'
|
||||
__date__ = '31/01/2017'
|
||||
__copyright__ = 'Copyright 2017, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from qgis.utils import spatialite_connect
|
||||
import os
|
||||
import time
|
||||
import urllib.parse
|
||||
from shutil import copyfile
|
||||
from qgis.core import QgsApplication
|
||||
from qgis.server import QgsServer
|
||||
from qgis.testing import unittest
|
||||
from utilities import unitTestDataPath
|
||||
|
||||
|
||||
class TestQgsServerSecurity(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.testdatapath = unitTestDataPath('qgis_server_security') + '/'
|
||||
cls.db = os.path.join(cls.testdatapath, 'db.sqlite')
|
||||
cls.db_clone = os.path.join(cls.testdatapath, 'db_clone.sqlite')
|
||||
cls.project = os.path.join(cls.testdatapath, 'project.qgs')
|
||||
cls.app = QgsApplication([], False)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.app.exitQgis()
|
||||
try:
|
||||
os.remove(cls.db_clone)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
self.server = QgsServer()
|
||||
copyfile(self.db, self.db_clone)
|
||||
|
||||
def test_wms_getfeatureinfo_filter_and_based_blind(self):
|
||||
"""
|
||||
And-based blind attack to check the kind of database currently used (if
|
||||
the result is valid for the point nammed 'b', then sqlite_version()
|
||||
function exist).
|
||||
|
||||
But does not work because of the whitelist.
|
||||
|
||||
If you remove the safety check, this is a valid injection.
|
||||
"""
|
||||
|
||||
filter_sql = "point:\"name\" = 'b'"
|
||||
injection_sql = ") and (select sqlite_version()"
|
||||
|
||||
query = "{0} {1}".format(filter_sql, injection_sql)
|
||||
d, h = self.handle_request_wms_getfeatureinfo(query)
|
||||
|
||||
self.assertFalse(b"name = 'b'" in d)
|
||||
|
||||
def test_wms_getfeatureinfo_filter_time_based_blind(self):
|
||||
"""
|
||||
Time-based blind to check the current version of database. If the
|
||||
server is too long to respond, then we have the answer!
|
||||
|
||||
But it does not work because of the whitelist.
|
||||
|
||||
If you remove the safety check, this is a valid injection.
|
||||
"""
|
||||
|
||||
# first step, retrieve the version of sqlite by a regular way
|
||||
conn = spatialite_connect(self.db_clone)
|
||||
cur = conn.cursor()
|
||||
sql = "select sqlite_version()"
|
||||
sqlite_version = ''
|
||||
for row in cur.execute(sql):
|
||||
sqlite_version = row[0]
|
||||
conn.close()
|
||||
|
||||
# second step, check the time of response for an invalid version
|
||||
filter_sql = "point:\"name\" = 'b'"
|
||||
injection_sql = ") and (select case sqlite_version() when '0.0.0' then substr(upper(hex(randomblob(99999999))),0,1) end)--"
|
||||
|
||||
query = "{0} {1}".format(filter_sql, injection_sql)
|
||||
start = time.time()
|
||||
d, h = self.handle_request_wms_getfeatureinfo(query)
|
||||
duration_invalid_version = time.time() - start
|
||||
|
||||
# third step, check the time of response for a valid version
|
||||
# maximum: several seconds
|
||||
injection_sql = ") and (select case sqlite_version() when '{0}' then substr(upper(hex(randomblob(99999999))),0,1) end)--".format(sqlite_version)
|
||||
|
||||
query = "{0} {1}".format(filter_sql, injection_sql)
|
||||
start = time.time()
|
||||
d, h = self.handle_request_wms_getfeatureinfo(query)
|
||||
duration_valid_version = time.time() - start
|
||||
|
||||
# compare duration. On my computer when safety check is deactivated:
|
||||
# duration_invalid_version: 0.012360334396362305
|
||||
# duration_valid_version: 2.8810460567474365
|
||||
self.assertAlmostEqual(duration_valid_version, duration_invalid_version, delta=0.5)
|
||||
|
||||
def test_wms_getfeatureinfo_filter_stacked(self):
|
||||
"""
|
||||
The aim is to execute some staked queries. Here the 'drop' function is
|
||||
used but it could be done with create, insert, ...
|
||||
|
||||
But the filter string is split thanks to the semicolon so it seems
|
||||
totally ignored whatever the query is (even without the safety check).
|
||||
"""
|
||||
|
||||
filter_sql = "point:\"name\" = 'fake'"
|
||||
injection_sql = "); drop table point"
|
||||
|
||||
query = "{0} {1}".format(filter_sql, injection_sql)
|
||||
d, h = self.handle_request_wms_getfeatureinfo(query)
|
||||
|
||||
self.assertTrue(self.is_point_table_still_exist())
|
||||
|
||||
def test_wms_getfeatureinfo_filter_union_0(self):
|
||||
"""
|
||||
The aim is to retrieve name of tables within the database (like
|
||||
'SpatialIndex').
|
||||
|
||||
But the whitelist blocks this request because of invalid tokens.
|
||||
|
||||
If you remove the safety check, this is a valid injection.
|
||||
"""
|
||||
|
||||
filter_sql = "point:\"name\" = 'fake'"
|
||||
injection_sql = ") union select 1,1,name,1,1 from sqlite_master where type = \"table\" order by name--"
|
||||
|
||||
query = "{0} {1}".format(filter_sql, injection_sql)
|
||||
d, h = self.handle_request_wms_getfeatureinfo(query)
|
||||
|
||||
self.assertFalse(b'SpatialIndex' in d)
|
||||
|
||||
def test_wms_getfeatureinfo_filter_union_1(self):
|
||||
"""
|
||||
The aim is to retrieve data from an excluded layer.
|
||||
|
||||
But the whitelist blocks this request because of invalid tokens.
|
||||
|
||||
If you remove the safety check, this is a valid injection.
|
||||
"""
|
||||
|
||||
filter_sql = "point:\"name\" = 'fake'"
|
||||
injection_sql = ") union select 1,1,* from aoi--"
|
||||
|
||||
query = "{0} {1}".format(filter_sql, injection_sql)
|
||||
d, h = self.handle_request_wms_getfeatureinfo(query)
|
||||
|
||||
self.assertFalse(b'private_value' in d)
|
||||
|
||||
def test_wms_getfeatureinfo_filter_unicode(self):
|
||||
"""
|
||||
The aim is to send some invalid token in unicode to bypass the
|
||||
whitelist.
|
||||
|
||||
But unicode is interpreted and checked by the safety function.
|
||||
"""
|
||||
|
||||
# %3B -> semicolon
|
||||
filter_sql = "point:\"name\" = 'fake %3B'"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
def test_wms_getfeatureinfo_filter_patternmatching(self):
|
||||
"""
|
||||
The aim is to retrieve the table's name thanks to pattern matching.
|
||||
|
||||
If you remove the safety check, this is a valid injection.
|
||||
"""
|
||||
|
||||
filter_sql = "point:\"name\" = 'b'"
|
||||
injection_sql = "or ( select name from sqlite_master where type='table' and name like '{0}') != ''"
|
||||
query = "{0} {1}".format(filter_sql, injection_sql)
|
||||
|
||||
# there's no table named as 'az%'
|
||||
name = "az%"
|
||||
sql = query.format(name)
|
||||
d, h = self.handle_request_wms_getfeatureinfo(sql)
|
||||
# self.assertTrue(b"name = 'b'" in d) #true if sanity check deactivated
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
# a table named as 'ao%' exist
|
||||
name = "ao%"
|
||||
sql = query.format(name)
|
||||
d, h = self.handle_request_wms_getfeatureinfo(sql)
|
||||
# self.assertTrue(b"name = 'a'" in d) #true if sanity check deactivated
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
# a table named as 'aoi' exist
|
||||
name = "aoi"
|
||||
sql = query.format(name)
|
||||
d, h = self.handle_request_wms_getfeatureinfo(sql)
|
||||
# self.assertTrue(b"name = 'a'" in d) #true if sanity check deactivated
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
def test_wms_getfeatureinfo_filter_whitelist(self):
|
||||
"""
|
||||
The aim is to check that some tokens cannot pass the safety check
|
||||
whatever their positions in the filter string.
|
||||
"""
|
||||
|
||||
# create
|
||||
filter_sql = "point:\"name\" = 'a'create"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
filter_sql = "point:\"name\" = 'a' create"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
# invalid token and escape single quote
|
||||
filter_sql = "point:\"name\" = 'a\\'create"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
# drop
|
||||
filter_sql = "point:\"name\" = 'a' drop"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
# select
|
||||
filter_sql = "point:\"name\" = 'a' select"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
# comments
|
||||
filter_sql = "point:\"name\" = 'a' #"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
filter_sql = "point:\"name\" = 'a' -"
|
||||
d, h = self.handle_request_wms_getfeatureinfo(filter_sql)
|
||||
self.assertTrue(self.check_service_exception_report(d))
|
||||
|
||||
def test_wfs_getfeature_filter_stacked(self):
|
||||
"""
|
||||
The aim is to execute some staked queries within the 'Literal'
|
||||
and 'PropertyName' field. Here the 'drop' function is used but it
|
||||
could be done with create, insert, ...
|
||||
|
||||
But due to the implementation, these filters are not resolved on
|
||||
database side but in server side with QgsExpression. So, there's no
|
||||
'WHERE' clause and the query never reach the database. By the way,
|
||||
it's exactly the same thing whatever the kind of attacks and for
|
||||
the EXP_FILTER parameter too (filter described with QgsExpression).
|
||||
|
||||
It's typically the kind of SQL injection which has been fixed in
|
||||
mapserver several years ago:
|
||||
https://trac.osgeo.org/mapserver/ticket/3874
|
||||
"""
|
||||
|
||||
# ogc:Literal / ogc:PropertyIsEqualTo
|
||||
literal = "4')); drop table point --"
|
||||
filter_xml = "<ogc:Filter%20xmlns:ogc=\"http://www.opengis.net/ogc\"><ogc:PropertyIsEqualTo><ogc:PropertyName>pkuid</ogc:PropertyName><ogc:Literal>{0}</ogc:Literal></ogc:PropertyIsEqualTo></ogc:Filter>".format(literal)
|
||||
self.handle_request_wfs_getfeature_filter(filter_xml)
|
||||
self.assertTrue(self.is_point_table_still_exist())
|
||||
|
||||
# ogc:Literal / ogc:PropertyIsLike
|
||||
literal = "4')); drop table point --"
|
||||
filter_xml = "<ogc:Filter%20xmlns:ogc=\"http://www.opengis.net/ogc\"><ogc:PropertyIsLike><ogc:PropertyName>pkuid</ogc:PropertyName><ogc:Literal>{0}</ogc:Literal></ogc:PropertyIsLike></ogc:Filter>".format(literal)
|
||||
self.handle_request_wfs_getfeature_filter(filter_xml)
|
||||
self.assertTrue(self.is_point_table_still_exist())
|
||||
|
||||
# ogc:PropertyName / ogc:PropertyIsLike
|
||||
propname = "name = 'a')); drop table point --"
|
||||
filter_xml = "<ogc:Filter%20xmlns:ogc=\"http://www.opengis.net/ogc\"><ogc:PropertyIsLike><ogc:PropertyName>{0}</ogc:PropertyName><ogc:Literal>4</ogc:Literal></ogc:PropertyIsLike></ogc:Filter>".format(propname)
|
||||
self.handle_request_wfs_getfeature_filter(filter_xml)
|
||||
self.assertTrue(self.is_point_table_still_exist())
|
||||
|
||||
def test_wms_getmap_sld_stacked(self):
|
||||
"""
|
||||
The aim is to execute some staked queries within the 'Literal'
|
||||
and 'PropertyName' field. Here the 'drop' function is used but it
|
||||
could be done with create, insert, ...
|
||||
|
||||
However it's not working because special characters are duplicated. For
|
||||
example, with 'Literal' as "4')); drop table point --", the underlying
|
||||
query is:
|
||||
SELECT .... AND (("pkuid" = '4'')); drop table point --'))
|
||||
"""
|
||||
|
||||
literal = "4')); drop table point --"
|
||||
sld = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><StyledLayerDescriptor xmlns=\"http://www.opengis.net/sld\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:ogc=\"http://www.opengis.net/ogc\" xsi:schemaLocation=\"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd\" version=\"1.1.0\" xmlns:se=\"http://www.opengis.net/se\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"> <NamedLayer> <se:Name>point</se:Name> <UserStyle> <se:Name>point</se:Name> <se:FeatureTypeStyle> <se:Rule> <se:Name>Single symbol</se:Name> <ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\"> <ogc:PropertyIsEqualTo> <ogc:PropertyName>pkuid</ogc:PropertyName> <ogc:Literal>{0}</ogc:Literal> </ogc:PropertyIsEqualTo> </ogc:Filter> <se:PointSymbolizer> <se:Graphic> <se:Mark> <se:WellKnownName>circle</se:WellKnownName> <se:Fill><se:SvgParameter name=\"fill\">5e86a1</se:SvgParameter></se:Fill><se:Stroke><se:SvgParameter name=\"stroke\">000000</se:SvgParameter></se:Stroke></se:Mark><se:Size>7</se:Size></se:Graphic></se:PointSymbolizer></se:Rule></se:FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>".format(literal)
|
||||
self.handle_request_wms_getmap(sld)
|
||||
self.assertTrue(self.is_point_table_still_exist())
|
||||
|
||||
def check_service_exception_report(self, d):
|
||||
"""
|
||||
Return True if a ServiceExceptionReport is raised, False otherwise
|
||||
"""
|
||||
|
||||
if b'<ServiceExceptionReport' in d:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def handle_request_wfs_getfeature_filter(self, filter_xml):
|
||||
qs = "&".join(["%s=%s" % i for i in list({
|
||||
"MAP": urllib.parse.quote(self.project),
|
||||
"SERVICE": "WFS",
|
||||
"VERSION": "1.1.1",
|
||||
"REQUEST": "GetFeature",
|
||||
"TYPENAME": "point",
|
||||
"STYLES": "",
|
||||
"CRS": "EPSG:32613",
|
||||
"FILTER": filter_xml}.items())])
|
||||
|
||||
return self.server.handleRequest(qs)
|
||||
|
||||
def handle_request_wms_getfeatureinfo(self, filter_sql):
|
||||
qs = "&".join(["%s=%s" % i for i in list({
|
||||
"MAP": urllib.parse.quote(self.project),
|
||||
"SERVICE": "WMS",
|
||||
"VERSION": "1.1.1",
|
||||
"REQUEST": "GetFeatureInfo",
|
||||
"QUERY_LAYERS": "point",
|
||||
"LAYERS": "point",
|
||||
"STYLES": "",
|
||||
"FORMAT": "image/png",
|
||||
"HEIGHT": "500",
|
||||
"WIDTH": "500",
|
||||
"BBOX": "606171,4822867,612834,4827375",
|
||||
"CRS": "EPSG:32613",
|
||||
"FILTER": filter_sql}.items())])
|
||||
|
||||
return self._result(self.server.handleRequest(qs))
|
||||
|
||||
def handle_request_wms_getmap(self, sld):
|
||||
qs = "&".join(["%s=%s" % i for i in list({
|
||||
"MAP": urllib.parse.quote(self.project),
|
||||
"SERVICE": "WMS",
|
||||
"VERSION": "1.0.0",
|
||||
"REQUEST": "GetMap",
|
||||
"QUERY_LAYERS": "point",
|
||||
"LAYERS": "point",
|
||||
"STYLES": "",
|
||||
"FORMAT": "image/png",
|
||||
"HEIGHT": "500",
|
||||
"WIDTH": "500",
|
||||
"BBOX": "606171,4822867,612834,4827375",
|
||||
"CRS": "EPSG:32613",
|
||||
"SLD": sld}.items())])
|
||||
|
||||
return self._result(self.server.handleRequest(qs))
|
||||
|
||||
def is_point_table_still_exist(self):
|
||||
conn = spatialite_connect(self.db_clone)
|
||||
cur = conn.cursor()
|
||||
sql = "select * from point"
|
||||
point_table_exist = True
|
||||
try:
|
||||
cur.execute(sql)
|
||||
except:
|
||||
point_table_exist = False
|
||||
conn.close()
|
||||
|
||||
return point_table_exist
|
||||
|
||||
def _result(self, data):
|
||||
headers = {}
|
||||
for line in data[0].decode('UTF-8').split("\n"):
|
||||
if line != "":
|
||||
header = line.split(":")
|
||||
self.assertEqual(len(header), 2, line)
|
||||
headers[str(header[0])] = str(header[1]).strip()
|
||||
|
||||
return data[1], headers
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
BIN
tests/testdata/qgis_server_security/db.sqlite
vendored
Normal file
BIN
tests/testdata/qgis_server_security/db.sqlite
vendored
Normal file
Binary file not shown.
490
tests/testdata/qgis_server_security/project.qgs
vendored
Normal file
490
tests/testdata/qgis_server_security/project.qgs
vendored
Normal file
@ -0,0 +1,490 @@
|
||||
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
|
||||
<qgis projectname="" version="2.99.0-Master">
|
||||
<title></title>
|
||||
<autotransaction active="0"/>
|
||||
<evaluateDefaultValues active="0"/>
|
||||
<layer-tree-group checked="Qt::Checked" expanded="1" name="">
|
||||
<customproperties/>
|
||||
<layer-tree-layer checked="Qt::Checked" id="point20170206172502668" expanded="1" name="point">
|
||||
<customproperties/>
|
||||
</layer-tree-layer>
|
||||
<layer-tree-layer checked="Qt::Checked" id="aoi20170206172502660" expanded="1" name="aoi">
|
||||
<customproperties/>
|
||||
</layer-tree-layer>
|
||||
<layer-tree-layer checked="Qt::Checked" id="background20170206172502667" expanded="1" name="background">
|
||||
<customproperties/>
|
||||
</layer-tree-layer>
|
||||
</layer-tree-group>
|
||||
<snapping-settings mode="2" enabled="0" intersection-snapping="0" type="1" unit="2" tolerance="0">
|
||||
<individual-layer-settings>
|
||||
<layer-setting enabled="0" id="aoi20170206172502660" type="1" tolerance="0" units="2"/>
|
||||
<layer-setting enabled="0" id="point20170206172502668" type="1" tolerance="0" units="2"/>
|
||||
<layer-setting enabled="0" id="background20170206172502667" type="1" tolerance="0" units="2"/>
|
||||
</individual-layer-settings>
|
||||
</snapping-settings>
|
||||
<relations/>
|
||||
<mapcanvas>
|
||||
<units>meters</units>
|
||||
<extent>
|
||||
<xmin>605206.40265044115949422</xmin>
|
||||
<ymin>4822097.18003295548260212</ymin>
|
||||
<xmax>613514.99562231602612883</xmax>
|
||||
<ymax>4827725.58172358199954033</ymax>
|
||||
</extent>
|
||||
<rotation>0</rotation>
|
||||
<projections>0</projections>
|
||||
<destinationsrs>
|
||||
<spatialrefsys>
|
||||
<proj4>+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs</proj4>
|
||||
<srsid>3097</srsid>
|
||||
<srid>32613</srid>
|
||||
<authid>EPSG:32613</authid>
|
||||
<description>WGS 84 / UTM zone 13N</description>
|
||||
<projectionacronym>utm</projectionacronym>
|
||||
<ellipsoidacronym>WGS84</ellipsoidacronym>
|
||||
<geographicflag>false</geographicflag>
|
||||
</spatialrefsys>
|
||||
</destinationsrs>
|
||||
<rendermaptile>0</rendermaptile>
|
||||
<layer_coordinate_transform_info/>
|
||||
</mapcanvas>
|
||||
<layer-tree-canvas>
|
||||
<custom-order enabled="0">
|
||||
<item>aoi20170206172502660</item>
|
||||
<item>background20170206172502667</item>
|
||||
<item>point20170206172502668</item>
|
||||
</custom-order>
|
||||
</layer-tree-canvas>
|
||||
<legend updateDrawingOrder="true">
|
||||
<legendlayer drawingOrder="-1" showFeatureCount="0" checked="Qt::Checked" open="true" name="point">
|
||||
<filegroup hidden="false" open="true">
|
||||
<legendlayerfile visible="1" layerid="point20170206172502668" isInOverview="0"/>
|
||||
</filegroup>
|
||||
</legendlayer>
|
||||
<legendlayer drawingOrder="-1" showFeatureCount="0" checked="Qt::Checked" open="true" name="aoi">
|
||||
<filegroup hidden="false" open="true">
|
||||
<legendlayerfile visible="1" layerid="aoi20170206172502660" isInOverview="0"/>
|
||||
</filegroup>
|
||||
</legendlayer>
|
||||
<legendlayer drawingOrder="-1" showFeatureCount="0" checked="Qt::Checked" open="true" name="background">
|
||||
<filegroup hidden="false" open="true">
|
||||
<legendlayerfile visible="1" layerid="background20170206172502667" isInOverview="0"/>
|
||||
</filegroup>
|
||||
</legendlayer>
|
||||
</legend>
|
||||
<projectlayers>
|
||||
<maplayer geometry="Polygon" simplifyAlgorithm="0" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0" type="vector" minimumScale="0" simplifyMaxScale="1" simplifyLocal="1" simplifyDrawingHints="1" simplifyDrawingTol="1" readOnly="0">
|
||||
<extent>
|
||||
<xmin>606510</xmin>
|
||||
<ymin>4823130</ymin>
|
||||
<xmax>612510</xmax>
|
||||
<ymax>4827130</ymax>
|
||||
</extent>
|
||||
<id>aoi20170206172502660</id>
|
||||
<datasource>dbname='./db_clone.sqlite' table="aoi" (geometry) sql=</datasource>
|
||||
<keywordList>
|
||||
<value></value>
|
||||
</keywordList>
|
||||
<layername>aoi</layername>
|
||||
<srs>
|
||||
<spatialrefsys>
|
||||
<proj4>+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs</proj4>
|
||||
<srsid>3097</srsid>
|
||||
<srid>32613</srid>
|
||||
<authid>EPSG:32613</authid>
|
||||
<description>WGS 84 / UTM zone 13N</description>
|
||||
<projectionacronym>utm</projectionacronym>
|
||||
<ellipsoidacronym>WGS84</ellipsoidacronym>
|
||||
<geographicflag>false</geographicflag>
|
||||
</spatialrefsys>
|
||||
</srs>
|
||||
<provider encoding="UTF-8">spatialite</provider>
|
||||
<vectorjoins/>
|
||||
<layerDependencies/>
|
||||
<defaults>
|
||||
<default field="pkuid" expression=""/>
|
||||
<default field="ftype" expression=""/>
|
||||
</defaults>
|
||||
<constraints>
|
||||
<constraint field="pkuid" unique_strength="1" exp_strength="0" constraints="3" notnull_strength="1"/>
|
||||
<constraint field="ftype" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
|
||||
</constraints>
|
||||
<constraintExpressions>
|
||||
<constraint exp="" field="pkuid" desc=""/>
|
||||
<constraint exp="" field="ftype" desc=""/>
|
||||
</constraintExpressions>
|
||||
<dataDependencies/>
|
||||
<expressionfields/>
|
||||
<map-layer-style-manager current="">
|
||||
<map-layer-style name=""/>
|
||||
</map-layer-style-manager>
|
||||
<edittypes/>
|
||||
<renderer-v2 forceraster="0" symbollevels="0" type="singleSymbol" enableorderby="0">
|
||||
<symbols>
|
||||
<symbol type="fill" alpha="1" clip_to_extent="1" name="0">
|
||||
<layer locked="0" enabled="1" class="SimpleFill" pass="0">
|
||||
<prop k="border_width_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||
<prop k="color" v="105,193,210,255"/>
|
||||
<prop k="joinstyle" v="bevel"/>
|
||||
<prop k="offset" v="0,0"/>
|
||||
<prop k="offset_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||
<prop k="offset_unit" v="MM"/>
|
||||
<prop k="outline_color" v="0,0,0,255"/>
|
||||
<prop k="outline_style" v="solid"/>
|
||||
<prop k="outline_width" v="0.26"/>
|
||||
<prop k="outline_width_unit" v="MM"/>
|
||||
<prop k="style" v="solid"/>
|
||||
</layer>
|
||||
</symbol>
|
||||
</symbols>
|
||||
<rotation/>
|
||||
<sizescale/>
|
||||
</renderer-v2>
|
||||
<labeling type="simple"/>
|
||||
<customproperties/>
|
||||
<blendMode>0</blendMode>
|
||||
<featureBlendMode>0</featureBlendMode>
|
||||
<layerTransparency>0</layerTransparency>
|
||||
<annotationform>.</annotationform>
|
||||
<aliases>
|
||||
<alias field="pkuid" index="0" name=""/>
|
||||
<alias field="ftype" index="1" name=""/>
|
||||
</aliases>
|
||||
<excludeAttributesWMS/>
|
||||
<excludeAttributesWFS/>
|
||||
<attributeactions>
|
||||
<defaultAction value="{00000000-0000-0000-0000-000000000000}" key="Canvas"/>
|
||||
</attributeactions>
|
||||
<attributetableconfig actionWidgetStyle="dropDown" sortExpression="" sortOrder="0">
|
||||
<columns>
|
||||
<column hidden="0" type="field" width="-1" name="pkuid"/>
|
||||
<column hidden="0" type="field" width="-1" name="ftype"/>
|
||||
<column hidden="1" type="actions" width="-1"/>
|
||||
</columns>
|
||||
</attributetableconfig>
|
||||
<editform>.</editform>
|
||||
<editforminit/>
|
||||
<editforminitcodesource>0</editforminitcodesource>
|
||||
<editforminitfilepath>.</editforminitfilepath>
|
||||
<editforminitcode><![CDATA[]]></editforminitcode>
|
||||
<featformsuppress>0</featformsuppress>
|
||||
<editorlayout>generatedlayout</editorlayout>
|
||||
<widgets/>
|
||||
<conditionalstyles>
|
||||
<rowstyles/>
|
||||
<fieldstyles/>
|
||||
</conditionalstyles>
|
||||
<expressionfields/>
|
||||
<previewExpression>"pkuid"</previewExpression>
|
||||
<mapTip></mapTip>
|
||||
</maplayer>
|
||||
<maplayer geometry="Polygon" simplifyAlgorithm="0" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0" type="vector" minimumScale="0" simplifyMaxScale="1" simplifyLocal="1" simplifyDrawingHints="1" simplifyDrawingTol="1" readOnly="0">
|
||||
<extent>
|
||||
<xmin>606410</xmin>
|
||||
<ymin>4823030</ymin>
|
||||
<xmax>612610</xmax>
|
||||
<ymax>4827230</ymax>
|
||||
</extent>
|
||||
<id>background20170206172502667</id>
|
||||
<datasource>dbname='./db_clone.sqlite' table="background" (geometry) sql=</datasource>
|
||||
<keywordList>
|
||||
<value></value>
|
||||
</keywordList>
|
||||
<layername>background</layername>
|
||||
<srs>
|
||||
<spatialrefsys>
|
||||
<proj4>+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs</proj4>
|
||||
<srsid>3097</srsid>
|
||||
<srid>32613</srid>
|
||||
<authid>EPSG:32613</authid>
|
||||
<description>WGS 84 / UTM zone 13N</description>
|
||||
<projectionacronym>utm</projectionacronym>
|
||||
<ellipsoidacronym>WGS84</ellipsoidacronym>
|
||||
<geographicflag>false</geographicflag>
|
||||
</spatialrefsys>
|
||||
</srs>
|
||||
<provider encoding="UTF-8">spatialite</provider>
|
||||
<vectorjoins/>
|
||||
<layerDependencies/>
|
||||
<defaults>
|
||||
<default field="pkuid" expression=""/>
|
||||
<default field="text" expression=""/>
|
||||
</defaults>
|
||||
<constraints>
|
||||
<constraint field="pkuid" unique_strength="1" exp_strength="0" constraints="3" notnull_strength="1"/>
|
||||
<constraint field="text" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
|
||||
</constraints>
|
||||
<constraintExpressions>
|
||||
<constraint exp="" field="pkuid" desc=""/>
|
||||
<constraint exp="" field="text" desc=""/>
|
||||
</constraintExpressions>
|
||||
<dataDependencies/>
|
||||
<expressionfields/>
|
||||
<map-layer-style-manager current="">
|
||||
<map-layer-style name=""/>
|
||||
</map-layer-style-manager>
|
||||
<edittypes/>
|
||||
<renderer-v2 forceraster="0" symbollevels="0" type="singleSymbol" enableorderby="0">
|
||||
<symbols>
|
||||
<symbol type="fill" alpha="1" clip_to_extent="1" name="0">
|
||||
<layer locked="0" enabled="1" class="SimpleFill" pass="0">
|
||||
<prop k="border_width_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||
<prop k="color" v="141,51,82,255"/>
|
||||
<prop k="joinstyle" v="bevel"/>
|
||||
<prop k="offset" v="0,0"/>
|
||||
<prop k="offset_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||
<prop k="offset_unit" v="MM"/>
|
||||
<prop k="outline_color" v="0,0,0,255"/>
|
||||
<prop k="outline_style" v="solid"/>
|
||||
<prop k="outline_width" v="0.26"/>
|
||||
<prop k="outline_width_unit" v="MM"/>
|
||||
<prop k="style" v="solid"/>
|
||||
</layer>
|
||||
</symbol>
|
||||
</symbols>
|
||||
<rotation/>
|
||||
<sizescale/>
|
||||
</renderer-v2>
|
||||
<labeling type="simple"/>
|
||||
<customproperties/>
|
||||
<blendMode>0</blendMode>
|
||||
<featureBlendMode>0</featureBlendMode>
|
||||
<layerTransparency>0</layerTransparency>
|
||||
<annotationform>.</annotationform>
|
||||
<aliases>
|
||||
<alias field="pkuid" index="0" name=""/>
|
||||
<alias field="text" index="1" name=""/>
|
||||
</aliases>
|
||||
<excludeAttributesWMS/>
|
||||
<excludeAttributesWFS/>
|
||||
<attributeactions>
|
||||
<defaultAction value="{00000000-0000-0000-0000-000000000000}" key="Canvas"/>
|
||||
</attributeactions>
|
||||
<attributetableconfig actionWidgetStyle="dropDown" sortExpression="" sortOrder="0">
|
||||
<columns>
|
||||
<column hidden="0" type="field" width="-1" name="pkuid"/>
|
||||
<column hidden="0" type="field" width="-1" name="text"/>
|
||||
<column hidden="1" type="actions" width="-1"/>
|
||||
</columns>
|
||||
</attributetableconfig>
|
||||
<editform>.</editform>
|
||||
<editforminit/>
|
||||
<editforminitcodesource>0</editforminitcodesource>
|
||||
<editforminitfilepath>.</editforminitfilepath>
|
||||
<editforminitcode><![CDATA[]]></editforminitcode>
|
||||
<featformsuppress>0</featformsuppress>
|
||||
<editorlayout>generatedlayout</editorlayout>
|
||||
<widgets/>
|
||||
<conditionalstyles>
|
||||
<rowstyles/>
|
||||
<fieldstyles/>
|
||||
</conditionalstyles>
|
||||
<expressionfields/>
|
||||
<previewExpression>"pkuid"</previewExpression>
|
||||
<mapTip></mapTip>
|
||||
</maplayer>
|
||||
<maplayer geometry="Point" simplifyAlgorithm="0" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0" type="vector" minimumScale="0" simplifyMaxScale="1" simplifyLocal="1" simplifyDrawingHints="1" simplifyDrawingTol="1" readOnly="0">
|
||||
<extent>
|
||||
<xmin>609510</xmin>
|
||||
<ymin>4825130</ymin>
|
||||
<xmax>609510</xmax>
|
||||
<ymax>4825130</ymax>
|
||||
</extent>
|
||||
<id>point20170206172502668</id>
|
||||
<datasource>dbname='./db_clone.sqlite' table="point" (geometry) sql=</datasource>
|
||||
<keywordList>
|
||||
<value></value>
|
||||
</keywordList>
|
||||
<layername>point</layername>
|
||||
<srs>
|
||||
<spatialrefsys>
|
||||
<proj4>+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs</proj4>
|
||||
<srsid>3097</srsid>
|
||||
<srid>32613</srid>
|
||||
<authid>EPSG:32613</authid>
|
||||
<description>WGS 84 / UTM zone 13N</description>
|
||||
<projectionacronym>utm</projectionacronym>
|
||||
<ellipsoidacronym>WGS84</ellipsoidacronym>
|
||||
<geographicflag>false</geographicflag>
|
||||
</spatialrefsys>
|
||||
</srs>
|
||||
<provider encoding="UTF-8">spatialite</provider>
|
||||
<vectorjoins/>
|
||||
<layerDependencies/>
|
||||
<defaults>
|
||||
<default field="pkuid" expression=""/>
|
||||
<default field="text" expression=""/>
|
||||
<default field="name" expression=""/>
|
||||
</defaults>
|
||||
<constraints>
|
||||
<constraint field="pkuid" unique_strength="1" exp_strength="0" constraints="3" notnull_strength="1"/>
|
||||
<constraint field="text" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
|
||||
<constraint field="name" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
|
||||
</constraints>
|
||||
<constraintExpressions>
|
||||
<constraint exp="" field="pkuid" desc=""/>
|
||||
<constraint exp="" field="text" desc=""/>
|
||||
<constraint exp="" field="name" desc=""/>
|
||||
</constraintExpressions>
|
||||
<dataDependencies/>
|
||||
<expressionfields/>
|
||||
<map-layer-style-manager current="">
|
||||
<map-layer-style name=""/>
|
||||
</map-layer-style-manager>
|
||||
<edittypes/>
|
||||
<renderer-v2 forceraster="0" symbollevels="0" type="singleSymbol" enableorderby="0">
|
||||
<symbols>
|
||||
<symbol type="marker" alpha="1" clip_to_extent="1" name="0">
|
||||
<layer locked="0" enabled="1" class="SimpleMarker" pass="0">
|
||||
<prop k="angle" v="0"/>
|
||||
<prop k="color" v="85,91,195,255"/>
|
||||
<prop k="horizontal_anchor_point" v="1"/>
|
||||
<prop k="joinstyle" v="bevel"/>
|
||||
<prop k="name" v="circle"/>
|
||||
<prop k="offset" v="0,0"/>
|
||||
<prop k="offset_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||
<prop k="offset_unit" v="MM"/>
|
||||
<prop k="outline_color" v="0,0,0,255"/>
|
||||
<prop k="outline_style" v="solid"/>
|
||||
<prop k="outline_width" v="0"/>
|
||||
<prop k="outline_width_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||
<prop k="outline_width_unit" v="MM"/>
|
||||
<prop k="scale_method" v="diameter"/>
|
||||
<prop k="size" v="2"/>
|
||||
<prop k="size_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||
<prop k="size_unit" v="MM"/>
|
||||
<prop k="vertical_anchor_point" v="1"/>
|
||||
</layer>
|
||||
</symbol>
|
||||
</symbols>
|
||||
<rotation/>
|
||||
<sizescale/>
|
||||
</renderer-v2>
|
||||
<labeling type="simple"/>
|
||||
<customproperties/>
|
||||
<blendMode>0</blendMode>
|
||||
<featureBlendMode>0</featureBlendMode>
|
||||
<layerTransparency>0</layerTransparency>
|
||||
<annotationform>.</annotationform>
|
||||
<aliases>
|
||||
<alias field="pkuid" index="0" name=""/>
|
||||
<alias field="text" index="1" name=""/>
|
||||
<alias field="name" index="2" name=""/>
|
||||
</aliases>
|
||||
<excludeAttributesWMS/>
|
||||
<excludeAttributesWFS/>
|
||||
<attributeactions>
|
||||
<defaultAction value="{00000000-0000-0000-0000-000000000000}" key="Canvas"/>
|
||||
</attributeactions>
|
||||
<attributetableconfig actionWidgetStyle="dropDown" sortExpression="" sortOrder="0">
|
||||
<columns>
|
||||
<column hidden="0" type="field" width="-1" name="pkuid"/>
|
||||
<column hidden="0" type="field" width="-1" name="text"/>
|
||||
<column hidden="1" type="actions" width="-1"/>
|
||||
</columns>
|
||||
</attributetableconfig>
|
||||
<editform>.</editform>
|
||||
<editforminit/>
|
||||
<editforminitcodesource>0</editforminitcodesource>
|
||||
<editforminitfilepath>.</editforminitfilepath>
|
||||
<editforminitcode><![CDATA[]]></editforminitcode>
|
||||
<featformsuppress>0</featformsuppress>
|
||||
<editorlayout>generatedlayout</editorlayout>
|
||||
<widgets/>
|
||||
<conditionalstyles>
|
||||
<rowstyles/>
|
||||
<fieldstyles/>
|
||||
</conditionalstyles>
|
||||
<expressionfields/>
|
||||
<previewExpression>"pkuid"</previewExpression>
|
||||
<mapTip></mapTip>
|
||||
</maplayer>
|
||||
</projectlayers>
|
||||
<properties>
|
||||
<Macros>
|
||||
<pythonCode type="QString"></pythonCode>
|
||||
</Macros>
|
||||
<WMSOnlineResource type="QString"></WMSOnlineResource>
|
||||
<WMSKeywordList type="QStringList">
|
||||
<value></value>
|
||||
</WMSKeywordList>
|
||||
<Identify>
|
||||
<disabledLayers type="QStringList"/>
|
||||
</Identify>
|
||||
<WMSContactOrganization type="QString"></WMSContactOrganization>
|
||||
<Measurement>
|
||||
<AreaUnits type="QString">m2</AreaUnits>
|
||||
<DistanceUnits type="QString">meters</DistanceUnits>
|
||||
</Measurement>
|
||||
<WMSContactMail type="QString"></WMSContactMail>
|
||||
<WFSLayersPrecision>
|
||||
<point20170206172502668 type="int">8</point20170206172502668>
|
||||
</WFSLayersPrecision>
|
||||
<WMSContactPerson type="QString"></WMSContactPerson>
|
||||
<DefaultStyles>
|
||||
<AlphaInt type="int">255</AlphaInt>
|
||||
<ColorRamp type="QString"></ColorRamp>
|
||||
<Line type="QString"></Line>
|
||||
<RandomColors type="bool">true</RandomColors>
|
||||
<Fill type="QString"></Fill>
|
||||
<Marker type="QString"></Marker>
|
||||
</DefaultStyles>
|
||||
<Legend>
|
||||
<filterByMap type="bool">false</filterByMap>
|
||||
</Legend>
|
||||
<Paths>
|
||||
<Absolute type="bool">false</Absolute>
|
||||
</Paths>
|
||||
<WMSFees type="QString">conditions unknown</WMSFees>
|
||||
<WFSLayers type="QStringList">
|
||||
<value>point20170206172502668</value>
|
||||
</WFSLayers>
|
||||
<Gui>
|
||||
<CanvasColorBluePart type="int">255</CanvasColorBluePart>
|
||||
<CanvasColorRedPart type="int">255</CanvasColorRedPart>
|
||||
<SelectionColorRedPart type="int">255</SelectionColorRedPart>
|
||||
<SelectionColorBluePart type="int">0</SelectionColorBluePart>
|
||||
<SelectionColorAlphaPart type="int">255</SelectionColorAlphaPart>
|
||||
<CanvasColorGreenPart type="int">255</CanvasColorGreenPart>
|
||||
<SelectionColorGreenPart type="int">255</SelectionColorGreenPart>
|
||||
</Gui>
|
||||
<WMSAccessConstraints type="QString">None</WMSAccessConstraints>
|
||||
<WMSRestrictedLayers type="QStringList">
|
||||
<value>aoi</value>
|
||||
</WMSRestrictedLayers>
|
||||
<WMSContactPosition type="QString"></WMSContactPosition>
|
||||
<WMSImageQuality type="int">90</WMSImageQuality>
|
||||
<WMSRequestDefinedDataSources type="bool">false</WMSRequestDefinedDataSources>
|
||||
<WMSSegmentizeFeatureInfoGeometry type="bool">false</WMSSegmentizeFeatureInfoGeometry>
|
||||
<Measure>
|
||||
<Ellipsoid type="QString">NONE</Ellipsoid>
|
||||
</Measure>
|
||||
<WMSAddWktGeometry type="bool">false</WMSAddWktGeometry>
|
||||
<PositionPrecision>
|
||||
<DegreeFormat type="QString">MU</DegreeFormat>
|
||||
<Automatic type="bool">true</Automatic>
|
||||
<DecimalPlaces type="int">2</DecimalPlaces>
|
||||
</PositionPrecision>
|
||||
<WMSServiceAbstract type="QString"></WMSServiceAbstract>
|
||||
<WMSUseLayerIDs type="bool">false</WMSUseLayerIDs>
|
||||
<WFSTLayers>
|
||||
<Delete type="QStringList"/>
|
||||
<Update type="QStringList"/>
|
||||
<Insert type="QStringList"/>
|
||||
</WFSTLayers>
|
||||
<SpatialRefSys>
|
||||
<ProjectCRSID type="int">3097</ProjectCRSID>
|
||||
<ProjectCRSProj4String type="QString">+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs</ProjectCRSProj4String>
|
||||
<ProjectCrs type="QString">EPSG:32613</ProjectCrs>
|
||||
</SpatialRefSys>
|
||||
<WMSServiceTitle type="QString"></WMSServiceTitle>
|
||||
<WMSPrecision type="QString">8</WMSPrecision>
|
||||
<WCSUrl type="QString"></WCSUrl>
|
||||
<WCSLayers type="QStringList"/>
|
||||
<WMSContactPhone type="QString"></WMSContactPhone>
|
||||
<WFSUrl type="QString"></WFSUrl>
|
||||
<WMSServiceCapabilities type="bool">true</WMSServiceCapabilities>
|
||||
<WMSUrl type="QString"></WMSUrl>
|
||||
</properties>
|
||||
<visibility-presets/>
|
||||
</qgis>
|
Loading…
x
Reference in New Issue
Block a user