source: TI12-security/trunk/MyProxyWebService/myproxy/server/test/test_httpbasicauth.py @ 6950

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/MyProxyWebService/myproxy/server/test/test_httpbasicauth.py@6950
Revision 6950, 7.2 KB checked in by pjkersha, 10 years ago (diff)

Incomplete - task 5: MyProxy? Logon HTTPS Interface

  • Finished epydoc ready for first release to PyPI.
Line 
1#!/usr/bin/env python
2"""Unit tests for MyProxy WSGI Middleware classes and Application
3"""
4__author__ = "P J Kershaw"
5__date__ = "21/05/10"
6__copyright__ = "(C) 2010 Science and Technology Facilities Council"
7__license__ = "BSD - see LICENSE file in top-level directory"
8__contact__ = "Philip.Kershaw@stfc.ac.uk"
9__revision__ = '$Id: $'
10import logging
11
12import unittest
13import os
14import base64
15
16import paste.fixture
17from paste.deploy import loadapp
18
19from myproxy.server.wsgi.httpbasicauth import (HttpBasicAuthMiddleware,
20                                               HttpBasicAuthResponseException)
21
22
23class TestApp(object):
24    """Test WSGI Application for use with the unit tests for the HTTP Basic
25    Auth middleware developed for the myproxy.server.app.MyProxyApp application
26    """
27    def __init__(self, global_conf, **app_conf):
28        """Follow standard Paste Deploy app factory function signature"""
29   
30    def __call__(self, environ, start_response):
31        """Make a simple response for unit test code to trap and validate
32        against.  If this method is executed then the HTTP Basic Auth step in
33        the upstream middleware has succeeded.
34        """
35        contentType = 'text/plain'
36        response = 'Authenticated!'
37        status = 200
38        start_response(status,
39                       [('Content-type', contentType),
40                        ('Content-Length', str(len(response)))])
41        return [response]
42   
43           
44class TestHttpBasicAuthCallBackAppMiddleware(object):
45    """Add an authentication function to the environ for HttpBasicAuthMiddleware
46    to pick up and use.  It behaves as an application returning a response
47    """   
48    USERNAME = 'myusername'
49    PASSWORD = 'mypassword'
50    SUCCESS_RESPONSE = 'AUTHENTICATED'
51    FAILURE_RESPONSE = 'FAILED'
52   
53    def __init__(self, app, global_conf, **app_conf):
54        """Follow standard Paste Deploy app factory function signature"""
55        self.app = app
56       
57    def __call__(self, environ, start_response):
58        def authenticationApp(environ, start_response, username, password):
59            """Authentication callback application - its responsible for the
60            response message and response code
61            """
62            if (username == self.__class__.USERNAME and
63                password == self.__class__.PASSWORD):
64                response = self.__class__.SUCCESS_RESPONSE
65                status = '200 OK'
66            else:
67                response = self.__class__.FAILURE_RESPONSE
68                status = '401 Unauthorized'
69               
70            start_response(status,
71                           [('Content-type', 'text/plain'),
72                            ('Content-Length', str(len(response)))])
73            return [response]
74           
75        environ['HTTPBASICAUTH_FUNC'] = authenticationApp
76       
77        return self.app(environ, start_response)
78
79
80class TestHttpBasicAuthCallBackMiddleware(object):
81    """Add an authentication function to the environ for HttpBasicAuthMiddleware
82    to pick up and use.  The callback does not return a response leaving control
83    with the HttpBasicAuthMiddleware
84    """   
85    USERNAME = 'myusername'
86    PASSWORD = 'mypassword'
87   
88    def __init__(self, app, global_conf, **app_conf):
89        """Follow standard Paste Deploy app factory function signature"""
90        self.app = app
91       
92    def __call__(self, environ, start_response):
93        """Create HTTP Basic Auth callback"""
94        def authenticate(environ, start_response, username, password):
95            """HTTP Basic Auth callback function"""
96            if (username != self.__class__.USERNAME or
97                password != self.__class__.PASSWORD):
98                raise HttpBasicAuthResponseException("Invalid credentials")
99           
100        environ['HTTPBASICAUTH_FUNC'] = authenticate
101       
102        return self.app(environ, start_response)
103   
104
105class HttpBasicAuthMiddlewareTestCase(unittest.TestCase):
106    """Unit tests for HTTP Basic Auth middleware used with the MyProxyWebService
107    package
108    """
109    CONFIG_FILE = 'httpbasicauth.ini'
110   
111    def __init__(self, *args, **kwargs):
112        """Set-up Paste fixture from ini file settings"""
113        here_dir = os.path.dirname(os.path.abspath(__file__))
114        configFilePath = ('config:%s' % 
115                          HttpBasicAuthMiddlewareTestCase.CONFIG_FILE)
116        wsgiapp = loadapp(configFilePath, relative_to=here_dir)
117        self.app = paste.fixture.TestApp(wsgiapp)
118         
119        unittest.TestCase.__init__(self, *args, **kwargs)
120       
121    def test01NoHttpBasicAuthHeader(self):
122        # Try with no HTTP Basic Auth HTTP header
123        response = self.app.get('/auth', status=401)
124           
125    def test02ValidCredentials(self):
126        # Try with no HTTP Basic Auth HTTP header
127        username = TestHttpBasicAuthCallBackAppMiddleware.USERNAME
128        password = TestHttpBasicAuthCallBackAppMiddleware.PASSWORD
129       
130        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
131        authHeader =  "Basic %s" % base64String
132        headers = {'Authorization': authHeader}
133       
134        response = self.app.get('/auth', headers=headers, status=200)
135        self.assert_((TestHttpBasicAuthCallBackAppMiddleware.SUCCESS_RESPONSE in
136                      response))
137                     
138    def test03InvalidCredentials(self):
139        # Try with no HTTP Basic Auth HTTP header
140        username = 'x'
141        password = 'y'
142       
143        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
144        authHeader =  "Basic %s" % base64String
145        headers = {'Authorization': authHeader}
146       
147        response = self.app.get('/auth', headers=headers, status=401)
148        self.assert_((TestHttpBasicAuthCallBackAppMiddleware.FAILURE_RESPONSE in
149                      response))
150       
151    def _createCallbackMiddleware(self):
152        # Test creating app independently of PasteScript and using an
153        # alternate middleware which doesn't return a response but simply
154        # raises a 401 exception type if input credentials don't match
155        app = TestApp({})
156        app = HttpBasicAuthMiddleware.filter_app_factory(app, {},
157                                prefix='',
158                                authnFuncEnvironKeyName='HTTPBASICAUTH_FUNC')
159        app = TestHttpBasicAuthCallBackMiddleware(app, {})
160
161        self.app2 = paste.fixture.TestApp(app)
162       
163    def test04SimpleCBMiddlewareWithValidCredentials(self):
164        self._createCallbackMiddleware()
165        username = TestHttpBasicAuthCallBackAppMiddleware.USERNAME
166        password = TestHttpBasicAuthCallBackAppMiddleware.PASSWORD
167       
168        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
169        authHeader =  "Basic %s" % base64String
170        headers = {'Authorization': authHeader}
171       
172        response = self.app.get('/auth', headers=headers, status=200)
173       
174    def test05SimpleCBMiddlewareWithInvalidCredentials(self):
175        self._createCallbackMiddleware()
176        username = 'a'
177        password = 'b'
178       
179        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
180        authHeader =  "Basic %s" % base64String
181        headers = {'Authorization': authHeader}
182       
183        response = self.app.get('/auth', headers=headers, status=401)       
184
185   
Note: See TracBrowser for help on using the repository browser.