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

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

Fixed setting of authentication realm for HTTP Basic Auth middleware and improved interface to callback function by providing a exception type for the callback function to use to pass back message and HTTP status code.

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    def __init__(self, global_conf, **app_conf):
25        pass
26   
27    def __call__(self, environ, start_response):
28        contentType = 'text/plain'
29        response = 'Authenticated!'
30        status = 200
31        start_response(status,
32                       [('Content-type', contentType),
33                        ('Content-Length', str(len(response)))])
34        return [response]
35   
36           
37class TestHttpBasicAuthCallBackAppMiddleware(object):
38    """Add an authentication function to the environ for HttpBasicAuthMiddleware
39    to pick up and use.  It behaves as an application returning a response
40    """   
41    USERNAME = 'myusername'
42    PASSWORD = 'mypassword'
43    SUCCESS_RESPONSE = 'AUTHENTICATED'
44    FAILURE_RESPONSE = 'FAILED'
45   
46    def __init__(self, app, global_conf, **app_conf):
47        self.app = app
48       
49    def __call__(self, environ, start_response):
50        def authenticationApp(environ, start_response, username, password):
51            """Authentication callback application - its responsible for the
52            response message and response code
53            """
54            if (username == self.__class__.USERNAME and
55                password == self.__class__.PASSWORD):
56                response = self.__class__.SUCCESS_RESPONSE
57                status = '200 OK'
58            else:
59                response = self.__class__.FAILURE_RESPONSE
60                status = '401 Unauthorized'
61               
62            start_response(status,
63                           [('Content-type', 'text/plain'),
64                            ('Content-Length', str(len(response)))])
65            return [response]
66           
67        environ['HTTPBASICAUTH_FUNC'] = authenticationApp
68       
69        return self.app(environ, start_response)
70
71
72class TestHttpBasicAuthCallBackMiddleware(object):
73    """Add an authentication function to the environ for HttpBasicAuthMiddleware
74    to pick up and use.  The callback does not return a response leaving control
75    with the HttpBasicAuthMiddleware
76    """   
77    USERNAME = 'myusername'
78    PASSWORD = 'mypassword'
79   
80    def __init__(self, app, global_conf, **app_conf):
81        self.app = app
82       
83    def __call__(self, environ, start_response):
84        def authenticate(environ, start_response, username, password):
85            if (username != self.__class__.USERNAME or
86                password != self.__class__.PASSWORD):
87                raise HttpBasicAuthResponseException("Invalid credentials")
88           
89        environ['HTTPBASICAUTH_FUNC'] = authenticate
90       
91        return self.app(environ, start_response)
92   
93
94class HttpBasicAuthMiddlewareTestCase(unittest.TestCase):
95    CONFIG_FILE = 'httpbasicauth.ini'
96   
97    def __init__(self, *args, **kwargs):
98        here_dir = os.path.dirname(os.path.abspath(__file__))
99        configFilePath = ('config:%s' % 
100                          HttpBasicAuthMiddlewareTestCase.CONFIG_FILE)
101        wsgiapp = loadapp(configFilePath, relative_to=here_dir)
102        self.app = paste.fixture.TestApp(wsgiapp)
103         
104        unittest.TestCase.__init__(self, *args, **kwargs)
105       
106    def test01NoHttpBasicAuthHeader(self):
107        # Try with no HTTP Basic Auth HTTP header
108        response = self.app.get('/auth', status=401)
109           
110    def test02ValidCredentials(self):
111        # Try with no HTTP Basic Auth HTTP header
112        username = TestHttpBasicAuthCallBackAppMiddleware.USERNAME
113        password = TestHttpBasicAuthCallBackAppMiddleware.PASSWORD
114       
115        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
116        authHeader =  "Basic %s" % base64String
117        headers = {'Authorization': authHeader}
118       
119        response = self.app.get('/auth', headers=headers, status=200)
120        self.assert_((TestHttpBasicAuthCallBackAppMiddleware.SUCCESS_RESPONSE in
121                      response))
122                     
123    def test03InvalidCredentials(self):
124        # Try with no HTTP Basic Auth HTTP header
125        username = 'x'
126        password = 'y'
127       
128        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
129        authHeader =  "Basic %s" % base64String
130        headers = {'Authorization': authHeader}
131       
132        response = self.app.get('/auth', headers=headers, status=401)
133        self.assert_((TestHttpBasicAuthCallBackAppMiddleware.FAILURE_RESPONSE in
134                      response))
135       
136    def _createCallbackMiddleware(self):
137        # Test creating app independently of PasteScript and using an
138        # alternate middleware which doesn't return a response but simply
139        # raises a 401 exception type if input credentials don't match
140        app = TestApp({})
141        app = HttpBasicAuthMiddleware.filter_app_factory(app, {},
142                                prefix='',
143                                authnFuncEnvironKeyName='HTTPBASICAUTH_FUNC')
144        app = TestHttpBasicAuthCallBackMiddleware(app, {})
145
146        self.app2 = paste.fixture.TestApp(app)
147       
148    def test04SimpleCBMiddlewareWithValidCredentials(self):
149        self._createCallbackMiddleware()
150        username = TestHttpBasicAuthCallBackAppMiddleware.USERNAME
151        password = TestHttpBasicAuthCallBackAppMiddleware.PASSWORD
152       
153        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
154        authHeader =  "Basic %s" % base64String
155        headers = {'Authorization': authHeader}
156       
157        response = self.app.get('/auth', headers=headers, status=200)
158       
159    def test05SimpleCBMiddlewareWithInvalidCredentials(self):
160        self._createCallbackMiddleware()
161        username = 'a'
162        password = 'b'
163       
164        base64String = base64.encodestring('%s:%s' % (username, password))[:-1]
165        authHeader =  "Basic %s" % base64String
166        headers = {'Authorization': authHeader}
167       
168        response = self.app.get('/auth', headers=headers, status=401)       
169
170   
Note: See TracBrowser for help on using the repository browser.