Browse Source

* More work on unit tests
* fixed imports for the new namespace
* some simplifying of peer verification and get modules


git-svn-id: http://proj.badc.rl.ac.uk/svn/ndg-security/trunk/ndg_httpsclient@7973 051b1e3e-aa0c-0410-b6c2-bfbade6052be

pjkersha 13 years ago
parent
commit
f9c6bde018

+ 50 - 24
ndg/httpsclient/get.py

@@ -6,12 +6,10 @@ import os
 import urllib2
 import urlparse
 
-from OpenSSL import SSL
+from ndg.httpsclient.urllib2_build_opener import build_opener
+from ndg.httpsclient.https import HTTPSContextHandler
+from ndg.httpsclient import ssl_context_util
 
-from urllib2pyopenssl.urllib2_build_opener import urllib2_build_opener
-from urllib2pyopenssl.https import HTTPSContextHandler
-import urllib2pyopenssl.ssl_context_util as ssl_context_util
-from urllib2pyopenssl.ssl_peer_verification import ServerSSLCertVerification
 
 def fetch_from_url(url, config):
     """Returns data retrieved from a URL.
@@ -19,7 +17,7 @@ def fetch_from_url(url, config):
     @param config - configuration
     @return data retrieved from URL or None
     """
-    (return_code, return_message, response) = open_url(url, config)
+    return_code, return_message, response = open_url(url, config)
     if return_code and return_code == httplib.OK:
         return_data = response.read()
         response.close()
@@ -38,14 +36,15 @@ def fetch_from_url_to_file(url, config, output_file):
         boolean indicating whether access was successful
     )
     """
-    (return_code, return_message, response) = open_url(url, config)
+    return_code, return_message, response = open_url(url, config)
     if return_code == httplib.OK:
         return_data = response.read()
         response.close()
         outfile = open(output_file, "w")
         outfile.write(return_data)
         outfile.close()
-    return (return_code, return_message, (return_code == httplib.OK))
+    return return_code, return_message, return_code == httplib.OK
+
 
 def open_url(url, config):
     """Attempts to open a connection to a specified URL.
@@ -67,18 +66,20 @@ def open_url(url, config):
 
     if config.debug:
         http_handler = urllib2.HTTPHandler(debuglevel=debuglevel)
-        https_handler = HTTPSContextHandler(config.ssl_context, debuglevel=debuglevel)
+        https_handler = HTTPSContextHandler(config.ssl_context, 
+                                            debuglevel=debuglevel)
         handlers.extend([http_handler, https_handler])
 
-    # Explicitly remove proxy handling if the host is one listed in the value of the no_proxy
-    # environment variable because urllib2 does use proxy settings set via http_proxy and
-    # https_proxy, but does not take the no_proxy value into account.
+    # Explicitly remove proxy handling if the host is one listed in the value of
+    # the no_proxy environment variable because urllib2 does use proxy settings 
+    # set via http_proxy and https_proxy, but does not take the no_proxy value 
+    # into account.
     if not _should_use_proxy(url):
         handlers.append(urllib2.ProxyHandler({}))
         if config.debug:
             print "Not using proxy"
 
-    opener = urllib2_build_opener(config.ssl_context, *handlers)
+    opener = build_opener(config.ssl_context, *handlers)
 
     # Open the URL and check the response.
     return_code = 0
@@ -105,6 +106,7 @@ def open_url(url, config):
             print exc.__class__, exc.__str__()
     return (return_code, return_message, response)
 
+
 def _should_use_proxy(url):
     """Determines whether a proxy should be used to open a connection to the specified URL, based on
         the value of the no_proxy environment variable.
@@ -119,6 +121,7 @@ def _should_use_proxy(url):
 
     return True
 
+
 class Configuration(object):
     """Checker configuration.
     """
@@ -131,6 +134,7 @@ class Configuration(object):
         self.ssl_context = ssl_context
         self.debug = debug
 
+
 def main():
     '''Utility to fetch data using HTTP or HTTPS GET from a specified URL.
     '''
@@ -141,36 +145,58 @@ def main():
     parser.add_option("-c", "--certificate", dest="cert_file", metavar="FILE",
                       default=os.path.expanduser("~/credentials.pem"),
                       help="Certificate file.")
-    parser.add_option("-t", "--ca-certificate-dir", dest="ca_dir", metavar="PATH",
+    parser.add_option("-t", "--ca-certificate-dir", dest="ca_dir", 
+                      metavar="PATH",
                       default=None,
                       help="Trusted CA certificate file directory.")
-    parser.add_option("-d", "--debug", action="store_true", dest="debug", default=False,
+    parser.add_option("-d", "--debug", action="store_true", dest="debug", 
+                      default=False,
                       help="Print debug information.")
     parser.add_option("-f", "--fetch", dest="output_file", metavar="FILE",
                       default=None, help="Output file.")
-    parser.add_option("-v", "--verify-peer", action="store_true", dest="verify_peer", default=False,
+    parser.add_option("-v", "--verify-peer", action="store_true", 
+                      dest="verify_peer", default=False,
                       help="Verify peer certificate.")
     (options, args) = parser.parse_args()
     if len(args) != 1:
         parser.error("Incorrect number of arguments")
 
     url = args[0]
-    # If a private key file is not specified, the key is assumed to be stored in the certificate file.
+    
+    if options.key_file and os.path.exists(options.key_file):
+        key_file = options.key_file
+    else:
+        key_file = None
+    
+    if options.cert_file and os.path.exists(options.cert_file):
+        cert_file = options.cert_file
+    else:
+        cert_file = None
+    
+    if options.ca_dir and os.path.exists(options.ca_dir):
+        ca_dir = options.ca_dir 
+    else:
+        ca_dir = None
+        
+    # If a private key file is not specified, the key is assumed to be stored in 
+    # the certificate file.
     ssl_context = ssl_context_util.make_ssl_context(
-        options.key_file if options.key_file and os.path.exists(options.key_file) else None,
-        options.cert_file if options.cert_file and os.path.exists(options.cert_file) else None,
+        key_file,
+        cert_file,
         None,
-        options.ca_dir if options.ca_dir and os.path.exists(options.ca_dir) else None,
+        ca_dir,
         options.verify_peer, url)
 
     config = Configuration(ssl_context, options.debug)
     if options.output_file:
-        (return_code, return_message, success) = fetch_from_url_to_file(url, config,
-            options.output_file)
-        print return_code, return_message
+        return_code, return_message, success = fetch_from_url_to_file(url, 
+                                                          config,
+                                                          options.output_file)
+        print(return_code, return_message)
     else:
         data = fetch_from_url(url, config)
-        print data
+        print(data)
+
 
 if __name__=='__main__':
     logging.basicConfig()

+ 1 - 1
ndg/httpsclient/https.py

@@ -17,7 +17,7 @@ from urllib2 import AbstractHTTPHandler
 
 from OpenSSL import SSL
 
-from urllib2pyopenssl.ssl_socket import SSLSocket
+from ndg.httpsclient.ssl_socket import SSLSocket
 
 log = logging.getLogger(__name__)
 

+ 1 - 1
ndg/httpsclient/ssl_context_util.py

@@ -2,7 +2,7 @@ import urlparse
 
 from OpenSSL import SSL
 
-from urllib2pyopenssl.ssl_peer_verification import ServerSSLCertVerification
+from ndg.httpsclient.ssl_peer_verification import ServerSSLCertVerification
 
 class SSlContextConfig(object):
     """

+ 2 - 21
ndg/httpsclient/ssl_peer_verification.py

@@ -30,9 +30,9 @@ class ServerSSLCertVerification(object):
     PARSER_RE_STR = '/(%s)=' % '|'.join(DN_LUT.keys() + DN_LUT.values())
     PARSER_RE = re.compile(PARSER_RE_STR)
 
-    __slots__ = ('__hostname', '__certDN', '__serverCNPrefixes')
+    __slots__ = ('__hostname', '__certDN')
 
-    def __init__(self, certDN=None, hostname=None, serverCNPrefixes=None):
+    def __init__(self, certDN=None, hostname=None):
         """Override parent class __init__ to enable setting of certDN
         setting
 
@@ -43,7 +43,6 @@ class ServerSSLCertVerification(object):
         """
         self.__certDN = None
         self.__hostname = None
-        self.__serverCNPrefixes = None
 
         if certDN is not None:
             self.certDN = certDN
@@ -51,11 +50,6 @@ class ServerSSLCertVerification(object):
         if hostname is not None:
             self.hostname = hostname
 
-        if serverCNPrefixes is not None:
-            self.serverCNPrefixes = serverCNPrefixes
-        else:
-            self.serverCNPrefixes = ['']
-
     def __call__(self, connection, peerCert, errorStatus, errorDepth,
                  preverifyOK):
         """Verify server certificate
@@ -167,16 +161,3 @@ class ServerSSLCertVerification(object):
     hostname = property(fget=_getHostname,
                         fset=_setHostname,
                         doc="hostname of server")
-
-    def _getServerCNPrefixes(self):
-        return self.__serverCNPrefixes
-
-    def _setServerCNPrefixes(self, val):
-        if not isinstance(val, list):
-            raise TypeError("Expecting string type for ServerCNPrefixes "
-                            "attribute")
-        self.__serverCNPrefixes = val
-
-    serverCNPrefixes = property(fget=_getServerCNPrefixes,
-                                fset=_setServerCNPrefixes,
-                                doc="Server CN Prefixes")

+ 5 - 1
ndg/httpsclient/test/__init__.py

@@ -8,4 +8,8 @@ __date__ = "05/01/12"
 __copyright__ = "(C) 2012 Science and Technology Facilities Council"
 __license__ = "BSD - see LICENSE file in top-level directory"
 __contact__ = "Philip.Kershaw@stfc.ac.uk"
-__revision__ = '$Id$'
+__revision__ = '$Id$'
+class Constants(object):
+    PORT = 4443
+    HOSTNAME = 'localhost'
+    TEST_URI = 'https://%s:%d' % (HOSTNAME, PORT)

+ 2 - 2
ndg/httpsclient/test/test.py

@@ -4,8 +4,8 @@ Created on Jan 5, 2012
 @author: philipkershaw
 '''
 import unittest
-from urllib2pyopenssl.urllib2_build_opener import urllib2_build_opener
-from urllib2pyopenssl.https import HTTPSConnection
+from ndg.httpsclient.urllib2_build_opener import urllib2_build_opener
+from ndg.httpsclient.https import HTTPSConnection
 
 
 class Urllib2PyOpenSslTestCase(unittest.TestCase):

+ 27 - 0
ndg/httpsclient/test/test_https.py

@@ -0,0 +1,27 @@
+'''
+Created on Jan 6, 2012
+
+@author: philipkershaw
+'''
+import logging
+logging.basicConfig(level=logging.DEBUG)
+import unittest
+
+from ndg.httpsclient.test import Constants
+from ndg.httpsclient.https import HTTPSConnection
+
+
+class TestHTTPSConnection(unittest.TestCase):
+
+
+    def test01(self):
+        conn = HTTPSConnection(Constants.HOSTNAME, port=Constants.PORT)
+        conn.connect()
+        conn.request('GET', '/')
+        resp = conn.getresponse()
+        print('Response = %s' % resp.read())
+        conn.close()
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 17 - 0
ndg/httpsclient/test_get.py

@@ -0,0 +1,17 @@
+'''
+Created on Jan 6, 2012
+
+@author: philipkershaw
+'''
+import unittest
+
+
+class TestGetModule(unittest.TestCase):
+
+
+    def test01(self):
+        pass
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 2 - 2
ndg/httpsclient/urllib2_build_opener.py

@@ -13,12 +13,12 @@ from urllib2 import (OpenerDirector, ProxyHandler, UnknownHandler, HTTPHandler,
                      HTTPDefaultErrorHandler, HTTPRedirectHandler,
                      FTPHandler, FileHandler, HTTPErrorProcessor)
 
-from urllib2pyopenssl.https import HTTPSContextHandler
+from ndg.httpsclient.https import HTTPSContextHandler
 
 log = logging.getLogger(__name__)
 
 # Copied from urllib2 with modifications for ssl
-def urllib2_build_opener(ssl_context=None, *handlers):
+def build_opener(ssl_context=None, *handlers):
     """Create an opener object from a list of handlers.
 
     The opener will use several default handlers, including support

+ 25 - 5
setup.py

@@ -6,17 +6,37 @@ except ImportError:
     from setuptools import setup, find_packages
 
 setup(
-    name='urllib2pyopenssl',
+    name='ndg_httpsclient',
     version="0.1.0",
-    description='Provides HTTPS with urllib2 using PyOpenSSL',
+    description='Provides HTTPS for httplib and urllib2 using PyOpenSSL',
     author='Richard Wilkinson',
     long_description=open('README').read(),
     license='BSD - See LICENCE file for details',
     namespace_packages=['ndg'],
     packages=find_packages(),
+    classifiers = [
+        'Development Status :: 5 - Production/Stable',
+        'Environment :: Console',
+        'Environment :: Web Environment',
+        'Intended Audience :: End Users/Desktop',
+        'Intended Audience :: Developers',
+        'Intended Audience :: System Administrators',
+        'Intended Audience :: Science/Research',
+        'License :: OSI Approved :: GNU Library or Lesser General Public License (BSD)',
+        'Natural Language :: English',
+        'Operating System :: Microsoft :: Windows',
+        'Operating System :: POSIX :: Linux',
+        'Programming Language :: Python',
+        'Topic :: Security',
+        'Topic :: Internet',
+        'Topic :: Scientific/Engineering',
+        'Topic :: System :: Distributed Computing',
+        'Topic :: System :: Systems Administration :: Authentication/Directory',
+        'Topic :: Software Development :: Libraries :: Python Modules'
+    ],
+    zip_safe = False,
     entry_points = {
-        'console_scripts': [
-#            'urllib2pyopenssl_get = urllib2pyopenssl.get:main'
-            ]
+        'console_scripts': ['ndg_httpclient = myproxy.script:main',
+                            ],
         }
 )