Skip to content

Commit b23e29f

Browse files
authored
Merge pull request #3716 from elpaso/auth_tests_more_2_18
More Authentication Tests
2 parents 0796ecb + 9c535b5 commit b23e29f

File tree

8 files changed

+531
-21
lines changed

8 files changed

+531
-21
lines changed

ci/travis/linux/qt5/blacklist.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ PyQgsMapUnitScale
66
PyQgsPalLabelingServer
77
PyQgsRelationEditWidget
88
PyQgsServer
9-
PyQgsAuthManagerEndpointTest
9+
PyQgsAuthManagerPasswordOWSTest
1010
PyQgsServerAccessControl
1111
PyQgsSipCoverage
1212
PyQgsSpatialiteProvider

tests/src/python/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,7 @@ IF (WITH_SERVER)
150150
ADD_PYTHON_TEST(PyQgsServerAccessControl test_qgsserver_accesscontrol.py)
151151
ADD_PYTHON_TEST(PyQgsServerWFST test_qgsserver_wfst.py)
152152
ADD_PYTHON_TEST(PyQgsOfflineEditingWFS test_offline_editing_wfs.py)
153-
ADD_PYTHON_TEST(PyQgsAuthManagerEndpointTest test_authmanager_endpoint.py)
153+
ADD_PYTHON_TEST(PyQgsAuthManagerPasswordOWSTest test_authmanager_password_ows.py)
154+
#ADD_PYTHON_TEST(PyQgsAuthManagerPKIOWSTest test_authmanager_pki_ows.py)
155+
ADD_PYTHON_TEST(PyQgsAuthManagerPKIPostgresTest test_authmanager_pki_postgres.py)
154156
ENDIF (WITH_SERVER)

tests/src/python/qgis_wrapped_server.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@
1313
* QGIS_SERVER_USERNAME (default ="username")
1414
* QGIS_SERVER_PASSWORD (default ="password")
1515
16+
PKI authentication with HTTPS can be enabled with:
17+
18+
* QGIS_SERVER_PKI_CERTIFICATE (server certificate)
19+
* QGIS_SERVER_PKI_KEY (server private key)
20+
* QGIS_SERVER_PKI_AUTHORITY (root CA)
21+
* QGIS_SERVER_PKI_USERNAME (valid username)
22+
23+
Sample run:
24+
25+
QGIS_SERVER_PKI_USERNAME=Gerardus QGIS_SERVER_PORT=47547 QGIS_SERVER_HOST=localhost \
26+
QGIS_SERVER_PKI_KEY=/home/dev/QGIS/tests/testdata/auth_system/certs_keys/localhost_ssl_key.pem \
27+
QGIS_SERVER_PKI_CERTIFICATE=/home/dev/QGIS/tests/testdata/auth_system/certs_keys/localhost_ssl_cert.pem \
28+
QGIS_SERVER_PKI_AUTHORITY=/home/dev/QGIS/tests/testdata/auth_system/certs_keys/chains_subissuer-issuer-root_issuer2-root2.pem \
29+
python /home/dev/QGIS/tests/src/python/qgis_wrapped_server.py
30+
1631
.. note:: This program is free software; you can redistribute it and/or modify
1732
it under the terms of the GNU General Public License as published by
1833
the Free Software Foundation; either version 2 of the License, or
@@ -31,12 +46,27 @@
3146

3247
import os
3348
import sys
49+
import ssl
3450
import urllib.parse
3551
from http.server import BaseHTTPRequestHandler, HTTPServer
3652
from qgis.server import QgsServer, QgsServerFilter
3753

3854
QGIS_SERVER_PORT = int(os.environ.get('QGIS_SERVER_PORT', '8081'))
3955
QGIS_SERVER_HOST = os.environ.get('QGIS_SERVER_HOST', '127.0.0.1')
56+
# PKI authentication
57+
QGIS_SERVER_PKI_CERTIFICATE = os.environ.get('QGIS_SERVER_PKI_CERTIFICATE')
58+
QGIS_SERVER_PKI_KEY = os.environ.get('QGIS_SERVER_PKI_KEY')
59+
QGIS_SERVER_PKI_AUTHORITY = os.environ.get('QGIS_SERVER_PKI_AUTHORITY')
60+
QGIS_SERVER_PKI_USERNAME = os.environ.get('QGIS_SERVER_PKI_USERNAME')
61+
62+
# Check if PKI - https is enabled
63+
https = (QGIS_SERVER_PKI_CERTIFICATE is not None and
64+
os.path.isfile(QGIS_SERVER_PKI_CERTIFICATE) and
65+
QGIS_SERVER_PKI_KEY is not None and
66+
os.path.isfile(QGIS_SERVER_PKI_KEY) and
67+
QGIS_SERVER_PKI_AUTHORITY is not None and
68+
os.path.isfile(QGIS_SERVER_PKI_AUTHORITY) and
69+
QGIS_SERVER_PKI_USERNAME)
4070

4171
qgs_server = QgsServer()
4272

@@ -66,8 +96,20 @@ def responseComplete(self):
6696
class Handler(BaseHTTPRequestHandler):
6797

6898
def do_GET(self):
99+
# For PKI: check the username from client certificate
100+
if https:
101+
try:
102+
ssl.match_hostname(self.connection.getpeercert(), QGIS_SERVER_PKI_USERNAME)
103+
except Exception as ex:
104+
print("SSL Exception %s" % ex)
105+
self.send_response(401)
106+
self.end_headers()
107+
self.wfile.write('UNAUTHORIZED')
108+
return
69109
# CGI vars:
70110
for k, v in self.headers.items():
111+
# Uncomment to print debug info about env vars passed into QGIS Server env
112+
#print('Setting ENV var %s to %s' % ('HTTP_%s' % k.replace(' ', '-').replace('-', '_').replace(' ', '-').upper(), v))
71113
qgs_server.putenv('HTTP_%s' % k.replace(' ', '-').replace('-', '_').replace(' ', '-').upper(), v)
72114
qgs_server.putenv('SERVER_PORT', str(self.server.server_port))
73115
qgs_server.putenv('SERVER_NAME', self.server.server_name)
@@ -96,7 +138,19 @@ def do_POST(self):
96138

97139
if __name__ == '__main__':
98140
server = HTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler)
99-
print('Starting server on %s:%s, use <Ctrl-C> to stop' %
100-
(QGIS_SERVER_HOST, server.server_port))
101-
sys.stdout.flush()
141+
if https:
142+
server.socket = ssl.wrap_socket(server.socket,
143+
certfile=QGIS_SERVER_PKI_CERTIFICATE,
144+
keyfile=QGIS_SERVER_PKI_KEY,
145+
ca_certs=QGIS_SERVER_PKI_AUTHORITY,
146+
cert_reqs=ssl.CERT_REQUIRED,
147+
server_side=True,
148+
ssl_version=ssl.PROTOCOL_TLSv1)
149+
message = 'Starting server on %s://%s:%s, use <Ctrl-C> to stop' % \
150+
('https' if https else 'http', QGIS_SERVER_HOST, server.server_port)
151+
try:
152+
print(message, flush=True)
153+
except:
154+
print(message)
155+
sys.stdout.flush()
102156
server.serve_forever()

tests/src/python/test_authmanager_endpoint.py renamed to tests/src/python/test_authmanager_password_ows.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
configuration to access an HTTP Basic protected endpoint.
99
1010
11-
From build dir, run: ctest -R PyQgsAuthManagerEnpointTest -V
11+
From build dir, run: ctest -R PyQgsAuthManagerPasswordOWSTest -V
1212
1313
.. note:: This program is free software; you can redistribute it and/or modify
1414
it under the terms of the GNU General Public License as published by
@@ -22,7 +22,10 @@
2222
import tempfile
2323
import random
2424
import string
25-
import urllib
25+
try:
26+
from urllib.parse import quote
27+
except:
28+
from urllib import quote
2629

2730
__author__ = 'Alessandro Pasotti'
2831
__date__ = '18/09/2016'
@@ -47,7 +50,7 @@
4750
try:
4851
QGIS_SERVER_ENDPOINT_PORT = os.environ['QGIS_SERVER_ENDPOINT_PORT']
4952
except:
50-
QGIS_SERVER_ENDPOINT_PORT = '0' # Auto
53+
QGIS_SERVER_ENDPOINT_PORT = '0' # Auto
5154

5255

5356
QGIS_AUTH_DB_DIR_PATH = tempfile.mkdtemp()
@@ -84,11 +87,14 @@ def setUpClass(cls):
8487
cls.auth_config.setConfig('username', cls.username)
8588
cls.auth_config.setConfig('password', cls.password)
8689
assert (authm.storeAuthenticationConfig(cls.auth_config)[0])
90+
cls.hostname = '127.0.0.1'
91+
cls.protocol = 'http'
8792

8893
os.environ['QGIS_SERVER_HTTP_BASIC_AUTH'] = '1'
8994
os.environ['QGIS_SERVER_USERNAME'] = cls.username
9095
os.environ['QGIS_SERVER_PASSWORD'] = cls.password
9196
os.environ['QGIS_SERVER_PORT'] = str(cls.port)
97+
os.environ['QGIS_SERVER_HOST'] = cls.hostname
9298
server_path = os.path.dirname(os.path.realpath(__file__)) + \
9399
'/qgis_wrapped_server.py'
94100
cls.server = subprocess.Popen([sys.executable, server_path],
@@ -98,7 +104,7 @@ def setUpClass(cls):
98104
cls.port = int(re.findall(b':(\d+)', line)[0])
99105
assert cls.port != 0
100106
# Wait for the server process to start
101-
assert waitServer('http://127.0.0.1:%s' % cls.port), "Server is not responding! http://127.0.0.1:%s" % cls.port
107+
assert waitServer('%s://%s:%s' % (cls.protocol, cls.hostname, cls.port)), "Server is not responding! '%s://%s:%s" % (cls.protocol, cls.hostname, cls.port)
102108

103109
@classmethod
104110
def tearDownClass(cls):
@@ -125,13 +131,16 @@ def _getWFSLayer(cls, type_name, layer_name=None, authcfg=None):
125131
parms = {
126132
'srsname': 'EPSG:4326',
127133
'typename': type_name,
128-
'url': 'http://127.0.0.1:%s/?map=%s' % (cls.port, cls.project_path),
134+
'url': '%s://%s:%s/?map=%s' % (cls.protocol, cls.hostname, cls.port, cls.project_path),
129135
'version': 'auto',
130136
'table': '',
131137
}
132138
if authcfg is not None:
133139
parms.update({'authcfg': authcfg})
134-
uri = ' '.join([("%s='%s'" % (k, v.decode('utf-8'))) for k, v in list(parms.items())])
140+
try: # Py2
141+
uri = ' '.join([("%s='%s'" % (k, v.decode('utf-8'))) for k, v in list(parms.items())])
142+
except AttributeError: # Py3
143+
uri = ' '.join([("%s='%s'" % (k, v)) for k, v in list(parms.items())])
135144
wfs_layer = QgsVectorLayer(uri, layer_name, 'WFS')
136145
return wfs_layer
137146

@@ -144,11 +153,11 @@ def _getWMSLayer(cls, layers, layer_name=None, authcfg=None):
144153
layer_name = 'wms_' + layers.replace(',', '')
145154
parms = {
146155
'crs': 'EPSG:4326',
147-
'url': 'http://127.0.0.1:%s/?map=%s' % (cls.port, cls.project_path),
156+
'url': '%s://%s:%s/?map=%s' % (cls.protocol, cls.hostname, cls.port, cls.project_path),
148157
'format': 'image/png',
149158
# This is needed because of a really weird implementation in QGIS Server, that
150159
# replaces _ in the the real layer name with spaces
151-
'layers': urllib.quote(layers.replace('_', ' ')),
160+
'layers': quote(layers.replace('_', ' ')),
152161
'styles': '',
153162
'version': 'auto',
154163
#'sql': '',

0 commit comments

Comments
 (0)