Changeset 6888 for TI12-security


Ignore:
Timestamp:
24/05/10 15:01:36 (9 years ago)
Author:
pjkersha
Message:

Working unit tests with HTTP based MyProxy? logon tested using past.deploy fronting a real MyProxy? server

Location:
TI12-security/trunk/MyProxyServerUtils
Files:
1 added
7 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/MyProxyServerUtils/.pydevproject

    r6886 r6888  
    88<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property> 
    99<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Python2.6</pydev_property> 
     10<pydev_pathproperty name="org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH"> 
     11<path>/home/pjkersha/workspace/MyProxyClient</path> 
     12</pydev_pathproperty> 
    1013</pydev_project> 
  • TI12-security/trunk/MyProxyServerUtils/myproxy/__init__.py

    r6881 r6888  
     1"""MyProxy Server Utilities myproxy namespace package 
     2 
     3This is a setuptools namespace_package.  DO NOT place any other 
     4code in this file!  There is no guarantee that it will be installed 
     5with easy_install.  See: 
     6 
     7http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages 
     8 
     9... for details. 
     10""" 
     11__author__ = "P J Kershaw" 
     12__date__ = "21/05/10" 
     13__copyright__ = "(C) 2010 Science and Technology Facilities Council" 
     14__license__ = "BSD - see LICENSE file in top-level directory" 
     15__contact__ = "Philip.Kershaw@stfc.ac.uk" 
     16__revision__ = '$Id$' 
     17 
     18__import__('pkg_resources').declare_namespace(__name__) 
  • TI12-security/trunk/MyProxyServerUtils/myproxy/server/test/myproxywsgi.ini

    r6887 r6888  
    11# 
    2 # SSL Client Authn WSGI Testing environment configuration 
     2# MyProxy Logon Application in file for unit tests. 
     3# 
     4# Author: P J Kershaw 
     5# 
     6# Date: 21/05/10 
     7# 
     8# Copyright: STFC 2010 
     9# 
     10# Licence: BSD - See top-level LICENCE file for licence details 
    311# 
    412# The %(here)s variable will be replaced with the parent directory of this file 
    513# 
    6 [DEFAULT] 
    7 testConfigDir = %(here)s/../../../config 
    8  
    914[server:main] 
    1015use = egg:Paste#http 
     
    1217port = 5000 
    1318 
    14 [pipeline:main] 
    15 pipeline = SSLClientAuthnFilter TestApp 
    16  
    17 [app:TestApp] 
    18 paste.app_factory = ndg.security.test.unit.wsgi.ssl.test_ssl:TestSSLClientAuthnApp 
    19  
    20 [filter:SSLClientAuthnFilter] 
    21 paste.filter_app_factory = ndg.security.server.wsgi.ssl:ApacheSSLAuthnMiddleware 
    22 prefix = ssl. 
    23 ssl.caCertFilePathList = %(testConfigDir)s/ca/ndg-test-ca.crt 
    24 ssl.rePathMatchList = ^/secured/.*$ ^/restrict.* 
    25 ssl.clientCertDNMatchList = /O=NDG/OU=BADC/CN=test, /O=localhost/OU=local client/CN=test 2 
     19[app:main] 
     20paste.app_factory = myproxy.server.wsgi.app:MyProxyLogonApp.app_factory 
     21prefix = myproxy. 
     22myproxy.logonFuncEnvKeyName = MYPROXY_LOGON_FUNC 
     23myproxy.rePathMatchList = /logon 
     24myproxy.client.hostname = localhost 
     25myproxy.client.caCertDir = /etc/grid-security/certificates 
  • TI12-security/trunk/MyProxyServerUtils/myproxy/server/test/test_myproxywsgi.py

    r6886 r6888  
    99__revision__ = '$Id: $' 
    1010import logging 
     11logging.basicConfig(level=logging.DEBUG) 
    1112 
    1213import unittest 
    1314import os 
    14 import re 
     15import base64 
     16from getpass import getpass 
     17from ConfigParser import SafeConfigParser, NoOptionError 
    1518 
     19from OpenSSL import crypto 
    1620import paste.fixture 
    1721from paste.deploy import loadapp 
    1822 
     23from myproxy.server.wsgi.middleware import MyProxyClientMiddleware 
    1924 
    20 class TestSSLClientAuthnApp(BaseTestCase): 
    21     '''Test Application for the Authentication handler to protect''' 
    22     response = "Test Authentication redirect application" 
    23         
    24     def __init__(self, app_conf, **local_conf): 
    25         pass 
     25 
     26class TestMyProxyClientMiddlewareApp(object): 
     27    '''Test Application for MyClientProxyMiddleware''' 
     28    RESPONSE = "Test MyProxyClientMiddleware" 
    2629     
    2730    def __call__(self, environ, start_response): 
    2831         
    29         if environ['PATH_INFO'] == '/secured/uri': 
    30             status = "200 OK" 
    31              
    32         elif environ['PATH_INFO'] == '/unsecured': 
    33             status = "200 OK" 
    34              
    35         elif environ['PATH_INFO'] == '/test_200WithNotLoggedIn': 
    36             status = "200 OK" 
    37              
    38         elif environ['PATH_INFO'] == '/test_200WithLoggedIn': 
    39             environ['REMOTE_USER'] = 'testuser' 
    40             status = "200 OK" 
    41         else: 
    42             status = "404 Not found" 
     32        assert(environ[MyProxyClientMiddleware.CLIENT_ENV_KEYNAME]) 
     33        assert(environ[MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME]) 
     34        status = "200 OK" 
    4335                 
    4436        start_response(status, 
    4537                       [('Content-length',  
    46                          str(len(TestSSLClientAuthnApp.response))), 
     38                         str(len(self.__class__.RESPONSE))), 
    4739                        ('Content-type', 'text/plain')]) 
    48         return [TestSSLClientAuthnApp.response] 
     40        return [self.__class__.RESPONSE] 
    4941 
    5042 
    51 class MyProxyWSGITestCase(BaseTestCase): 
     43class MyProxyClientMiddlewareTestCase(unittest.TestCase): 
     44    def __init__(self, *args, **kwargs): 
     45        app = TestMyProxyClientMiddlewareApp() 
     46        app = MyProxyClientMiddleware.filter_app_factory(app, {}, prefix='') 
     47        self.app = paste.fixture.TestApp(app) 
     48          
     49        unittest.TestCase.__init__(self, *args, **kwargs) 
    5250 
     51    def test01AssertMyProxyClientInEnviron(self): 
     52        # Check the middleware has set the MyProxy client object in environ 
     53        response = self.app.get('/', status=200) 
     54        self.assert_(response) 
     55         
     56         
     57class MyProxyLogonAppTestCase(unittest.TestCase): 
     58    INI_FILENAME = 'myproxywsgi.ini' 
     59    THIS_DIR = os.path.dirname(__file__) 
     60    CONFIG_FILENAME = 'test_myproxywsgi.cfg' 
     61    CONFIG_FILEPATH = os.path.join(THIS_DIR, CONFIG_FILENAME) 
     62     
    5363    def __init__(self, *args, **kwargs): 
    5464        here_dir = os.path.dirname(os.path.abspath(__file__)) 
    55         wsgiapp = loadapp('config:test.ini', relative_to=here_dir) 
     65        wsgiapp = loadapp('config:' + MyProxyLogonAppTestCase.INI_FILENAME,  
     66                          relative_to=here_dir) 
    5667        self.app = paste.fixture.TestApp(wsgiapp) 
    57           
    58         BaseTestCase.__init__(self, *args, **kwargs) 
    5968         
    60  
    61     def test01NotAnSSLRequest(self): 
    62         # This request should be ignored because the SSL environment settings 
    63         # are not present 
    64         response = self.app.get('/unsecured') 
    65      
    66     def test02NoClientCertSet(self): 
    67         extra_environ = {'HTTPS':'1'} 
    68         response = self.app.get('/secured/uri', 
    69                                 extra_environ=extra_environ, 
    70                                 status=401) 
    71      
    72     def test03ClientCertSet(self): 
    73         thisDir = os.path.dirname(__file__) 
    74         sslClientCertFilePath = os.path.join( 
    75                                 os.environ[BaseTestCase.configDirEnvVarName], 
    76                                 'pki', 
    77                                 'test.crt') 
    78         sslClientCert = X509Cert.Read(sslClientCertFilePath).toString() 
    79         extra_environ = {'HTTPS':'1', 'SSL_CLIENT_CERT': sslClientCert} 
    80         response = self.app.get('/secured/uri', 
    81                                 extra_environ=extra_environ, 
    82                                 status=200) 
    83  
     69        self.cfg = SafeConfigParser({'here': MyProxyLogonAppTestCase.THIS_DIR}) 
     70        self.cfg.optionxform = str 
     71        self.cfg.read(MyProxyLogonAppTestCase.CONFIG_FILEPATH) 
     72         
     73        unittest.TestCase.__init__(self, *args, **kwargs) 
     74         
     75    def _createRequestCreds(self): 
     76        keyPair = crypto.PKey() 
     77        keyPair.generate_key(crypto.TYPE_RSA, 1024) 
     78         
     79        certReq = crypto.X509Req() 
     80         
     81        # Create public key object 
     82        certReq.set_pubkey(keyPair) 
     83         
     84        # Add the public key to the request 
     85        certReq.sign(keyPair, "md5") 
     86         
     87        pemCertReq = crypto.dump_certificate_request(crypto.FILETYPE_PEM,  
     88                                                     certReq) 
     89        return keyPair, pemCertReq 
     90         
     91    def test01Logon(self): 
     92        username = self.cfg.get('test01Logon', 'username') 
     93        try:  
     94            password = self.cfg.get('test01Logon', 'password') 
     95        except NoOptionError: 
     96            password = getpass('test01Logon password: ') 
     97             
     98        base64String = base64.encodestring('%s:%s' % (username, password))[:-1] 
     99        authHeader =  "Basic %s" % base64String 
     100        headers = {'Authorization': authHeader} 
     101         
     102        # Create key pair and certificate request 
     103        keyPair, certReq = self._createRequestCreds() 
     104        response = self.app.post('/logon', certReq, headers=headers, status=200) 
     105        print response  
     106        self.assert_(response) 
     107           
    84108 
    85109if __name__ == "__main__": 
  • TI12-security/trunk/MyProxyServerUtils/myproxy/server/wsgi/app.py

    r6886 r6888  
    99__contact__ = "Philip.Kershaw@stfc.ac.uk" 
    1010__revision__ = "$Id: $" 
    11 import logging 
    12 log = logging.getLogger(__name__) 
    13 import traceback 
    14 import re 
    15 import httplib 
    16 import socket 
    17  
    1811from myproxy.server.wsgi.httpbasicauth import HttpBasicAuthMiddleware 
    1912from myproxy.server.wsgi.middleware import MyProxyClientMiddleware 
     
    2518     
    2619class MyProxyLogonApp(object): 
    27     """HTTP Basic Auth interface to MyProxy logon.  This interfaces creates a 
    28     MyProxy client instance and HTTP Basic Auth based web service interface 
    29     for MyProxy logon calls.  This WSGI must be run over HTTPS to ensure 
    30     confidentiality of username/passphrase credentials 
     20    """HTTP interface to MyProxy logon.  This interfaces creates a MyProxy  
     21    client instance with a HTTP Basic Auth based web service interface 
     22    to pass username/passphrase for MyProxy logon calls.   
     23     
     24    This WSGI must be run over HTTPS to ensure confidentiality of  
     25    username/passphrase credentials.  PKI based verification of requests 
     26    should be done out of band of this app e.g. in other filter middleware or 
     27    Apache SSL configuration. 
    3128    """ 
    3229    PARAM_PREFIX = 'myproxy.logon.' 
     
    4441        dictionary 
    4542        """ 
    46         authnFuncEnvKeyNameOptName = HttpBasicAuthMiddleware.PARAM_PREFIX + \ 
    47                         HttpBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME_OPTNAME 
    48                          
    49         if authnFuncEnvKeyNameOptName in app_conf: 
    50             raise MyProxyLogonMiddlewareConfigError("Found %r option name in " 
    51                 "application configuration settings.  Use %r instead" % 
    52                 (authnFuncEnvKeyNameOptName,  
    53                  MyProxyLogonApp.PARAM_PREFIX + \ 
    54                  MyProxyLogonApp.LOGON_FUNC_ENV_KEYNAME_OPTNAME)) 
     43        logonFuncEnvKeyNameOptName = prefix + \ 
     44                        MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME_OPTNAME 
     45         
     46        logonFuncEnvironKeyName = app_conf.get(logonFuncEnvKeyNameOptName, 
     47                                MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME) 
    5548             
     49        # Mirror callback function setting in HTTP Basic Auth middleware so 
     50        # that it correctly picks up the authentication function 
     51        app_conf[prefix + HttpBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME_OPTNAME 
     52                 ] = logonFuncEnvironKeyName 
     53                  
    5654        app = MyProxyLogonApp() 
    5755        httpBasicAuthMWare = HttpBasicAuthMiddleware.filter_app_factory(app,  
    58                                                                     global_conf,  
    59                                                                     prefix,  
    60                                                                     **app_conf) 
     56                                                                global_conf,  
     57                                                                prefix=prefix,  
     58                                                                **app_conf) 
    6159         
    6260        app = MyProxyClientMiddleware.filter_app_factory(httpBasicAuthMWare,  
    6361                                                         global_conf,  
    64                                                          prefix,  
    65                                                          myProxyClientPrefix) 
     62                                                         prefix=prefix, 
     63                                                         **app_conf) 
    6664         
    6765        # Set HTTP Basic Auth to use the MyProxy client logon for its 
    6866        # authentication method 
    69         httpBasicAuthApp.authnFuncEnvironKeyName = app.logonFuncEnvironKeyName 
     67        httpBasicAuthMWare.authnFuncEnvironKeyName = app.logonFuncEnvironKeyName 
    7068         
    7169        return app 
     70     
     71    def __call__(self, environ, start_response): 
     72        """Catch case where request path doesn't match mount point for app""" 
     73        status = response = '404 Not Found' 
     74        start_response(status, 
     75                       [('Content-type', 'text/plain'), 
     76                        ('Content-length', str(len(response)))]) 
     77        return [response] 
     78         
    7279 
    7380 
  • TI12-security/trunk/MyProxyServerUtils/myproxy/server/wsgi/httpbasicauth.py

    r6886 r6888  
    161161             
    162162        creds = base64.decodestring(encodedCreds) 
    163         username, password = creds.split(HttpBasicAuthMiddleware.FIELD_SEP, 1) 
     163        username, password = creds.rsplit(HttpBasicAuthMiddleware.FIELD_SEP, 1) 
    164164        return username, password 
    165165 
  • TI12-security/trunk/MyProxyServerUtils/myproxy/server/wsgi/middleware.py

    r6886 r6888  
    1313log = logging.getLogger(__name__) 
    1414import traceback 
    15  
     15import socket 
     16import httplib 
     17from cStringIO import StringIO 
     18 
     19from OpenSSL import crypto 
    1620from myproxy.client import MyProxyClient, MyProxyClientError 
    1721        
     
    3135     
    3236    # Default environ key names 
    33     CLIENT_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.' 
    34                           'MyProxyClientMiddleware') 
    35     LOGON_FUNC_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.' 
     37    CLIENT_ENV_KEYNAME = ('myproxy.server.wsgi.middleware.' 
     38                          'MyProxyClientMiddleware.myProxyClient') 
     39    LOGON_FUNC_ENV_KEYNAME = ('myproxy.server.wsgi.middleware.' 
    3640                              'MyProxyClientMiddleware.logon') 
    3741     
     
    179183            """                
    180184            if environ.get('REQUEST_METHOD') == 'POST': 
    181                 pemCertReq = environ[ 
    182                         MyProxyClientMiddleware.WSGI_INPUT_ENV_KEYNAME].read() 
    183                  
    184                 # TODO: restore WSGI file object 
     185                wsgiInput = environ[ 
     186                                MyProxyClientMiddleware.WSGI_INPUT_ENV_KEYNAME] 
     187                pemCertReq = wsgiInput.read() 
     188                 
     189                # Restore WSGI file object with duck typing(!) 
     190                wsgiInput = StringIO() 
     191                wsgiInput.write(pemCertReq) 
     192                wsgiInput.seek(0) 
    185193                 
    186194                # Expecting PEM encoded request 
    187                 certReq = X509.load_request_string(pemCertReq)   
     195                certReq = crypto.load_certificate_request( 
     196                                                        crypto.FILETYPE_PEM, 
     197                                                        pemCertReq) 
     198                 
     199                # Convert to ASN1 format expect by logon client call 
     200                asn1CertReq = crypto.dump_certificate_request( 
     201                                                        crypto.FILETYPE_ASN1,  
     202                                                        certReq) 
    188203            else: 
    189204                status = self.getStatusMessage(httplib.UNAUTHORIZED) 
     
    195210                credentials = self.myProxyClient.logon(username,  
    196211                                                       password, 
    197                                                        certReq=certReq) 
     212                                                       certReq=asn1CertReq) 
    198213                status = self.getStatusMessage(httplib.OK) 
    199214                response = '\n'.join(credentials) 
    200215                 
    201             except MyProxyClientError: 
     216            except MyProxyClientError, e: 
    202217                status = self.getStatusMessage(httplib.UNAUTHORIZED) 
    203218                response = str(e) 
     
    220235         
    221236        return _myProxylogon 
     237 
     238    @staticmethod 
     239    def getStatusMessage(statusCode): 
     240        '''Make a standard status message for use with start_response 
     241        @type statusCode: int 
     242        @param statusCode: HTTP status code 
     243        @rtype: str 
     244        @return: status code with standard message 
     245        @raise KeyError: for invalid status code 
     246        ''' 
     247        return '%d %s' % (statusCode, httplib.responses[statusCode]) 
     248     
Note: See TracChangeset for help on using the changeset viewer.