Author: chrisz
Date: Mon Jan 17 17:44:30 2011
New Revision: 7209
URL: http://trac.turbogears.org/changeset/7209
Log:
Made visit/identity Tests work with both SA and SO and both CherryPy 3.1 and
3.2.
Modified:
branches/1.5/turbogears/identity/base.py
branches/1.5/turbogears/identity/exceptions.py
branches/1.5/turbogears/identity/tests/test_identity_sa.py
branches/1.5/turbogears/identity/tests/test_identity_so.py
branches/1.5/turbogears/identity/visitor.py
branches/1.5/turbogears/startup.py
branches/1.5/turbogears/testutil.py
branches/1.5/turbogears/visit/__init__.py
branches/1.5/turbogears/visit/api.py
Modified: branches/1.5/turbogears/identity/base.py
==============================================================================
--- branches/1.5/turbogears/identity/base.py Mon Jan 17 14:34:21 2011
(r7208)
+++ branches/1.5/turbogears/identity/base.py Mon Jan 17 17:44:30 2011
(r7209)
@@ -85,7 +85,6 @@
try:
cherrypy.request.user_name = identity.user_name
-
except AttributeError:
cherrypy.request.user_name = None
@@ -149,7 +148,6 @@
def identity(self):
try:
identity = cherrypy.request.identity
-
except AttributeError:
identity = None
@@ -184,7 +182,6 @@
def __getattr__(self, name):
try:
provider = cherrypy.request.identityProvider
-
except AttributeError:
try:
provider = create_default_provider()
Modified: branches/1.5/turbogears/identity/exceptions.py
==============================================================================
--- branches/1.5/turbogears/identity/exceptions.py Mon Jan 17 14:34:21
2011 (r7208)
+++ branches/1.5/turbogears/identity/exceptions.py Mon Jan 17 17:44:30
2011 (r7209)
@@ -50,19 +50,22 @@
presence of an HTTP request.
"""
+
def __str__(self):
- return "An attempt was made to use a facility of the TurboGears " \
- "Identity Management framework that relies on an HTTP request " \
- "outside of a request."
+ return self.args and self.args[0] or (
+ "An attempt was made to use a facility of the TurboGears "
+ "Identity Management framework that relies on an HTTP request "
+ "outside of a request.")
class IdentityManagementNotEnabledException(IdentityException):
"""User forgot to enable Identity management."""
def __str__(self):
- return "An attempt was made to use a facility of the TurboGears " \
- "Identity Management framework, but identity management hasn't " \
- "been enabled in the config file [via identity.on]."
+ return self.args and self.args[0] or (
+ "An attempt was made to use a facility of the TurboGears "
+ "Identity Management framework, but identity management hasn't "
+ "been enabled in the config file (via identity.on).")
class IdentityConfigurationException(IdentityException):
@@ -72,11 +75,10 @@
correctly. Mostly, when failure_url is not specified.
"""
- args = ()
def __str__(self):
- return (self.args and self.args[0] or
- 'Unknown Identity configuration error')
+ return self.args and self.args[0] or (
+ "Unknown Identity configuration error.")
class IdentityFailure(cherrypy.InternalRedirect, IdentityException):
Modified: branches/1.5/turbogears/identity/tests/test_identity_sa.py
==============================================================================
--- branches/1.5/turbogears/identity/tests/test_identity_sa.py Mon Jan 17
14:34:21 2011 (r7208)
+++ branches/1.5/turbogears/identity/tests/test_identity_sa.py Mon Jan 17
17:44:30 2011 (r7209)
@@ -7,7 +7,7 @@
import formencode
import cherrypy
-from turbogears import testutil, database, identity, config, expose
+from turbogears import testutil, identity, config, expose
from turbogears.controllers import Controller, RootController
from turbogears.database import session
from turbogears.identity import (SecureObject, SecureResource,
@@ -274,7 +274,7 @@
@expose()
def is_anonymous(self):
- assert cherrypy.serving.request.identity.user_name == None
+ assert cherrypy.serving.request.identity.user_name is None
assert cherrypy.serving.request.identity.anonymous
assert identity.current.anonymous
return 'is_anonymous'
@@ -293,26 +293,35 @@
def setUp(self):
# identity requires visit and a failure_url
- test_config = {'global': {
- 'visit.on': True, 'identity.on': True,
+ test_config = {
+ 'visit.on': True,
'visit.manager': 'sqlalchemy',
- 'identity.failure_url': '/identity_failed',
+ 'identity.on': True,
'identity.provider': 'sqlalchemy',
- 'identity.saprovider.encryption_algorithm': None,
+ 'identity.failure_url': '/identity_failed',
+ 'identity.source': 'form,http_auth,visit',
'identity.http_basic_auth': False,
'identity.http_auth_realm': None,
+ 'identity.http_basic_auth': False,
+ 'identity.form.user_name': 'user_name',
+ 'identity.form.password': 'password',
+ 'identity.form.submit': 'login',
+ 'identity.saprovider.encryption_algorithm': None,
+ 'identity.custom_encryption': None,
'tg.strict_parameters': True,
- 'tg.allow_json': False}}
+ 'tg.allow_json': False}
if not self.config:
original_config = dict()
- for key in test_config['global']:
+ for key in test_config:
original_config[key] = config.get(key)
self.config = original_config
config.configure_loggers(test_config)
- config.update(test_config['global'])
+ config.update(test_config)
+ testutil.stop_server(tg_only=False)
super(TestIdentity, self).setUp()
self.init_model()
+
def init_model(self):
if not session.query(TG_User).first():
user = TG_User()
@@ -511,14 +520,18 @@
'user_name=samIam&password=secret&login=Login')
assert 'in_peon_group' in response, response
user = TG_User.by_user_name(u'samIam')
+ assert user.user_name == 'samIam'
def test_require_group_viewable(self):
"""Test that the current user and group properties are set
correctly."""
response = self.app.get('/in_peon_group?'
'user_name=samIam&password=secret&login=Login')
+ assert 'in_peon_group' in response, response
user = TG_User.by_user_name(u'samIam')
+ assert user.user_name == 'samIam'
group_ids = set((TG_Group.by_group_name(u'peon').group_id,
TG_Group.by_group_name(u'other').group_id))
+ assert group_ids == set((2, 3))
def test_user_not_in_right_group(self):
"""Test that a user is denied access if they aren't in the right group.
@@ -529,8 +542,7 @@
def test_require_permission(self):
"""Test that an anonymous user is denied access to a permission
- restricted url.
- """
+ restricted url."""
response = self.app.get('/has_chopper_permission', status=403)
assert 'identity_failed_answer' in response, response
@@ -623,6 +635,7 @@
user.email_address = u'[email protected]'
user.password = 'geheim'
session.add(user)
+ assert user.user_name == u'säm'
response = self.app.get('/logged_in_only?'
'user_name=säm&password=geheim&login=Login')
assert 'logged_in_only' in response, response
@@ -733,16 +746,17 @@
class TestIdentityVisitPlugin(unittest.TestCase):
def setUp(self):
- test_config = {'global': {
+ test_config = {
'visit.on': True,
+ 'visit.manager': 'sqlalchemy',
'identity.on': True,
'identity.provider': 'sqlalchemy',
- 'identity.source': 'form, http_auth, visit, '}}
+ 'identity.source': 'form, http_auth, visit, '}
original_config = dict()
- for key in test_config['global']:
+ for key in test_config:
original_config[key] = config.get(key)
self._original_config = original_config
- config.update(test_config['global'])
+ config.update(test_config)
def tearDown(self):
config.update(self._original_config)
Modified: branches/1.5/turbogears/identity/tests/test_identity_so.py
==============================================================================
--- branches/1.5/turbogears/identity/tests/test_identity_so.py Mon Jan 17
14:34:21 2011 (r7208)
+++ branches/1.5/turbogears/identity/tests/test_identity_so.py Mon Jan 17
17:44:30 2011 (r7209)
@@ -276,7 +276,7 @@
@expose()
def is_anonymous(self):
- assert cherrypy.serving.request.identity.user_name == None
+ assert cherrypy.serving.request.identity.user_name is None
assert cherrypy.serving.request.identity.anonymous
assert identity.current.anonymous
return 'is_anonymous'
@@ -295,23 +295,29 @@
def setUp(self):
# identity requires visit and a failure_url
- test_config = {'global': {
- 'visit.on': True, 'identity.on': True,
+ test_config = {
+ 'visit.on': True,
'visit.manager': 'sqlobject',
- 'identity.failure_url': '/identity_failed',
+ 'identity.on': True,
'identity.provider': 'sqlobject',
- 'identity.soprovider.encryption_algorithm': None,
+ 'identity.failure_url': '/identity_failed',
+ 'identity.source': 'form,http_auth,visit',
'identity.http_basic_auth': False,
'identity.http_auth_realm': None,
+ 'identity.form.user_name': 'user_name',
+ 'identity.form.password': 'password',
+ 'identity.form.submit': 'login',
+ 'identity.soprovider.encryption_algorithm': None,
+ 'identity.custom_encryption': None,
'tg.strict_parameters': True,
- 'tg.allow_json': False}}
+ 'tg.allow_json': False}
if not self.config:
original_config = dict()
- for key in test_config['global']:
+ for key in test_config:
original_config[key] = config.get(key)
self.config = original_config
config.configure_loggers(test_config)
- config.update(test_config['global'])
+ config.update(test_config)
super(TestIdentity, self).setUp()
self.init_model()
@@ -540,14 +546,18 @@
'user_name=samIam&password=secret&login=Login')
assert 'in_peon_group' in response, response
user = TG_User.by_user_name('samIam')
+ assert user.user_name == 'samIam'
def test_require_group_viewable(self):
"""Test that the current user and group properties are set
correctly."""
response = self.app.get('/in_peon_group?'
'user_name=samIam&password=secret&login=Login')
+ assert 'in_peon_group' in response, response
user = TG_User.by_user_name('samIam')
+ assert user.user_name == 'samIam'
group_ids = set((TG_Group.by_group_name('peon').id,
TG_Group.by_group_name('other').id))
+ assert group_ids == set((2, 3))
def test_user_not_in_right_group(self):
"""Test that a user is denied access if they aren't in the right group.
@@ -558,8 +568,7 @@
def test_require_permission(self):
"""Test that an anonymous user is denied access to a permission
- restricted url.
- """
+ restricted url."""
response = self.app.get('/has_chopper_permission', status=403)
assert 'identity_failed_answer' in response, response
@@ -648,6 +657,7 @@
"""Test that we can have non-ascii user names."""
user = TG_User(user_name=u'säm', display_name=u'Sämüel Käsfuß',
email_address='[email protected]', password='geheim')
+ assert user.user_name == u'säm'
response = self.app.get('/logged_in_only?'
'user_name=säm&password=geheim&login=Login')
assert 'logged_in_only' in response, response
@@ -759,16 +769,17 @@
class TestIdentityVisitPlugin(unittest.TestCase):
def setUp(self):
- test_config = {'global': {
+ test_config = {
'visit.on': True,
+ 'visit.manager': 'sqlobject',
'identity.on': True,
'identity.provider': 'sqlobject',
- 'identity.source': 'form, http_auth, visit, '}}
+ 'identity.source': 'form, http_auth, visit, '}
original_config = dict()
- for key in test_config['global']:
+ for key in test_config:
original_config[key] = config.get(key)
self._original_config = original_config
- config.update(test_config['global'])
+ config.update(test_config)
def tearDown(self):
config.update(self._original_config)
Modified: branches/1.5/turbogears/identity/visitor.py
==============================================================================
--- branches/1.5/turbogears/identity/visitor.py Mon Jan 17 14:34:21 2011
(r7208)
+++ branches/1.5/turbogears/identity/visitor.py Mon Jan 17 17:44:30 2011
(r7209)
@@ -24,14 +24,20 @@
log = logging.getLogger("turbogears.identity")
+# Global Visit plugin
+_plugin = None
+
+
# Interface for the TurboGears extension
def start_extension():
- """Register the IdentityVisitPluguin with the visit plug-in framework.
+ """Register the IdentityVisitPlugin with the visit plugin framework.
Also sets up the configured Identity provider.
"""
+ global _plugin
+
# Bail out if the application hasn't enabled this extension
if not config.get('identity.on', False):
return
@@ -39,24 +45,37 @@
# Identity requires that Visit tracking be enabled
if not config.get('visit.on', False):
raise IdentityConfigurationException(
- "Visit tracking must be enabled (visit.on)")
+ "Visit tracking must be enabled (via visit.on).")
+
+ # Bail out if Visit tracking plugin is already registered
+ if _plugin:
+ log.info("Identity already started")
+ return
log.info("Identity starting")
# Temporary until tg-admin can call create_extension_model
create_extension_model()
- # Register the plugin for the Visit Tracking framework
- visit.enable_visit_plugin(IdentityVisitPlugin())
+ # Create and register the plugin for the Visit Tracking framework
+ _plugin = IdentityVisitPlugin()
+ visit.enable_visit_plugin(_plugin)
def shutdown_extension():
- """Stops the IdentityVisitPlugin.
+ """Stops the IdentityVisitPlugin."""
+ global _plugin
- This is practically a no-op currently.
-
- """
# Bail out if the application hasn't enabled this extension
if not config.get('identity.on', False):
return
+
+ # Bail out if the Visit tracking plugin is already unregistered
+ if not _plugin:
+ log.info("Identity already shut down")
+ return
+
+ # Unregister the plugin for the Visit Tracking framework
+ visit.disable_visit_plugin(_plugin)
+ _plugin = None
log.info("Identity has been shut down.")
@@ -67,7 +86,7 @@
class IdentityVisitPlugin(object):
- """Visit plug-in tying the Identity framework to the visit management."""
+ """Visit plugin tying the Identity framework to the visit management."""
def __init__(self):
log.info("Identity visit plugin initialized")
@@ -85,7 +104,10 @@
# Sources for identity information and the order in which they should
be
# checked. These terms are mapped to methods by prepending
# "identity_from_".
- sources = get('identity.source', 'form,http_auth,visit') or ''
+ sources = get('identity.source', 'form,http_auth,visit')
+ if not sources:
+ raise IdentityConfigurationException(
+ "You must set some identity source (via identity.source).")
sources = [s.strip() for s in sources.split(',')]
self.identity_sources = list()
for s in sources:
@@ -93,8 +115,8 @@
try:
source_method = getattr(self, 'identity_from_' + s)
except AttributeError:
- raise IdentityConfigurationException(
- "Invalid identity source: %s" % s)
+ raise IdentityConfigurationException("Invalid "
+ "identity source: %s (check identity.source)" % s)
self.identity_sources.append(source_method)
def identity_from_request(self, visit_key):
@@ -202,7 +224,7 @@
def record_request(self, visit):
"""Authenticate request and try to associate the visit with an
identity."""
- # This method is called by the visit plug-in mechanism on each request
with a visit key.
+ # This method is called by the visit plugin mechanism on each request
with a visit key.
# default to keeping the identity filter off
if not config.get('identity.on', True):
log.debug("Identity is not enabled. Setting current identity to
None")
Modified: branches/1.5/turbogears/startup.py
==============================================================================
--- branches/1.5/turbogears/startup.py Mon Jan 17 14:34:21 2011 (r7208)
+++ branches/1.5/turbogears/startup.py Mon Jan 17 17:44:30 2011 (r7209)
@@ -26,6 +26,7 @@
from turbogears import config, database, scheduler, view
from turbogears.visit.api import VisitTool
+from turbogears.identity.exceptions import IdentityConfigurationException
from turbogears.identity.base import verify_identity_status
from turbogears.database import hub_registry
from turbogears.dispatchers import VirtualPathDispatcher
@@ -160,18 +161,18 @@
log.info("Adding CherryPy tools, hooks and dispatchers...")
config_static()
config_root()
- if config.get('visit.on', False):
- # The VisitTool needs to happen after cherrypy.tools.decode
- # so that request params are properly decoded before it runs,
- # but it must run before the NestedVariablesHook to work properly
- cherrypy.tools.visit = cherrypy.Tool(
- 'before_handler', VisitTool(), priority=55)
hooks = cherrypy.request.hooks
hooks.attach('before_finalize', verify_identity_status)
hooks.attach('on_end_resource', database.EndTransactions)
# The NestedVariablesHook needs to happen after cherrypy.tools.decode
# so that request params are properly decoded before it runs
- hooks.attach('before_handler', NestedVariablesHook, priority=60)
+ hooks.attach('before_handler', NestedVariablesHook, priority=70)
+ if config.get('visit.on', False):
+ # The VisitTool needs to happen after cherrypy.tools.decode
+ # so that request params are properly decoded before it runs,
+ # but it must run before the NestedVariablesHook to work properly
+ cherrypy.tools.visit = cherrypy.Tool(
+ 'before_handler', VisitTool(), priority=60)
# Register server with Bonjour framework
bonjoursetting = config.get('tg.bonjour', None)
@@ -206,6 +207,8 @@
except Exception, e:
log.exception("Error starting TurboGears extension %s: %s",
entrypoint, e)
+ if isinstance(e, IdentityConfigurationException):
+ raise # don't swallow internal configuration error
# Call registered startup functions
if call_on_startup:
Modified: branches/1.5/turbogears/testutil.py
==============================================================================
--- branches/1.5/turbogears/testutil.py Mon Jan 17 14:34:21 2011 (r7208)
+++ branches/1.5/turbogears/testutil.py Mon Jan 17 17:44:30 2011 (r7209)
@@ -58,6 +58,8 @@
def mount(controller, path='/'):
"""Mount a controller at a path. Returns a WSGI application."""
startup.config_static()
+ if path == '/':
+ startup.config_root()
config.update({'environment': 'test_suite', 'log.screen': False})
cherrypy.tree.mount(controller, path, config=config.app)
return make_wsgiapp()
Modified: branches/1.5/turbogears/visit/__init__.py
==============================================================================
--- branches/1.5/turbogears/visit/__init__.py Mon Jan 17 14:34:21 2011
(r7208)
+++ branches/1.5/turbogears/visit/__init__.py Mon Jan 17 17:44:30 2011
(r7209)
@@ -8,11 +8,14 @@
'create_extension_model',
'current',
'enable_visit_plugin',
+ 'disable_visit_plugin',
'set_current',
'start_extension',
'shutdown_extension',
]
-from turbogears.visit.api import (BaseVisitManager, Visit, VisitTool,
- create_extension_model, current, enable_visit_plugin, set_current,
+from turbogears.visit.api import (
+ BaseVisitManager, Visit, VisitTool,
+ create_extension_model, current, set_current,
+ enable_visit_plugin, disable_visit_plugin,
start_extension, shutdown_extension)
Modified: branches/1.5/turbogears/visit/api.py
==============================================================================
--- branches/1.5/turbogears/visit/api.py Mon Jan 17 14:34:21 2011
(r7208)
+++ branches/1.5/turbogears/visit/api.py Mon Jan 17 17:44:30 2011
(r7209)
@@ -8,6 +8,7 @@
'create_extension_model',
'current',
'enable_visit_plugin',
+ 'disable_visit_plugin',
'set_current',
'start_extension',
'shutdown_extension',
@@ -120,6 +121,11 @@
_plugins.append(plugin)
+def disable_visit_plugin(plugin):
+ """Unregister a visit tracking plugin."""
+ _plugins[:] = [p for p in _plugins if p is not plugin]
+
+
class Visit(object):
"""Basic container for visit related data."""