diff --git a/configure.ac b/configure.ac index ebb60a6c0dd3b7618b0971dd4f29e01bf865f7ef..764afdcc8237bdd9e7afb4282eb5b318beb7eb62 100644 --- a/configure.ac +++ b/configure.ac @@ -93,7 +93,7 @@ dnl Python-dev (actually only used for $PYTHON) AX_PYTHON_DEVEL dnl nosetests -AC_PATH_PROG([NOSETESTS], [nosetests]) +AC_PATH_PROG([NOSETESTS], [nosetests${PYTHON_VERSION}]) dnl GoogleTest (use the embedded version) GTEST_LIBS="\$(top_builddir)/lib/gtest/libgtest.la" diff --git a/debian/compat b/debian/compat index ec635144f60048986bc560c5576355344005e6e7..f599e28b8ab0d8c9c57a486c89c4a5132dcbd3b2 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -9 +10 diff --git a/debian/control b/debian/control index c27a7673075d5d0a3071329f26d64a638e4b5cf5..8d23064e2a89082ae83c67d56e719d416049f67f 100644 --- a/debian/control +++ b/debian/control @@ -3,9 +3,9 @@ Section: net Priority: extra Maintainer: Autistici/Inventati <debian@autistici.org> Build-Depends: debhelper (>= 10), apache2-dev | apache2-prefork-dev | apache2-threaded-dev, - apache2, autoconf, automake, libtool, python-dev, dh-python, python-all, - libpam-dev, libssl-dev, python-setuptools, python-flup, pkg-config, libz-dev, - python-m2crypto, python-flask, python-nose, python-mox, python-beautifulsoup + apache2, autoconf, automake, libtool, python3-dev, dh-python, python3-all, + libpam-dev, libssl-dev, python3-setuptools, pkg-config, libz-dev, + python3-requests, python3-nose, python3-mox Standards-Version: 3.7.2 Package: ai-sso diff --git a/debian/rules b/debian/rules index ce37677bfdd763473e45a44cbe90c50fb97d0a87..f606ef66890f1788e36aea4eccfc0210f15b8555 100755 --- a/debian/rules +++ b/debian/rules @@ -9,7 +9,7 @@ override_dh_auto_configure: sh autogen.sh - ./configure --prefix=/usr --with-pam-dir=/lib/security --enable-pam-sso --enable-mod-sso --enable-shared + ./configure --prefix=/usr --with-pam-dir=/lib/security --enable-pam-sso --enable-mod-sso --enable-shared PYTHON_VERSION=3 override_dh_auto_install: install -d $(CURDIR)/debian/tmp/etc/sso diff --git a/src/Makefile.am b/src/Makefile.am index 4e52d79e7a429fa5a9d89aea7e06d15d4dd38c19..8e6236b36d900a35445a19e51725b7f52e9265c0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,7 @@ if ENABLE_PAM_SSO PAM_SSO_SUBDIR = pam_sso endif -PYTHON_SUBDIR = python sso_server +PYTHON_SUBDIR = python #sso_server SUBDIRS = \ sso \ diff --git a/src/mod_sso/test/httpd_integration_test.py b/src/mod_sso/test/httpd_integration_test.py index 38079f56219c3658f1e9bf68b6e615a9e2ba2b2c..996b667e0c9e3ca5e3a7b7ed01556653208c3f8d 100755 --- a/src/mod_sso/test/httpd_integration_test.py +++ b/src/mod_sso/test/httpd_integration_test.py @@ -1,15 +1,13 @@ #!/usr/bin/python -import Cookie -import httplib import os import re +import requests import subprocess import sys import time import unittest -import urllib -import urlparse +from urllib.parse import urlencode, urlsplit, parse_qsl sys.path.append("../../python") import sso @@ -31,7 +29,7 @@ devnull = open(os.devnull) def _start_httpd(public_key, config_file): - with open('public.key', 'w') as fd: + with open('public.key', 'wb') as fd: fd.write(public_key) env = dict(os.environ) env['TESTROOT'] = os.getcwd() @@ -58,7 +56,7 @@ def _start_httpd(public_key, config_file): return httpd -def _stop_httpd(httpd): +def _stop_httpd(httpd, dump_log=False): httpd.terminate() time.sleep(1) try: @@ -66,23 +64,25 @@ def _stop_httpd(httpd): except OSError: pass - dump_log = (os.getenv('DUMP_APACHE_LOG') == '1') + if os.getenv('DUMP_APACHE_LOG'): + dump_log = True status = httpd.wait() if os.WIFEXITED(status): if os.WEXITSTATUS(status) != 0: - print 'WARNING: httpd exited with status %d' % os.WEXITSTATUS(status) + print('WARNING: httpd exited with status %d' % os.WEXITSTATUS(status)) dump_log = True elif os.WIFSIGNALED(status): - print 'WARNING: httpd exited due to signal %d' % os.WTERMSIG(status) + print('WARNING: httpd exited due to signal %d' % os.WTERMSIG(status)) dump_log = True else: - print 'WARNING: httpd exited for unknown reason (returncode=%d)' % status + print('WARNING: httpd exited for unknown reason (returncode=%d)' % status) dump_log = True if dump_log: + print('*** APACHE LOG ***') with open(APACHE_LOG) as fd: for line in fd: - print line.rstrip('\n') + print(line.rstrip('\n')) for f in ('public.key', 'test-httpd.pid', '.apache_log'): try: @@ -93,20 +93,20 @@ def _stop_httpd(httpd): def _query(url, host=None, cookie=None): """Make a simple request to url using httplib.""" - conn = httplib.HTTPConnection("127.0.0.1", APACHE_PORT) headers = {"Host": host or "localhost"} if cookie: - headers["Cookie"] = cookie - conn.request("GET", url, headers=headers) - resp = conn.getresponse() + headers['Cookie'] = cookie + resp = requests.get( + f'http://127.0.0.1:{APACHE_PORT}{url}', + headers=headers, + allow_redirects=False) location = None body = None - if resp.status in (301, 302): - location = resp.getheader("Location") - elif resp.status == 200: - body = resp.read() - conn.close() - return (resp.status, body, location) + if resp.status_code in (301, 302): + location = resp.headers["Location"] + elif resp.status_code == 200: + body = resp.text + return (resp.status_code, body, location) def mkcookie(tkt): @@ -116,9 +116,9 @@ def mkcookie(tkt): class HttpdKeyLoadingTest(unittest.TestCase): def test_key_with_null_byte(self): - has_null_byte = lambda s: '\0' in s + has_null_byte = lambda s: 0 in s - public = '' + public = b'' while not has_null_byte(public): public, secret = sso.generate_keys() @@ -138,6 +138,7 @@ class HttpdKeyLoadingTest(unittest.TestCase): class HttpdIntegrationTestBase(unittest.TestCase): CONFIG = None + DUMP_LOG = False def setUp(self): self.public, self.secret = sso.generate_keys() @@ -145,7 +146,7 @@ class HttpdIntegrationTestBase(unittest.TestCase): self.httpd = _start_httpd(self.public, self.CONFIG) def tearDown(self): - _stop_httpd(self.httpd) + _stop_httpd(self.httpd, self.DUMP_LOG) def _ticket(self, user="testuser", group="group1", service="service.example.com/", nonce=None): @@ -238,8 +239,8 @@ class HttpdIntegrationTestBase(unittest.TestCase): "status": 401 }), ] for name, check in checks: - for i in xrange(repeats): - print 'CHECKING %s (%d of 10)' % (name, i), check + for i in range(repeats): + print('CHECKING %s (%d of 10)' % (name, i), check) status, body, location = _query(check["url"], host=check.get("http_host"), cookie=check.get("cookie")) @@ -268,48 +269,41 @@ class HttpdIntegrationTestBase(unittest.TestCase): if not sso_login_url: sso_login_url = '/%s/sso_login' % sso_service.split('/', 1)[1] - cookies = Cookie.SimpleCookie() + sess = requests.Session() # Make a request to a protected URL. - conn = httplib.HTTPConnection("127.0.0.1", APACHE_PORT) - conn.request("GET", url, headers={'Host': host_header}) - resp = conn.getresponse() - self.assertEquals(302, resp.status) - set_cookie_hdr = resp.getheader("Set-Cookie") - self.assertTrue(set_cookie_hdr) - cookies.load(set_cookie_hdr) - location = resp.getheader("Location") - location_u = urlparse.urlsplit(location) - location_args = dict(urlparse.parse_qsl(location_u.query)) + resp = sess.get( + f'http://127.0.0.1:{APACHE_PORT}{url}', + headers={'Host': host_header}, + allow_redirects=False) + self.assertEquals(302, resp.status_code) + location = resp.headers["Location"] + location_u = urlsplit(location) + location_args = dict(parse_qsl(location_u.query)) nonce = location_args.get('n') self.assertTrue(nonce) - conn.close() + session_cookie = resp.cookies['_sso_local_session'] # Now call the /sso_login endpoint. - conn = httplib.HTTPConnection("127.0.0.1", APACHE_PORT) tkt = self._ticket(nonce=nonce, service=sso_service) - conn.request("GET", sso_login_url + "?" + urllib.urlencode( - {"t": tkt, "d": "https://" + host_header + url}), headers={ - "Cookie": cookies.output(attrs=[], header='', sep='; '), - "Host": host_header, - }) - resp = conn.getresponse() - self.assertEquals(302, resp.status) - set_cookie_hdr = resp.getheader("Set-Cookie") - self.assertTrue(set_cookie_hdr) - cookies.load(set_cookie_hdr) - self.assertEquals(tkt, cookies['SSO_test'].value) - conn.close() + query_args = urlencode({"t": tkt, "d": "https://" + host_header + url}) + resp = sess.get( + f'http://127.0.0.1:{APACHE_PORT}{sso_login_url}?{query_args}', + headers={'Host': host_header}, + cookies={'_sso_local_session': session_cookie}, + allow_redirects=False) + self.assertEquals(302, resp.status_code) + self.assertTrue('SSO_test' in resp.cookies, + 'missing cookie: %s\n%s' % (resp.cookies, resp.headers)) + self.assertEquals(tkt, resp.cookies['SSO_test']) # Make the original request again. - conn = httplib.HTTPConnection("127.0.0.1", APACHE_PORT) - conn.request("GET", url, headers={ - "Cookie": cookies.output(attrs=[], header='', sep='; '), - 'Host': host_header, - }) - resp = conn.getresponse() - self.assertEquals(200, resp.status) - conn.close() + resp = sess.get( + f'http://127.0.0.1:{APACHE_PORT}{url}', + headers={'Host': host_header}, + cookies={'SSO_test': tkt}, + allow_redirects=False) + self.assertEquals(200, resp.status_code) class HttpdIntegrationTest(HttpdIntegrationTestBase): @@ -324,8 +318,7 @@ class HttpdIntegrationTest(HttpdIntegrationTestBase): # to spot issues where we are not cleaning up state properly # between requests. n = 100 - errors = 0 - for i in xrange(n): + for i in range(n): cookie = 'SSO_test=%s' % self._ticket() status, body, location = _query("/index.html", cookie=cookie) self.assertEquals(200, status) @@ -345,29 +338,28 @@ class HttpdIntegrationTest(HttpdIntegrationTestBase): def test_sso_login(self): # Call the /sso_login endpoint, verify that it sets the local # SSO cookie to whatever the 't' parameter says. - cookies = Cookie.SimpleCookie() - conn = httplib.HTTPConnection("127.0.0.1", APACHE_PORT) tkt = self._ticket() - conn.request("GET", "/sso_login?%s" % urllib.urlencode( - {"t": tkt, "d": "https://service.example.com/index.html"})) - resp = conn.getresponse() - self.assertEquals(302, resp.status) - set_cookie_hdr = resp.getheader("Set-Cookie") + query_args = urlencode( + {"t": tkt, "d": "https://service.example.com/index.html"}) + resp = requests.get( + f'http://127.0.0.1:{APACHE_PORT}/sso_login?{query_args}', + allow_redirects=False) + self.assertEquals(302, resp.status_code) + set_cookie_hdr = resp.headers["Set-Cookie"] self.assertTrue(set_cookie_hdr) - cookies.load(set_cookie_hdr) - self.assertEquals(tkt, cookies['SSO_test'].value) - conn.close() + self.assertTrue('SSO_test' in resp.cookies, + 'missing cookie: %s' % resp.cookies) + self.assertEquals(tkt, resp.cookies['SSO_test']) def test_sso_logout(self): # test the /sso_logout endpoint - conn = httplib.HTTPConnection("127.0.0.1", APACHE_PORT) - conn.request("GET", "/sso_logout", headers={ - "Cookie": mkcookie(self._ticket())}) - resp = conn.getresponse() - set_cookie_hdr = resp.getheader("Set-Cookie") + resp = requests.get( + f'http://127.0.0.1:{APACHE_PORT}/sso_logout', + headers={"Cookie": mkcookie(self._ticket())}, + allow_redirects=False) + set_cookie_hdr = resp.headers["Set-Cookie"] self.assertTrue(set_cookie_hdr) self.assertTrue("SSO_test=;" in set_cookie_hdr) - conn.close() class HttpdIntegrationTestWithNonces(HttpdIntegrationTestBase): @@ -375,7 +367,7 @@ class HttpdIntegrationTestWithNonces(HttpdIntegrationTestBase): CONFIG = 'test-httpd-2.4-nonces.conf' def setUp(self): - with open('session.key', 'w') as fd: + with open('session.key', 'wb') as fd: fd.write(os.urandom(32)) HttpdIntegrationTestBase.setUp(self) @@ -409,9 +401,10 @@ class HttpdIntegrationTestWithNonces(HttpdIntegrationTestBase): class WebmailIntegrationTestWithNonces(HttpdIntegrationTestBase): CONFIG = 'test-httpd-2.4-webmail.conf' + DUMP_LOG = True def setUp(self): - with open('session.key', 'w') as fd: + with open('session.key', 'wb') as fd: fd.write(os.urandom(32)) HttpdIntegrationTestBase.setUp(self) diff --git a/src/python/sso/urllib2_handler.py b/src/python/sso/urllib2_handler.py index 4f60ee9fa6e29c6a195cf623f52811089c701964..73f698bd230ed1707b13fb2cbc5434a73fc0b1b4 100644 --- a/src/python/sso/urllib2_handler.py +++ b/src/python/sso/urllib2_handler.py @@ -87,9 +87,3 @@ def install_handler(jar=None, **kwargs): urllib2.build_opener(urllib2.HTTPCookieProcessor(jar), SSOProcessor(**kwargs))) - -if __name__ == '__main__': - install_handler() - resp = urllib2.urlopen('https://wiki.autistici.org/') - print resp.read() -