Permalink
Browse files

Introduced the SQLAlchemyUserMDPlugin metadata provider.

  • Loading branch information...
1 parent caf439c commit bb579c6a74ab1e8f323f669e2a11cb5e8a41a041 @gnarea gnarea committed Jan 26, 2009
Showing with 284 additions and 37 deletions.
  1. +9 −0 docs/source/News.rst
  2. +9 −0 docs/source/index.rst
  3. +154 −37 repoze/who/plugins/sa.py
  4. +112 −0 tests/test_mdprovider.py
View
@@ -5,6 +5,15 @@
This document describes the releases of :mod:`repoze.who.plugins.sa`.
+.. _repoze.who.plugins.sa-1.0rc1:
+
+:mod:`repoze.who.plugins.sa` 1.0rc1 (2009-01-26)
+================================================
+* Introduced the :class:`repoze.who.plugins.sa.SQLAlchemyUserMDPlugin` metadata
+ provider.
+* Minor docstring fixes.
+
+
.. _repoze.who.plugins.sa-1.0b3:
:mod:`repoze.who.plugins.sa` 1.0b3 (2009-01-08)
View
@@ -34,6 +34,15 @@ Authenticator
.. autofunction:: make_sa_authenticator
+
+Metadata provider
+=================
+
+.. autoclass:: SQLAlchemyUserMDPlugin
+
+.. autofunction:: make_sa_user_mdprovider
+
+
How to get help?
================
View
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2008, Gustavo Narea <me@gustavonarea.net>
+# Copyright (c) 2008-2009, Gustavo Narea <me@gustavonarea.net>
# All Rights Reserved.
#
# This software is subject to the provisions of the BSD-like license at
@@ -13,20 +13,59 @@
#
##############################################################################
-"""SQLAlchemy plugin for repoze.who."""
+"""
+SQLAlchemy plugin for repoze.who.
+
+TODO: Write a function that configures both plugins in one go.
+
+"""
from zope.interface import implements
-from repoze.who.interfaces import IAuthenticator
+from repoze.who.interfaces import IAuthenticator, IMetadataProvider
from repoze.who.utils import resolveDotted
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
-__all__ = ['SQLAlchemyAuthenticatorPlugin', 'make_sa_authenticator']
+__all__ = ['SQLAlchemyAuthenticatorPlugin', 'SQLAlchemyUserMDPlugin',
+ 'make_sa_authenticator', 'make_sa_user_mdprovider']
-class SQLAlchemyAuthenticatorPlugin(object):
+class _BaseSQLAlchemyPlugin(object):
+
+ def __init__(self, user_class, dbsession):
+ """
+ Setup the plugin.
+
+ :param user_class: The SQLAlchemy/Elixir class for the users.
+ :param session: The SQLAlchemy/Elixir session.
+
+ """
+ self.user_class = user_class
+ self.dbsession = dbsession
+ self.translations = self.default_translations.copy()
+
+ def get_user(self, username):
+ # Getting a translation:
+ username_attr = getattr(self.user_class,
+ self.translations['user_name'])
+
+ query = self.dbsession.query(self.user_class)
+ query = query.filter(username_attr==username)
+
+ try:
+ return query.one()
+ except (NoResultFound, MultipleResultsFound):
+ # As recommended in the docs for repoze.who, it's important to
+ # verify that there's only _one_ matching userid.
+ return None
+
+
+#{ repoze.who plugins
+
+
+class SQLAlchemyAuthenticatorPlugin(_BaseSQLAlchemyPlugin):
"""
- repoze.who authenticator for SQLAlchemy models.
+ :mod:`repoze.who` authenticator for SQLAlchemy models.
Example::
@@ -41,7 +80,7 @@ class SQLAlchemyAuthenticatorPlugin(object):
login form (it receives the password to be verified as the only argument
and such method is assumed to be called ``validate_password``).
- If you don't want to call the attributes below as ``user_name`` and/or
+ If you don't want to call the attributes above as ``user_name`` and/or
``validate_password``, respectively, then you have to "translate" them as
in the sample below::
@@ -60,17 +99,7 @@ class SQLAlchemyAuthenticatorPlugin(object):
implements(IAuthenticator)
- def __init__(self, user_class, dbsession):
- """
- Setup the authenticator.
-
- :param user_class: The SQLAlchemy/Elixir class for the users.
- :param session: The SQLAlchemy/Elixir session.
-
- """
- self.user_class = user_class
- self.dbsession = dbsession
- self.translations = {
+ default_translations = {
'user_name': 'user_name',
'validate_password': 'validate_password'
}
@@ -80,22 +109,65 @@ def authenticate(self, environ, identity):
if not ('login' in identity and 'password' in identity):
return None
- # Getting a translation:
- username = getattr(self.user_class, self.translations['user_name'])
+ user = self.get_user(identity['login'])
- query = self.dbsession.query(self.user_class)
- query = query.filter(username==identity['login'])
-
- try:
- user = query.one()
- # Getting the other translation:
+ if user:
validator = getattr(user, self.translations['validate_password'])
if validator(identity['password']):
- return getattr(user, self.translations['user_name'])
- except (NoResultFound, MultipleResultsFound):
- # As recommended in the docs for repoze.who, it's important to
- # verify that there's only _one_ matching userid.
- return None
+ return identity['login']
+
+
+class SQLAlchemyUserMDPlugin(_BaseSQLAlchemyPlugin):
+ """
+ :mod:`repoze.who` metadata provider that loads the SQLAlchemy-powered
+ object for the current user.
+
+ It loads the object into ``identity['user']``.
+
+ Example::
+
+ from repoze.who.plugins.sa import SQLAlchemyUserMDPlugin
+ from yourcoolproject.model import User, DBSession
+
+ mdprovider = SQLAlchemyUserMDPlugin(User, DBSession)
+
+ This plugin assumes that the user name is kept in the ``user_name``
+ attribute of the users' class. If you don't want to call the attribute
+ above as ``user_name``, then you have to "translate" it as in the sample
+ below::
+
+ # You have User.username instead of User.user_name:
+ authenticator.translations['user_name'] = 'username'
+
+ .. note::
+
+ If you want to configure this plugin from an ``ini`` file, use
+ :func:`make_sa_user_mdprovider`.
+
+ """
+
+ implements(IMetadataProvider)
+
+ default_translations = {'user_name': 'user_name'}
+
+ def add_metadata(self, environ, identity):
+ identity['user'] = self.get_user(identity['repoze.who.userid'])
+
+
+#{ Functions to instantiate the plugins from a Paste configuration
+
+
+def _base_plugin_maker(user_class=None, dbsession=None):
+ """
+ Turn ``userclass`` and ``dbsession`` into Python objects.
+
+ """
+
+ if user_class is None:
+ raise ValueError('user_class must not be None')
+ if dbsession is None:
+ raise ValueError('dbsession must not be None')
+ return resolveDotted(user_class), resolveDotted(dbsession)
def make_sa_authenticator(user_class=None, dbsession=None,
@@ -112,6 +184,8 @@ def make_sa_authenticator(user_class=None, dbsession=None,
:type user_name_translation: str
:param validate_password_translation: The translation for ``validate_password``, if any.
:type validate_password_translation: str
+ :return: The authenticator.
+ :rtype: SQLAlchemyAuthenticatorPlugin
Example from an ``*.ini`` file::
@@ -135,12 +209,7 @@ def make_sa_authenticator(user_class=None, dbsession=None,
"""
- if user_class is None:
- raise ValueError('user_class must not be None')
- if dbsession is None:
- raise ValueError('user_class must not be None')
- user_model = resolveDotted(user_class)
- dbsession_object = resolveDotted(dbsession)
+ user_model, dbsession_object = _base_plugin_maker(user_class, dbsession)
authenticator = SQLAlchemyAuthenticatorPlugin(user_model, dbsession_object)
@@ -151,3 +220,51 @@ def make_sa_authenticator(user_class=None, dbsession=None,
validate_password_translation
return authenticator
+
+
+def make_sa_user_mdprovider(user_class=None, dbsession=None,
+ user_name_translation=None):
+ """
+ Configure :class:`SQLAlchemyUserMDPlugin`.
+
+ :param user_class: The SQLAlchemy/Elixir class for the users.
+ :type user_class: str
+ :param dbsession: The SQLAlchemy/Elixir session.
+ :type dbsession: str
+ :param user_name_translation: The translation for ``user_name``, if any.
+ :type user_name_translation: str
+ :return: The metadata provider.
+ :rtype: SQLAlchemyUserMDPlugin
+
+ Example from an ``*.ini`` file::
+
+ # ...
+ [plugin:sa_md]
+ use = repoze.who.plugins.sa:make_sa_user_mdprovider
+ user_class = yourcoolproject.model:User
+ dbsession = yourcoolproject.model:DBSession
+ # ...
+
+ Or, if you need translations::
+
+ # ...
+ [plugin:sa_auth]
+ use = repoze.who.plugins.sa:make_sa_user_mdprovider
+ user_class = yourcoolproject.model:User
+ dbsession = yourcoolproject.model:DBSession
+ user_name_translation = username
+ # ...
+
+ """
+
+ user_model, dbsession_object = _base_plugin_maker(user_class, dbsession)
+
+ mdprovider = SQLAlchemyUserMDPlugin(user_model, dbsession_object)
+
+ if user_name_translation:
+ mdprovider.translations['user_name'] = user_name_translation
+
+ return mdprovider
+
+
+#}
Oops, something went wrong.

0 comments on commit bb579c6

Please sign in to comment.