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

Reply via email to