Permalink
Browse files

Get rid of repoze.what-pylons dependency and merge repoze.what-quicks…

…tart removal from pylons-less branch
  • Loading branch information...
1 parent 9791c7a commit 13157ae45f045046a36053d97230c98e0804cb13 @amol- amol- committed Jan 30, 2012
View
@@ -13,12 +13,13 @@
'jinja2',
'Chameleon < 2.0a',
'simplegeneric',
- 'repoze.what >= 1.0.8',
- 'repoze.what-quickstart >= 1.0.3',
+ 'repoze.what >= 1.0.3',
+ 'repoze.who >= 1.0.18, <= 1.99',
+ 'repoze.who.plugins.sa >= 1.0.1',
+ 'repoze.what.plugins.sql >= 1.0rc2',
'repoze.who-testutil >= 1.0.1',
'repoze.what-pylons >= 1.0',
"repoze.who-friendlyform >=1.0.4",
- 'repoze.who',
'repoze.tm2 >= 1.0a4',
'wsgiref',
'tw.forms',
@@ -0,0 +1 @@
+from app_config import AppConfig, config
@@ -700,7 +700,7 @@ def add_auth_middleware(self, app, skip_authentication):
raise TGConfigError(msg)
if self.auth_backend == "sqlalchemy":
- from repoze.what.plugins.quickstart import setup_sql_auth
+ from sqlauth_config import setup_sql_auth
app = setup_sql_auth(app, skip_authentication=skip_authentication, **auth_args)
elif self.auth_backend == "ming":
from tgming import setup_ming_auth
@@ -0,0 +1,268 @@
+# -*- coding: utf-8 -*-
+"""
+Sample plugins and middleware configuration for :mod:`repoze.who` and
+:mod:`repoze.what`.
+Mostly took from repoze.what-quickstart.
+"""
+
+import sys, logging, copy
+
+from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin
+from repoze.who.plugins.sa import SQLAlchemyAuthenticatorPlugin,SQLAlchemyUserMDPlugin
+from repoze.who.plugins.friendlyform import FriendlyFormPlugin
+from repoze.who.config import _LEVELS
+
+from repoze.what.middleware import setup_auth
+from repoze.what.plugins.sql import configure_sql_adapters
+
+__all__ = ("setup_sql_auth")
+
+def copy_dict_keys(d, keys, replacements):
+ new_dict = {}
+ for index, key in enumerate(replacements):
+ old_key = keys[index]
+ if old_key in d:
+ new_dict[key] = d[old_key]
+ return new_dict
+
+def setup_sql_auth(app, user_class, group_class, permission_class,
+ dbsession, form_plugin=None, form_identifies=True,
+ cookie_secret='secret', cookie_name='authtkt',
+ login_url='/login', login_handler='/login_handler',
+ post_login_url=None, logout_handler='/logout_handler',
+ post_logout_url=None, login_counter_name=None,
+ translations={}, cookie_timeout=None,
+ cookie_reissue_time=None, charset="iso-8859-1",
+ use_default_authenticator=True,
+ **who_args):
+ """
+ Configure :mod:`repoze.who` and :mod:`repoze.what` with SQL-only
+ authentication and authorization, respectively.
+
+ :param app: Your WSGI application.
+ :param user_class: The SQLAlchemy/Elixir class for the users.
+ :param group_class: The SQLAlchemy/Elixir class for the groups.
+ :param permission_class: The SQLAlchemy/Elixir class for the permissions.
+ :param dbsession: The SQLAlchemy/Elixir session.
+ :param form_plugin: The main :mod:`repoze.who` challenger plugin; this is
+ usually a login form.
+ :param form_identifies: Whether the ``form_plugin`` may and should act as
+ an :mod:`repoze.who` identifier.
+ :type form_identifies: bool
+ :param cookie_secret: The "secret" for the AuthTktCookiePlugin (**set a
+ custom one!**).
+ :type cookie_secret: str
+ :param cookie_name: The name for the AuthTktCookiePlugin.
+ :type cookie_name: str
+ :param login_url: The URL where the login form is displayed.
+ :type login_url: str
+ :param login_handler: The URL where the login form is submitted.
+ :type login_handler: str
+ :param post_login_url: The URL/path where users should be redirected to
+ after login.
+ :type post_login_url: str
+ :param logout_handler: The URL where the logout is handled.
+ :type login_handler: str
+ :param post_logout_url: The URL/path where users should be redirected to
+ after logout.
+ :type post_logout_url: str
+ :param login_counter_name: The name of the variable in the query string
+ that represents the login counter; defaults to ``__logins``.
+ :type login_counter_name: str
+ :param translations: The model translations.
+ :type translations: dict
+ :param cookie_timeout: The time (in seconds) during which the session cookie
+ would be valid.
+ :type cookie_timeout: :class:`int`
+ :param cookie_reissue_time: How often should the session cookie be reissued
+ (in seconds); must be less than ``timeout``.
+ :type cookie_reissue_time: :class:`int`
+ :param use_default_authenticator: Whether the default SQL authenticator
+ should be used.
+ :type use_default_authenticator: :class:`bool`
+ :return: The WSGI application with authentication and authorization
+ middleware.
+
+ It configures :mod:`repoze.who` with the following plugins:
+
+ * Identifiers:
+
+ * :class:`repoze.who.plugins.friendlyform.FriendlyFormPlugin` as the
+ first identifier and challenger -- using ``login`` as the URL/path
+ where the login form will be displayed, ``login_handler`` as the
+ URL/path where the form will be sent and ``logout_handler`` as the
+ URL/path where the user will be logged out. The so-called *rememberer*
+ of such an identifier will be the identifier below.
+
+ If ``post_login_url`` is defined, the user will be redirected to that
+ page after login. Likewise, if ``post_logout_url`` is defined, the
+ user will be redirected to that page after logout.
+
+ You can override the
+ :class:`repoze.who.plugins.friendlyform.FriendlyFormPlugin`'s login
+ counter variable name (which defaults to ``__logins``) by defining
+ ``login_counter_name``.
+
+ .. tip::
+
+ This plugin may be overridden with the ``form_plugin`` argument.
+ See also the ``form_identifies`` argument.
+ * :class:`repoze.who.plugins.auth_tkt.AuthTktCookiePlugin`. You can
+ customize the cookie name and secret using the ``cookie_name`` and
+ ``cookie_secret`` arguments, respectively.
+
+ Then it will append the identifiers you pass through the ``identifiers``
+ keyword argument, if any.
+
+ * Authenticators:
+
+ * :class:`repoze.who.plugins.sa.SQLAlchemyAuthenticatorPlugin` (unless
+ ``use_default_authenticator`` is ``False``), using the ``user_class``
+ and ``dbsession`` arguments as its user class and DB session,
+ respectively.
+
+ Then it will be appended to the authenticators you pass through the
+ ``authenticators`` keyword argument, if any. The default authenticator
+ would have the lowest precedence.
+
+ * Challengers:
+
+ * The same Form-based plugin used in the identifiers.
+
+ Then it will append the challengers you pass through the
+ ``challengers`` keyword argument, if any.
+
+ * Metadata providers:
+
+ * :class:`repoze.who.plugins.sa.SQLAlchemyUserMDPlugin`, using
+ the ``user_class`` and ``dbsession`` arguments as its user class and
+ DB session, respectively.
+
+ Then it will append the metadata providers you pass through the
+ ``mdproviders`` keyword argument, if any.
+
+ The ``charset`` is passed to any component which needs to decode/encode
+ data to/from the user. At present, only
+ :class:`~repoze.who.plugins.friendlyform.FriendlyFormPlugin` does.
+
+ Additional keyword arguments will be passed to
+ :class:`repoze.who.middleware.PluggableAuthenticationMiddleware`.
+
+ .. warning::
+
+ It's very important to set a custom ``cookie_secret``! It's the key to
+ encrypt *and* decrypt the cookies, so you shouldn't leave the default
+ one.
+
+ .. note::
+
+ If you don't want to use the groups/permissions-based authorization
+ pattern, then set ``group_class`` and ``permission_class`` to ``None``.
+
+ .. versionchanged:: 1.0.5
+ Introduced the ``cookie_timeout`` and ``cookie_reissue_time`` arguments.
+
+ .. versionchanged:: 1.0.6
+ Introduced the ``charset`` argument.
+
+ .. versionchanged:: 1.0.8
+ Introduced the ``use_default_authenticator`` argument.
+
+ """
+ plugin_translations = {'group_adapter':copy_dict_keys(translations,
+ ['user_name', 'users', 'group_name', 'groups'],
+ ['item_name', 'items', 'section_name', 'sections']),
+ 'permission_adapter':copy_dict_keys(translations,
+ ['group_name', 'groups', 'permission_name', 'permissions'],
+ ['item_name', 'items', 'section_name', 'sections']),
+ 'authenticator':copy_dict_keys(translations,
+ ['validate_password', 'user_name'],
+ ['validate_password', 'user_name']),
+ 'mdprovider':copy_dict_keys(translations, ['user_name'], ['user_name'])}
+
+ source_adapters = configure_sql_adapters(
+ user_class,
+ group_class,
+ permission_class,
+ dbsession,
+ plugin_translations['group_adapter'],
+ plugin_translations['permission_adapter'])
+
+ group_adapters= {}
+ group_adapter = source_adapters.get('group')
+ if group_adapter:
+ group_adapters = {'sql_auth': group_adapter}
+
+ permission_adapters = {}
+ permission_adapter = source_adapters.get('permission')
+ if permission_adapter:
+ permission_adapters = {'sql_auth': permission_adapter}
+
+ # Setting the repoze.who authenticators:
+ if 'authenticators' not in who_args:
+ who_args['authenticators'] = []
+
+ if use_default_authenticator:
+ sqlauth = SQLAlchemyAuthenticatorPlugin(user_class, dbsession)
+ sqlauth.translations.update(plugin_translations['authenticator'])
+ who_args['authenticators'].append(('sqlauth', sqlauth))
+
+ cookie = AuthTktCookiePlugin(cookie_secret, cookie_name,
+ timeout=cookie_timeout,
+ reissue_time=cookie_reissue_time)
+
+ # Setting the repoze.who identifiers
+ if 'identifiers' not in who_args:
+ who_args['identifiers'] = []
+ who_args['identifiers'].append(('cookie', cookie))
+
+ if form_plugin is None:
+ form = FriendlyFormPlugin(
+ login_url,
+ login_handler,
+ post_login_url,
+ logout_handler,
+ post_logout_url,
+ login_counter_name=login_counter_name,
+ rememberer_name='cookie',
+ charset=charset,
+ )
+ else:
+ form = form_plugin
+
+ if form_identifies:
+ who_args['identifiers'].insert(0, ('main_identifier', form))
+
+ # Setting the repoze.who challengers:
+ if 'challengers' not in who_args:
+ who_args['challengers'] = []
+ who_args['challengers'].append(('form', form))
+
+ # Setting up the repoze.who mdproviders:
+ sql_user_md = SQLAlchemyUserMDPlugin(user_class, dbsession)
+ sql_user_md.translations.update(plugin_translations['mdprovider'])
+ if 'mdproviders' not in who_args:
+ who_args['mdproviders'] = []
+ who_args['mdproviders'].append(('sql_user_md', sql_user_md))
+
+ # Including logging
+ log_file = who_args.pop('log_file', None)
+ if log_file is not None:
+ if log_file.lower() == 'stdout':
+ log_stream = sys.stdout
+ elif log_file.lower() == 'stderr':
+ log_stream = sys.stderr
+ else:
+ log_stream = open(log_file, 'wb')
+ who_args['log_stream'] = log_stream
+
+ log_level = who_args.get('log_level', None)
+ if log_level is None:
+ log_level = logging.INFO
+ else:
+ log_level = _LEVELS[log_level.lower()]
+ who_args['log_level'] = log_level
+
+ middleware = setup_auth(app, group_adapters, permission_adapters,
+ **who_args)
+ return middleware
View
@@ -23,16 +23,9 @@
from tg import tmpl_context
from tg.util import partial
-try:
- from repoze.what.plugins.pylonshq import ActionProtector
- from repoze.what.plugins.pylonshq.protectors import _BaseProtectionDecorator
-except ImportError:
- class ActionProtector(object):
- pass
- class _BaseProtectionDecorator(object):
- pass
+from repoze.what.predicates import NotAuthorizedError
-from tg.configuration import Bunch
+from tg.util import Bunch
from tg.flash import flash
#from tg.controllers import redirect
@@ -618,9 +611,27 @@ def sample(self, *args):
#{ Authorization decorators
-class require(ActionProtector):
+class _BaseProtectionDecorator(object):
+ default_denial_handler = None
+
+ def __init__(self, predicate, denial_handler=None):
+ """
+ Make :mod:`repoze.what` verify that the predicate is met.
+
+ :param predicate: A :mod:`repoze.what` predicate.
+ :param denial_handler: The callable to be run if authorization is
+ denied (overrides :attr:`default_denial_handler` if defined).
+
+ If called, ``denial_handler`` will be passed a positional argument
+ which represents a message on why authorization was denied.
+
+ """
+ self.predicate = predicate
+ self.denial_handler = denial_handler or self.default_denial_handler
+
+class require(_BaseProtectionDecorator):
"""
- TurboGears-specific repoze.what-pylons action protector.
+ TurboGears-specific repoze.what action protector.
The default authorization denial handler of this protector will flash
the message of the unmet predicate with ``warning`` or ``error`` as the
@@ -629,9 +640,30 @@ class require(ActionProtector):
See :class:`allow_only` for controller-wide authorization.
"""
+ def __call__(self, action_):
+ return decorator(self.wrap_action, action_)
+
+ def wrap_action(self, action_, *args, **kwargs):
+ req = request._current_obj()
+
+ try:
+ self.predicate.check_authorization(req.environ)
+ except NotAuthorizedError, e:
+ reason = unicode(e)
+ if req.environ.get('repoze.who.identity'):
+ # The user is authenticated.
+ code = 403
+ else:
+ # The user is not authenticated.
+ code = 401
+ if self.denial_handler:
+ response.status = code
+ return self.denial_handler(reason)
+ abort(code, comment=reason)
+ return action_(*args, **kwargs)
def default_denial_handler(self, reason):
- """Authorization denial handler for repoze.what-pylons protectors."""
+ """Authorization denial handler for repoze.what protectors."""
if response.status_int == 401:
status = 'warning'
else:
@@ -643,7 +675,7 @@ def default_denial_handler(self, reason):
class allow_only(_BaseProtectionDecorator):
"""
- TurboGears-specific repoze.what-pylons controller protector.
+ TurboGears-specific repoze.what controller protector.
The default authorization denial handler of this protector will flash
the message of the unmet predicate with ``warning`` or ``error`` as the
View
@@ -10,7 +10,7 @@
from webhelpers.html import literal
import tg
-from tg.configuration import Bunch
+from tg.util import Bunch
from webhelpers.html import literal

0 comments on commit 13157ae

Please sign in to comment.