Browse Source

Added http basic auth support

git-svn-id: http://proj.badc.rl.ac.uk/svn/ndg-security/trunk/ndg_httpsclient@8182 051b1e3e-aa0c-0410-b6c2-bfbade6052be
pjkersha 12 years ago
parent
commit
c5708a44d9
1 changed files with 49 additions and 5 deletions
  1. 49 5
      ndg/httpsclient/utils.py

+ 49 - 5
ndg/httpsclient/utils.py

@@ -14,7 +14,8 @@ import logging
 from optparse import OptionParser
 import os
 import urllib2
-from urllib2 import HTTPHandler, HTTPCookieProcessor
+from urllib2 import (HTTPHandler, HTTPCookieProcessor, 
+                     HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm)
 
 import urlparse
 
@@ -126,6 +127,8 @@ def open_url(url, config, data=None, handlers=None):
     @type config: Configuration
     @param data: HTTP POST data
     @type data: str
+    @param handlers: list of custom urllib2 handlers to add to the request
+    @type handlers: iterable
     @return: tuple (
         returned HTTP status code or 0 if an error occurred
         returned message or error description
@@ -154,6 +157,15 @@ def open_url(url, config, data=None, handlers=None):
         https_handler = HTTPSContextHandler(config.ssl_context, 
                                             debuglevel=debuglevel)
         handlers.extend([http_handler, https_handler])
+        
+    if config.http_basicauth:
+        # currently only supports http basic auth
+        auth_handler = HTTPBasicAuthHandler(HTTPPasswordMgrWithDefaultRealm())
+        auth_handler.add_password(realm=None, uri=url,
+                                  user=config.httpauth[0],
+                                  passwd=config.httpauth[1])
+        handlers.append(auth_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 
@@ -167,13 +179,18 @@ def open_url(url, config, data=None, handlers=None):
         log.debug("Configuring proxies: %s" % config.proxies)
 
     opener = build_opener(*handlers, ssl_context=config.ssl_context)
+    
+    headers = config.headers
+    if headers is None: 
+        headers = {}
+        request = urllib2.Request(url, data, headers)
 
     # Open the URL and check the response.
     return_code = 0
     return_message = ''
     response = None
     try:
-        response = opener.open(url, data)
+        response = opener.open(request)
         return_message = response.msg
         return_code = response.code
         if log.isEnabledFor(logging.DEBUG):
@@ -231,7 +248,7 @@ class Configuration(object):
     """Connection configuration.
     """
     def __init__(self, ssl_context, debug=False, proxies=None, no_proxy=None,
-                 cookie=None):
+                 cookie=None, http_basicauth=None, headers=None):
         """
         @param ssl_context: SSL context to use with this configuration
         @type ssl_context: OpenSSL.SSL.Context
@@ -243,12 +260,18 @@ class Configuration(object):
         @type no_proxy: basestring
         @param cookie: cookies to set for request
         @type cookie: cookielib.CookieJar
+        @param http_basicauth: http authentication, or None
+        @type http_basicauth: tuple of (username,password)
+        @param headers: http headers
+        @type headers: dict
         """
         self.ssl_context = ssl_context
         self.debug = debug
         self.proxies = proxies
         self.no_proxy = no_proxy
         self.cookie = cookie
+        self.http_basicauth = http_basicauth
+        self.headers = headers
 
 
 def main():
@@ -276,6 +299,12 @@ def main():
     parser.add_option("-n", "--no-verify-peer", action="store_true", 
                       dest="no_verify_peer", default=False,
                       help="Skip verification of peer certificate.")
+    parser.add_option("-a", "--basicauth", dest="auth", metavar="USER:PASSWD",
+                      default=None,
+                      help="HTTP authentication credentials")
+    parser.add_option("--header", action="append", dest="headers", 
+                      metavar="HEADER: VALUE",
+                      help="Add HTTP header to request")
     (options, args) = parser.parse_args()
     if len(args) != 1:
         parser.error("Incorrect number of arguments")
@@ -309,6 +338,17 @@ def main():
     else:
         data = None
     
+    if options.basicauth:
+        http_basicauth = options.auth.split(':', 1)
+    else:
+        http_basicauth = None
+
+    headers = {}
+    if options.headers:
+        for h in options.headers:
+            key, val = h.split(':', 1)
+            headers[key.strip()] = val.lstrip()
+            
     # 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(key_file,
@@ -318,9 +358,13 @@ def main():
                                                     verify_peer, 
                                                     url)
 
-    config = Configuration(ssl_context, options.debug)
+    config = Configuration(ssl_context, 
+                           options.debug,
+                           http_basicauth=http_basicauth,
+                           headers=headers)
     if options.output_file:
-        return_code, return_message = fetch_from_url_to_file(url, 
+        return_code, return_message = fetch_from_url_to_file(
+                                                      url, 
                                                       config,
                                                       options.output_file,
                                                       data)[:2]