laurance <nlaura...@qubic.tv> added the comment:
patch with tests
__________________________________
Repoze Bugs <b...@bugs.repoze.org>
<http://bugs.repoze.org/issue159>
__________________________________
diff -Naur -x '*svn' -x coverage official//repoze/what/plugins/sql/adapters.py trunk//repoze/what/plugins/sql/adapters.py
--- official//repoze/what/plugins/sql/adapters.py 2011-01-11 23:27:49.000000000 +0100
+++ trunk//repoze/what/plugins/sql/adapters.py 2011-01-11 22:41:23.000000000 +0100
@@ -63,6 +63,7 @@
from sqlalchemy.exceptions import SQLAlchemyError
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm import eagerload
+from sqlalchemy.exc import InvalidRequestError
from repoze.what.adapters import BaseSourceAdapter, SourceError
@@ -194,7 +195,11 @@
# "field" usually equals to {tg_package}.model.User.user_name
# or {tg_package}.model.Group.group_name
field = getattr(self.children_class, self.translations['item_name'])
- query = self.dbsession.query(self.children_class).options(eagerload(self.translations['sections']))
+ try:
+ query = self.dbsession.query(self.children_class).options(eagerload(self.translations['sections']))
+ except InvalidRequestError: # permissions might be a decorated method
+ query = self.dbsession.query(self.children_class)
+
try:
item_as_row = query.filter(field==item_name).one()
except NoResultFound:
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/dependency_links.txt trunk//repoze.what.plugins.sql.egg-info/dependency_links.txt
--- official//repoze.what.plugins.sql.egg-info/dependency_links.txt 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/dependency_links.txt 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1 @@
+
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/entry_points.txt trunk//repoze.what.plugins.sql.egg-info/entry_points.txt
--- official//repoze.what.plugins.sql.egg-info/entry_points.txt 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/entry_points.txt 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1 @@
+
\ No newline at end of file
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/namespace_packages.txt trunk//repoze.what.plugins.sql.egg-info/namespace_packages.txt
--- official//repoze.what.plugins.sql.egg-info/namespace_packages.txt 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/namespace_packages.txt 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1,3 @@
+repoze
+repoze.what
+repoze.what.plugins
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/not-zip-safe trunk//repoze.what.plugins.sql.egg-info/not-zip-safe
--- official//repoze.what.plugins.sql.egg-info/not-zip-safe 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/not-zip-safe 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1 @@
+
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/PKG-INFO trunk//repoze.what.plugins.sql.egg-info/PKG-INFO
--- official//repoze.what.plugins.sql.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/PKG-INFO 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1,30 @@
+Metadata-Version: 1.0
+Name: repoze.what.plugins.sql
+Version: 1.0.1dev-r0
+Summary: The repoze.what SQL plugin
+Home-page: http://what.repoze.org/docs/plugins/sql/
+Author: Gustavo Narea
+Author-email: repoze-dev@lists.repoze.org
+License: BSD-derived (http://www.repoze.org/LICENSE.txt)
+Description: **************************
+ The repoze.what SQL plugin
+ **************************
+
+ This is an adapters plugin for repoze.what.
+
+ The SQL plugin makes repoze.what support sources defined in SQLAlchemy-managed
+ databases by providing one group adapter, one permission adapter and one
+ utility to configure both in one go (optionally, when the group source and the
+ permission source have a relationship).
+
+Keywords: web application wsgi server wsgi sql sqlalchemy elixir authorization repoze
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Database
+Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
+Classifier: Topic :: Security
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/requires.txt trunk//repoze.what.plugins.sql.egg-info/requires.txt
--- official//repoze.what.plugins.sql.egg-info/requires.txt 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/requires.txt 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1,2 @@
+repoze.what >= 1.0.3
+sqlalchemy >= 0.5
\ No newline at end of file
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/SOURCES.txt trunk//repoze.what.plugins.sql.egg-info/SOURCES.txt
--- official//repoze.what.plugins.sql.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/SOURCES.txt 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1,18 @@
+MANIFEST.in
+README.txt
+VERSION.txt
+setup.cfg
+setup.py
+repoze/__init__.py
+repoze.what.plugins.sql.egg-info/PKG-INFO
+repoze.what.plugins.sql.egg-info/SOURCES.txt
+repoze.what.plugins.sql.egg-info/dependency_links.txt
+repoze.what.plugins.sql.egg-info/entry_points.txt
+repoze.what.plugins.sql.egg-info/namespace_packages.txt
+repoze.what.plugins.sql.egg-info/not-zip-safe
+repoze.what.plugins.sql.egg-info/requires.txt
+repoze.what.plugins.sql.egg-info/top_level.txt
+repoze/what/__init__.py
+repoze/what/plugins/__init__.py
+repoze/what/plugins/sql/__init__.py
+repoze/what/plugins/sql/adapters.py
\ No newline at end of file
diff -Naur -x '*svn' -x coverage official//repoze.what.plugins.sql.egg-info/top_level.txt trunk//repoze.what.plugins.sql.egg-info/top_level.txt
--- official//repoze.what.plugins.sql.egg-info/top_level.txt 1970-01-01 01:00:00.000000000 +0100
+++ trunk//repoze.what.plugins.sql.egg-info/top_level.txt 2011-01-11 20:48:19.000000000 +0100
@@ -0,0 +1 @@
+repoze
diff -Naur -x '*svn' -x coverage official//tests/databasesetup_properties.py trunk//tests/databasesetup_properties.py
--- official//tests/databasesetup_properties.py 1970-01-01 01:00:00.000000000 +0100
+++ trunk//tests/databasesetup_properties.py 2011-01-11 22:39:39.000000000 +0100
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2008-2009, Gustavo Narea <m...@gustavonarea.net>.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt. A copy of the license 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.
+#
+##############################################################################
+
+"""Stuff required to setup the test database."""
+
+import os
+
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from cgi import FieldStorage
+
+from fixture.model_properties import init_model, DBSession, metadata, Permission, \
+ Group, User
+
+engine = create_engine(os.environ.get('DBURL', 'sqlite://'))
+
+def setup_database():
+ init_model(engine)
+ teardownDatabase()
+ metadata.create_all(engine)
+
+ # Creating permissions
+
+ see_site = Permission()
+ see_site.permission_name = u'see-site'
+ DBSession.add(see_site)
+
+ edit_site = Permission()
+ edit_site.permission_name = u'edit-site'
+ DBSession.add(edit_site)
+
+ commit = Permission()
+ commit.permission_name = u'commit'
+ DBSession.add(commit)
+
+ # Creating groups
+
+ admins = Group(u'admins')
+ admins.all_permissions.append(edit_site)
+ DBSession.add(admins)
+
+ developers = Group(u'developers')
+ developers.all_permissions = [commit, edit_site]
+ DBSession.add(developers)
+
+ trolls = Group(u'trolls')
+ trolls.all_permissions.append(see_site)
+ DBSession.add(trolls)
+
+ # Plus a couple of groups with no permissions
+ php = Group(u'php')
+ DBSession.add(php)
+
+ python = Group(u'python')
+ DBSession.add(python)
+
+ # Creating users
+
+ user = User()
+ user.user_name = u'rms'
+ user.password = u'freedom'
+ user.all_groups.append(admins)
+ user.all_groups.append(developers)
+ DBSession.add(user)
+
+ user = User()
+ user.user_name = u'linus'
+ user.password = u'linux'
+ user.all_groups.append(developers)
+ DBSession.add(user)
+
+ user = User()
+ user.user_name = u'sballmer'
+ user.password = u'developers'
+ user.all_groups.append(trolls)
+ DBSession.add(user)
+
+ # Plus a couple of users without groups
+ user = User()
+ user.user_name = u'guido'
+ user.password = u'phytonic'
+ DBSession.add(user)
+
+ user = User()
+ user.user_name = u'rasmus'
+ user.password = u'php'
+ DBSession.add(user)
+
+ DBSession.commit()
+
+
+def teardownDatabase():
+ DBSession.rollback()
+ metadata.drop_all(engine)
+
diff -Naur -x '*svn' -x coverage official//tests/fixture/model_properties.py trunk//tests/fixture/model_properties.py
--- official//tests/fixture/model_properties.py 1970-01-01 01:00:00.000000000 +0100
+++ trunk//tests/fixture/model_properties.py 2011-01-11 23:26:43.000000000 +0100
@@ -0,0 +1,198 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2008-2009, Gustavo Narea <m...@gustavonarea.net>.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt. A copy of the license 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.
+#
+##############################################################################
+
+"""Mock SQLAlchemy-powered model definition."""
+
+from hashlib import sha1
+
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.types import Unicode, Integer
+from sqlalchemy.orm import scoped_session, sessionmaker, relation, synonym
+
+
+DBSession = scoped_session(sessionmaker(autoflush=True, autocommit=False))
+
+DeclarativeBase = declarative_base()
+
+metadata = DeclarativeBase.metadata
+
+def init_model(engine):
+ """Call me before using any of the tables or classes in the model."""
+ DBSession.configure(bind=engine)
+
+# This is the association table for the many-to-many relationship between
+# groups and permissions.
+group_permission_table = Table('tg_group_permission', metadata,
+ Column('group_id', Integer, ForeignKey('tg_group.group_id',
+ onupdate="CASCADE", ondelete="CASCADE")),
+ Column('permission_id', Integer, ForeignKey('tg_permission.permission_id',
+ onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+# This is the association table for the many-to-many relationship between
+# groups and members - this is, the memberships.
+user_group_table = Table('tg_user_group', metadata,
+ Column('user_id', Integer, ForeignKey('tg_user.user_id',
+ onupdate="CASCADE", ondelete="CASCADE")),
+ Column('group_id', Integer, ForeignKey('tg_group.group_id',
+ onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+# auth model
+
+class Group(DeclarativeBase):
+ """An ultra-simple group definition.
+ """
+ __tablename__ = 'tg_group'
+
+ group_id = Column(Integer, autoincrement=True, primary_key=True)
+
+ group_name = Column(Unicode(16), unique=True)
+
+ users = relation('User', secondary=user_group_table, backref='all_groups')
+
+ def __init__(self, group_name=None, display_name=u''):
+ if group_name is not None:
+ self.group_name = group_name
+ self.display_name = display_name
+
+ @property
+ def permissions(self):
+ """ we prefer to compute the permissions dynamically rather than a simple relation
+ the computing itself doesnt matter for test, in real life the relation
+ might be time triggered
+ (group 'admin' can only manage at night)
+
+ this behaviour is silly but is repoze.what.testutil compatible
+ """
+ if self.group_name in set((u'admins',)):
+ return [DBSession.query(Permission).filter_by(permission_name=u'edit-site').one(),]
+ elif self.group_name in set((u'trolls',)):
+ return [DBSession.query(Permission).filter_by(permission_name=u'see-site').one(),]
+ elif self.group_name in set((u'developers',)):
+ return [DBSession.query(Permission).filter_by(permission_name=u'edit-site').one(),
+ DBSession.query(Permission).filter_by(permission_name=u'commit').one(),
+ ]
+ else:
+ return set([])
+
+ # this schema is hard coded in repoze.what testutil
+# self.all_sections = {
+# u'see-site': set((u'trolls', )),
+# u'edit-site': set((u'admins', u'developers')),
+# u'commit': set((u'developers', ))
+# }
+
+
+ def __repr__(self):
+ return '<Group: name=%s>' % self.group_name
+
+
+class User(DeclarativeBase):
+ """Reasonably basic User definition. Probably would want additional
+ attributes.
+ """
+ __tablename__ = 'tg_user'
+
+ user_id = Column(Integer, autoincrement=True, primary_key=True)
+
+ user_name = Column(Unicode(16), unique=True)
+
+ _password = Column('password', Unicode(40))
+
+ @property
+ def groups(self):
+ """ we prefer to compute the groups dynamically rather than a simple relation
+ the computing itself doesnt matter for test, in real life the relation
+ might be enriched with an Association proxy
+ (user1 is 'manager' of group1, 'auditor' of group2)
+ """
+ if self.user_name in set((u'sballmer',)):
+ return [DBSession.query(Group).filter_by(group_name=u'trolls').one(),]
+ elif self.user_name in set((u'linus',)):
+ return [DBSession.query(Group).filter_by(group_name=u'developers').one(),]
+ elif self.user_name in set((u'rms',)):
+ return [DBSession.query(Group).filter_by(group_name=u'admins').one(),
+ DBSession.query(Group).filter_by(group_name=u'developers').one(),
+ ]
+ else:
+ return set([])
+
+# this schema is imposed (hard coded as a result) by the test infrastructure
+# self.all_sections = {
+# u'admins': set((u'rms', )),
+# u'developers': set((u'rms', u'linus')),
+# u'trolls': set((u'sballmer', )),
+# u'python': set(),
+# u'php': set()
+# }
+
+
+ def _set_password(self, password):
+ """encrypts password on the fly."""
+ self._password = self.__encrypt_password(password)
+
+ def _get_password(self):
+ """returns password"""
+ return self._password
+
+ password = synonym('password', descriptor=property(_get_password,
+ _set_password))
+
+ def __encrypt_password(self, password):
+ """Hash the given password with SHA1."""
+
+ if isinstance(password, unicode):
+ password_8bit = password.encode('UTF-8')
+
+ else:
+ password_8bit = password
+
+ hashed_password = sha1()
+ hashed_password.update(password_8bit)
+ hashed_password = hashed_password.hexdigest()
+
+ # make sure the hased password is an UTF-8 object at the end of the
+ # process because SQLAlchemy _wants_ a unicode object for Unicode columns
+ if not isinstance(hashed_password, unicode):
+ hashed_password = hashed_password.decode('UTF-8')
+
+ return hashed_password
+
+ def validate_password(self, password):
+ """Check the password against existing credentials.
+ this method _MUST_ return a boolean.
+
+ @param password: the password that was provided by the user to
+ try and authenticate. This is the clear text version that we will
+ need to match against the (possibly) encrypted one in the database.
+ @type password: unicode object
+ """
+ return self.password == self.__encrypt_password(password)
+
+
+class Permission(DeclarativeBase):
+ """A relationship that determines what each Group can do
+ """
+ __tablename__ = 'tg_permission'
+
+ permission_id = Column(Integer, autoincrement=True, primary_key=True)
+
+ permission_name = Column(Unicode(16), unique=True)
+
+ groups = relation(Group, secondary=group_permission_table,
+ backref='all_permissions')
+
diff -Naur -x '*svn' -x coverage official//tests/test_adapters.py trunk//tests/test_adapters.py
--- official//tests/test_adapters.py 2011-01-11 23:27:49.000000000 +0100
+++ trunk//tests/test_adapters.py 2011-01-11 23:23:50.000000000 +0100
@@ -23,10 +23,14 @@
PermissionsAdapterTester
import databasesetup
+import fixture.model
+
import databasesetup_translations
-from fixture.model import User, Group, Permission, DBSession
from fixture.model_translations import Member, Team, Right, DBSession
+import databasesetup_properties
+import fixture.model_properties
+
class _BaseSqlAdapterTester(unittest.TestCase):
"""Base class for the test suite of the SQL source adapters"""
@@ -44,6 +48,7 @@
self.adapter = SqlGroupsAdapter(databasesetup.Group,
databasesetup.User,
databasesetup.DBSession)
+ self.new_items = set((u'guido', u'rasmus'))
class TestSqlPermissionsAdapter(PermissionsAdapterTester,
@@ -56,7 +61,7 @@
self.adapter = SqlPermissionsAdapter(databasesetup.Permission,
databasesetup.Group,
databasesetup.DBSession)
-
+ self.new_items = set((u'python', u'php'))
class TestSqlGroupsAdapterWithTranslations(GroupsAdapterTester,
_BaseSqlAdapterTester):
@@ -75,6 +80,7 @@
'section_name': 'team_name',
'sections': 'teams'}
self.adapter.translations.update(translations)
+ self.new_items = set((u'guido', u'rasmus'))
class TestSqlPermissionsAdapterWithTranslations(PermissionsAdapterTester,
@@ -94,23 +100,84 @@
'section_name': 'right_name',
'sections': 'rights'}
self.adapter.translations.update(translations)
+ self.new_items = set((u'python', u'php'))
+
+############################################################################# NIL
+
+class TestSqlGroupsAdapterWithProperties(GroupsAdapterTester,
+ _BaseSqlAdapterTester):
+ """Test suite for the SQL group source adapter with properties"""
+
+ def setUp(self):
+ super(TestSqlGroupsAdapterWithProperties, self).setUp()
+ databasesetup_properties.setup_database()
+ self.adapter = SqlGroupsAdapter(databasesetup_properties.Group,
+ databasesetup_properties.User,
+ databasesetup_properties.DBSession)
+
+# repoze.what.adapters.testutils L 161
+# new_items = set((u'guido', u'rasmus'))
+# should really be in the setUp otherwise new_items is consumed silently (2 tests only)
+# and when we introduce the third test everything fails with
+#======================================================================
+#ERROR: test_checking_excluded_item_inclusion (tests.test_adapters.TestSqlGroupsAdapterWithTranslations)
+#----------------------------------------------------------------------
+#Traceback (most recent call last):
+# File ".../lib/python2.6/site-packages/repoze.what-1.0.9-py2.6.egg/repoze/what/adapters/testutil.py", line 66, in test_checking_excluded_item_inclusion
+# excluded_item = self.new_items.pop()
+#KeyError: 'pop from an empty set'
+
+ self.new_items = set((u'guido', u'rasmus'))
+
+ self.all_sections = {
+ u'admins': set((u'rms', )),
+ u'developers': set((u'rms', u'linus')),
+ u'trolls': set((u'sballmer', )),
+ u'python': set(),
+ u'php': set()
+ }
+
+
+class TestSqlPermissionsAdapterWithProperties(PermissionsAdapterTester,
+ _BaseSqlAdapterTester):
+ """Test suite for the SQL permission source adapter with translations"""
+
+ def setUp(self):
+ super(TestSqlPermissionsAdapterWithProperties, self).setUp()
+ databasesetup_properties.setup_database()
+ self.adapter = SqlPermissionsAdapter(databasesetup_properties.Permission,
+ databasesetup_properties.Group,
+ databasesetup_properties.DBSession)
+ # same remark as above
+ self.new_items = set((u'python', u'php'))
+
+
+
+ def test_retrieving_all_sections(self):
+# from nose.tools import set_trace; set_trace()
+ self.assertEqual(self.adapter._get_all_sections(), self.all_sections)
+
+
+
+############################################################################# /NIL
+
class TestAdaptersConfigurator(unittest.TestCase):
"""Tests for the L{configure_sql_adapters} utility"""
def test_without_translations(self):
- adapters = configure_sql_adapters(User, Group, Permission, DBSession)
+ adapters = configure_sql_adapters(fixture.model.User, fixture.model.Group, fixture.model.Permission, fixture.model.DBSession)
group_adapter = adapters['group']
permission_adapter = adapters['permission']
assert isinstance(group_adapter, SqlGroupsAdapter)
assert isinstance(permission_adapter, SqlPermissionsAdapter)
- self.assertEquals(DBSession, group_adapter.dbsession,
+ self.assertEquals(fixture.model.DBSession, group_adapter.dbsession,
permission_adapter.dbsession)
- self.assertEqual(User, group_adapter.children_class)
- self.assertEquals(Group, group_adapter.parent_class,
+ self.assertEqual(fixture.model.User, group_adapter.children_class)
+ self.assertEquals(fixture.model.Group, group_adapter.parent_class,
permission_adapter.children_class)
- self.assertEqual(Permission, permission_adapter.parent_class)
+ self.assertEqual(fixture.model.Permission, permission_adapter.parent_class)
def test_with_translations(self):
group_translations = {
@@ -119,7 +186,7 @@
permission_translations = {
'section_name': 'authorization_name',
'sections': 'authorizations'}
- adapters = configure_sql_adapters(User, Group, Permission, DBSession,
+ adapters = configure_sql_adapters(fixture.model.User, fixture.model.Group, fixture.model.Permission, fixture.model.DBSession,
group_translations,
permission_translations)
group_adapter = adapters['group']
_______________________________________________
Repoze-dev mailing list
Repoze-dev@lists.repoze.org
http://lists.repoze.org/listinfo/repoze-dev