[Zope-CVS] CVS: Products/PluggableAuthService/plugins/tests - ChallengeProtocolChooser.txt:1.3.2.1 test_ChallengeProtocolChooser.py:1.3.2.1 test_RequestTypeSniffer.py:1.3.2.1 test_doctests.py:1.3.2.1

Sidnei da Silva sidnei at enfoldsystems.com
Wed Aug 17 00:53:06 EDT 2005


Update of /cvs-repository/Products/PluggableAuthService/plugins/tests
In directory cvs.zope.org:/tmp/cvs-serv27657/plugins/tests

Added Files:
      Tag: sidnei-challenge-protocol-chooser
	ChallengeProtocolChooser.txt test_ChallengeProtocolChooser.py 
	test_RequestTypeSniffer.py test_doctests.py 
Log Message:

- Had mistakenly added those files to HEAD instead of this branch


=== Added File Products/PluggableAuthService/plugins/tests/ChallengeProtocolChooser.txt ===
Challenge Protocol Chooser
--------------------------

The Challenge Protocol Chooser is a plugin that decides what
authentication protocol to use for a given request type.

Let's start by setting up a PAS instance inside our existing test
folder.

  >>> folder = self.folder

  >>> 'acl_users' in folder.objectIds()
  True

  >>> folder.manage_delObjects(ids=['acl_users'])

  >>> 'acl_users' in folder.objectIds()
  False

  >>> dispatcher = folder.manage_addProduct['PluggableAuthService']
  >>> dispatcher.addPluggableAuthService()

  >>> 'acl_users' in folder.objectIds()
  True

  >>> folder.acl_users.meta_type
  'Pluggable Auth Service'

Now, we'll setup this PAS instance with what most people would get by
default, users and roles stored in ZODB with HTTP Basic auth.

  >>> pas = folder.acl_users

  >>> dispatcher = pas.manage_addProduct['PluggableAuthService']

  >>> dispatcher.addZODBUserManager('users')
  >>> dispatcher.addZODBRoleManager('roles')
  >>> dispatcher.addHTTPBasicAuthHelper('http_auth')

  >>> plugins = pas.plugins

  >>> from Products.PluggableAuthService.interfaces.plugins import \
  ...   IAuthenticationPlugin, IUserEnumerationPlugin, IRolesPlugin, \
  ...	IRoleEnumerationPlugin, IRoleAssignerPlugin, \
  ...   IChallengePlugin, IExtractionPlugin, IUserAdderPlugin

  >>> plugins.activatePlugin(IUserAdderPlugin, 'users')
  >>> plugins.activatePlugin(IAuthenticationPlugin, 'users')
  >>> plugins.activatePlugin(IUserEnumerationPlugin, 'users')
  >>> plugins.activatePlugin(IRolesPlugin, 'roles')
  >>> plugins.activatePlugin(IRoleEnumerationPlugin, 'roles')
  >>> plugins.activatePlugin(IRoleAssignerPlugin, 'roles')
  >>> plugins.activatePlugin(IExtractionPlugin, 'http_auth')
  >>> plugins.activatePlugin(IChallengePlugin, 'http_auth')

Create a user for testing:

  >>> pas.getUserById('test_user_') is None
  True

  >>> username, password  = 'test_user_', 'test_user_pw'
  >>> pas._doAddUser(username, password, ['Manager'], [])

  >>> pas.getUserById('test_user_') is None
  False

We are now going to try some different kinds of requests and make sure
all of them work. They all use HTTP Basic Auth, which is the default
in this configuration we just set up. For the sake of testing, we are
going to create a simple script that requires the 'Manager' role to be
called.

  >>> folder_name = folder.getId()
  >>> dispatcher = folder.manage_addProduct['PythonScripts']
  >>> _ = dispatcher.manage_addPythonScript('test_script')

  >>> script = folder._getOb('test_script')
  >>> script.write('return "Access Granted"')
  >>> script.manage_permission(permission_to_manage='View',
  ...                          roles=['Manager'], acquire=0)

Access the script through a simple ``GET`` request, simulating browser
access. Anonymous user should be challenged with a 401 response
status.

  >>> print http(r"""
  ... GET /%s/test_script HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 401 Unauthorized...

With the right credentials though the request should succeed:

  >>> print http(r"""
  ... GET /%s/test_script HTTP/1.1
  ... Authorization: Basic %s:%s
  ... """ % (folder_name, username, password), handle_errors=False)
  HTTP/1.1 200 OK
  ...
  Access Granted

Now a PROPFIND request, simulating a WebDAV client. Anonymous user
should be challenged with a 401 response status:

  >>> print http(r"""
  ... PROPFIND /%s/test_script HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 401 Unauthorized...

And with the right credentials the request should succeed:

  >>> print http(r"""
  ... PROPFIND /%s/test_script HTTP/1.1
  ... Authorization: Basic %s:%s
  ... """ % (folder_name, username, password), handle_errors=False)
  HTTP/1.1 207 Multi-Status...

  >>> print http(r"""
  ... GET /%s/test_script/manage_DAVget HTTP/1.1
  ... Authorization: Basic %s:%s
  ... """ % (folder_name, username, password), handle_errors=False)
  HTTP/1.1 200 OK...

And a XML-RPC Request. Again, Anonymous user should be challenged with
a 401 response status.

  >>> print http(r"""
  ... POST /%s HTTP/1.1
  ... Content-Type: text/xml; charset="utf-8"
  ... <?xml version="1.0"?>
  ... <methodCall>
  ... <methodName>test_script</methodName>
  ... </methodCall>
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 401 Unauthorized...

And with valid credentials the reqeuest should succeed:

  >>> print http(r"""
  ... POST /%s HTTP/1.1
  ... Content-Type: text/xml; charset="utf-8"
  ... Authorization: Basic %s:%s
  ... <?xml version="1.0"?>
  ... <methodCall>
  ... <methodName>test_script</methodName>
  ... </methodCall>
  ... """ % (folder_name, username, password), handle_errors=False)
  HTTP/1.1 200 OK
  Content-Length: 140
  Content-Type: text/xml
  <BLANKLINE>
  <?xml version='1.0'?>
  <methodResponse>
  <params>
  <param>
  <value><string>Access Granted</string></value>
  </param>
  </params>
  </methodResponse>

Adding a Cookie Auth Helper now to test the correct behaviour of the
Challenge Protocol Helper.

  >>> dispatcher = pas.manage_addProduct['PluggableAuthService']
  >>> dispatcher.addCookieAuthHelper('cookie_auth',
  ...                                cookie_name='__ac')

  >>> plugins.activatePlugin(IExtractionPlugin, 'cookie_auth')
  >>> plugins.activatePlugin(IChallengePlugin, 'cookie_auth')

Re-activate HTTP Auth Helper so that it appears **after** Cookie Auth
Helper:

  >>> plugins.deactivatePlugin(IExtractionPlugin, 'http_auth')
  >>> plugins.activatePlugin(IExtractionPlugin, 'http_auth')
  >>> plugins.deactivatePlugin(IChallengePlugin, 'http_auth')
  >>> plugins.activatePlugin(IChallengePlugin, 'http_auth')

Now, invalid credentials should result in a 302 response status for a
normal (eg: browser) request:

  >>> print http(r"""
  ... GET /%s/test_script HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 302 Moved Temporarily...

And the same for a WebDAV request:

  >>> print http(r"""
  ... PROPFIND /%s/test_script HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 302 Moved Temporarily...

  >>> print http(r"""
  ... GET /%s/test_script/manage_DAVget HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 302 Moved Temporarily...

And for a XML-RPC request:

  >>> print http(r"""
  ... POST /%s HTTP/1.1
  ... Content-Type: text/xml; charset="utf-8"
  ... <?xml version="1.0"?>
  ... <methodCall>
  ... <methodName>test_script</methodName>
  ... </methodCall>
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 302 Moved Temporarily...

However, not all WebDAV and XML-RPC clients understand the
redirect. Even worse, they will not be able to display the login form
that is the target of this redirect.

For this reason we should disable the Cookie Auth Helper for
non-browser requests. In fact, we might only want plugins that
understand the 'http' authorization protocol to issue challenges for
WebDAV and XML-RPC.

To do this, we use the Challenge Protocol Chooser plugin together with
the Request Type Sniffer plugin.

  >>> from Products.PluggableAuthService.interfaces.plugins import \
  ...   IRequestTypeSniffer, IChallengeProtocolChooser

  >>> dispatcher = pas.manage_addProduct['PluggableAuthService']

  >>> dispatcher.addRequestTypeSnifferPlugin('sniffer')
  >>> plugins.activatePlugin(IRequestTypeSniffer, 'sniffer')

  >>> mapping = {'WebDAV': ['http'],
  ...            'XML-RPC': ['http'],
  ...		 'Browser': []}

  >>> dispatcher.addChallengeProtocolChooserPlugin('chooser',
  ...                                              mapping=mapping)
  >>> plugins.activatePlugin(IChallengeProtocolChooser, 'chooser')

Now, invalid credentials should result in a 302 response status for a
normal (eg: browser) request:

  >>> print http(r"""
  ... GET /%s/test_script HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 302 Moved Temporarily...

A WebDAV request should result in a 401 response status:

  >>> print http(r"""
  ... PROPFIND /%s/test_script HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 401 Unauthorized...

  >>> print http(r"""
  ... GET /%s/test_script/manage_DAVget HTTP/1.1
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 401 Unauthorized...

And a XML-RPC request should also result in a 401 response status:

  >>> print http(r"""
  ... POST /%s HTTP/1.1
  ... Content-Type: text/xml; charset="utf-8"
  ... <?xml version="1.0"?>
  ... <methodCall>
  ... <methodName>test_script</methodName>
  ... </methodCall>
  ... """ % (folder_name), handle_errors=False)
  HTTP/1.1 401 Unauthorized...


=== Added File Products/PluggableAuthService/plugins/tests/test_ChallengeProtocolChooser.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
# Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this
# distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Unit tests for ChallengeProtocolChooser

$Id: test_ChallengeProtocolChooser.py,v 1.3.2.1 2005/08/17 04:53:05 sidnei Exp $
"""
import unittest

from Products.PluggableAuthService.tests.conformance \
    import IChallengeProtocolChooser_conformance

class ChallengeProtocolChooser( unittest.TestCase
                                , IChallengeProtocolChooser_conformance 
                              ):


    def _getTargetClass( self ):

        from Products.PluggableAuthService.plugins.ChallengeProtocolChooser \
            import ChallengeProtocolChooser

        return ChallengeProtocolChooser

    def _makeOne( self, id='test', *args, **kw ):

        return self._getTargetClass()( id, *args, **kw )


if __name__ == "__main__":
    unittest.main()
        
def test_suite():
    return unittest.TestSuite((
        unittest.makeSuite( ChallengeProtocolChooser ),
        ))
        


=== Added File Products/PluggableAuthService/plugins/tests/test_RequestTypeSniffer.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
# Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this
# distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Unit tests for RequestTypeSniffer

$Id: test_RequestTypeSniffer.py,v 1.3.2.1 2005/08/17 04:53:05 sidnei Exp $
"""
import unittest

from Products.PluggableAuthService.tests.conformance \
    import IRequestTypeSniffer_conformance

class RequestTypeSniffer( unittest.TestCase
                          , IRequestTypeSniffer_conformance 
                        ):


    def _getTargetClass( self ):

        from Products.PluggableAuthService.plugins.RequestTypeSniffer \
            import RequestTypeSniffer

        return RequestTypeSniffer

    def _makeOne( self, id='test', *args, **kw ):

        return self._getTargetClass()( id, *args, **kw )


if __name__ == "__main__":
    unittest.main()
        
def test_suite():
    return unittest.TestSuite((
        unittest.makeSuite( RequestTypeSniffer ),
        ))
        


=== Added File Products/PluggableAuthService/plugins/tests/test_doctests.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
# Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this
# distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################

import unittest
from Testing import ZopeTestCase

ZopeTestCase.installProduct('PythonScripts')
ZopeTestCase.installProduct('PluginRegistry')
ZopeTestCase.installProduct('PluggableAuthService')

def test_suite():
    suite = unittest.TestSuite()
    package = 'Products.PluggableAuthService.plugins.tests'
    tests = [
        ZopeTestCase.FunctionalDocFileSuite('ChallengeProtocolChooser.txt',
                                            package=package),
        ]
    for t in tests:
        suite.addTest(t)
    return suite



More information about the Zope-CVS mailing list