Commit 7033e2bf authored by Ivan Tyagov's avatar Ivan Tyagov

Move test for security uid optimization into a separate file as it requires to have

a new bt5 (erp5_security_uid_innodb_catalog) upon installation.
Add all generic ZSQL methods to this new bt5
so projects can adjust only the minimum required.
parent 6fbe87df
<catalog_method>
<item key="sql_catalog_object_list" type="int">
<value>1</value>
</item>
</catalog_method>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>optimised_roles_and_users</string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>class_file_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>class_name_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_hook</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z_catalog_roles_and_users_list</string> </value>
</item>
<item>
<key> <string>max_cache_</string> </key>
<value> <int>100</int> </value>
</item>
<item>
<key> <string>max_rows_</string> </key>
<value> <int>1000</int> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
<dtml-let row_list="[]">\n
<dtml-in prefix="loop" expr="_.range(_.len(optimised_roles_and_users))">\n
<dtml-in prefix="role" expr="optimised_roles_and_users[loop_item]">\n
<dtml-call expr="row_list.append([role_item[0], role_item[1], role_item[2]])">\n
</dtml-in>\n
</dtml-in>\n
<dtml-if expr="row_list">\n
INSERT INTO\n
roles_and_users(uid, local_roles_group_id, allowedRolesAndUsers)\n
VALUES\n
<dtml-in prefix="row" expr="row_list">\n
(<dtml-sqlvar expr="row_item[0]" type="string">, <dtml-sqlvar expr="row_item[1]" type="string">, <dtml-sqlvar expr="row_item[2]" type="string">)\n
<dtml-if sequence-end><dtml-else>,</dtml-if>\n
</dtml-in>\n
</dtml-if>\n
</dtml-let>\n
]]></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<catalog_method>
<item key="sql_clear_catalog" type="int">
<value>1</value>
</item>
</catalog_method>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_col</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>class_file_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>class_name_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_hook</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z_create_roles_and_users</string> </value>
</item>
<item>
<key> <string>max_cache_</string> </key>
<value> <int>100</int> </value>
</item>
<item>
<key> <string>max_rows_</string> </key>
<value> <int>1000</int> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string>CREATE TABLE roles_and_users (\n
uid INT UNSIGNED,\n
local_roles_group_id VARCHAR(255) default \'\',\n
allowedRolesAndUsers VARCHAR(255),\n
KEY `uid` (`uid`),\n
KEY `allowedRolesAndUsers` (`allowedRolesAndUsers`),\n
KEY `local_roles_group_id` (`local_roles_group_id`)\n
) ENGINE=InnoDB;\n
</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<catalog_method>
<item key="sql_search_security" type="int">
<value>1</value>
</item>
</catalog_method>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>security_roles_list</string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>class_file_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>class_name_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_hook</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z_search_security</string> </value>
</item>
<item>
<key> <string>max_cache_</string> </key>
<value> <int>100</int> </value>
</item>
<item>
<key> <string>max_rows_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
SELECT\n
DISTINCT uid, local_roles_group_id\n
FROM \n
roles_and_users\n
WHERE \n
allowedRolesAndUsers \n
IN (<dtml-in security_roles_list><dtml-var sequence-item><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>)\n
]]></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
2012-07-12 Ivan
* Initial import
\ No newline at end of file
This bt5 provides generic Z SQL methods that allows a site developer to split security uids into different catalog columns.
In order to use this optimization it's required to:
1. Make analyze which are most common uids
2. Place them in a new catalog column
3. Adjust respective z_catalog_object_list and z_create_catalog
Note: In order to use this technique a full catalog re-index is required.
\ No newline at end of file
erp5_mysql_innodb/z_catalog_roles_and_users_list
erp5_mysql_innodb/z_create_roles_and_users
erp5_mysql_innodb/z_search_security
\ No newline at end of file
erp5_security_uid_innodb_catalog
\ No newline at end of file
...@@ -4426,133 +4426,6 @@ VALUES ...@@ -4426,133 +4426,6 @@ VALUES
self.assertEqual([x.getObject() for x in catalog.searchResults(**query_lj)], self.assertEqual([x.getObject() for x in catalog.searchResults(**query_lj)],
[org_a.default_address]) [org_a.default_address])
def test_local_roles_group_id_on_role_information(self):
"""Test usage of local_roles_group_id when searching catalog.
"""
sql_connection = self.getSQLConnection()
sql_catalog = self.portal.portal_catalog.getSQLCatalog()
# Add a catalog table (uid, alternate_security_uid)
sql_connection.manage_test(
"""DROP TABLE IF EXISTS alternate_roles_and_users""")
sql_connection.manage_test("""
CREATE TABLE alternate_roles_and_users (
`uid` BIGINT UNSIGNED NOT NULL,
`alternate_security_uid` INT UNSIGNED) """)
# make it a search table
current_sql_search_tables = sql_catalog.sql_search_tables
sql_catalog.sql_search_tables = sql_catalog.sql_search_tables + [
'alternate_roles_and_users']
# Configure sql method to insert this table
sql_catalog.manage_addProduct['ZSQLMethods'].manage_addZSQLMethod(
id='z_catalog_alternate_roles_and_users_list',
title='',
connection_id='erp5_sql_connection',
arguments="\n".join(['uid', 'alternate_security_uid']),
template="""REPLACE INTO alternate_roles_and_users VALUES
<dtml-in prefix="loop" expr="_.range(_.len(uid))">
( <dtml-sqlvar expr="uid[loop_item]" type="int">,
<dtml-sqlvar expr="alternate_security_uid[loop_item]" type="int" optional>
)<dtml-unless sequence-end>,</dtml-unless>
</dtml-in>""")
current_sql_catalog_object_list = sql_catalog.sql_catalog_object_list
sql_catalog.sql_catalog_object_list = \
current_sql_catalog_object_list + \
('z_catalog_alternate_roles_and_users_list',)
# configure Alternate local roles group id to go in alternate_security_uid
current_sql_catalog_security_uid_columns =\
sql_catalog.sql_catalog_security_uid_columns
sql_catalog.sql_catalog_security_uid_columns = (
' | security_uid',
'Alternate | alternate_security_uid', )
# configure security on person, each user will be able to see his own
# person thanks to an Auditor role on "Alternate" local roles group id.
self.portal.portal_types.Person.newContent(
portal_type='Role Information',
role_name='Auditor',
role_base_category_script_id='ERP5Type_getSecurityCategoryFromSelf',
role_base_category='agent',
local_roles_group_id='Alternate')
self.portal.portal_caches.clearAllCache()
self.tic()
try:
# create two persons and users
user1 = self.portal.person_module.newContent(portal_type='Person',
reference='user1')
user1.newContent(portal_type='Assignment').open()
user1.updateLocalRolesOnSecurityGroups()
self.assertEquals(user1.__ac_local_roles__.get('user1'), ['Auditor'])
user2 = self.portal.person_module.newContent(portal_type='Person',
reference='user2')
user2.newContent(portal_type='Assignment').open()
user2.updateLocalRolesOnSecurityGroups()
self.assertEquals(user2.__ac_local_roles__.get('user2'), ['Auditor'])
self.tic()
# security_uid_dict in catalog contains entries for user1 and user2:
user1_alternate_security_uid = sql_catalog.security_uid_dict[
('Alternate', ('user:user1', 'user:user1:Auditor'))]
bob_alternate_security_uid = sql_catalog.security_uid_dict[
('Alternate', ('user:user2', 'user:user2:Auditor'))]
# those entries are in alternate security table
alternate_roles_and_users = sql_connection.manage_test(
"SELECT * from alternate_roles_and_users").dictionaries()
self.assertTrue(dict(uid=user1.getUid(),
alternate_security_uid=user1_alternate_security_uid) in
alternate_roles_and_users)
self.assertTrue(dict(uid=user2.getUid(),
alternate_security_uid=bob_alternate_security_uid) in
alternate_roles_and_users)
# low level check of the security query of a logged in user
self.login('user1')
security_query = self.portal.portal_catalog.getSecurityQuery()
# This query is a complex query wrapping another complex query with a
# criterion on altenate_security_uid. This check is quite low level and
# is subject to change.
security_uid_query = security_query.query_list[0]
alternate_security_query, = [q for q in
security_query.query_list[0].query_list if
q.kw.get('alternate_security_uid')]
self.assertEquals([user1_alternate_security_uid],
alternate_security_query.kw['alternate_security_uid'])
# high level check that that logged in user can see document
self.assertEquals([user1],
[o.getObject() for o in self.portal.portal_catalog(portal_type='Person')])
# also with local_roles= argument which is used in worklists
self.assertEquals([user1],
[o.getObject() for o in self.portal.portal_catalog(portal_type='Person',
local_roles='Auditor')])
# searches still work for other users
self.login('ERP5TypeTestCase')
self.assertSameSet([user1, user2],
[o.getObject() for o in
self.portal.portal_catalog(portal_type='Person')])
finally:
# restore catalog configuration
sql_catalog.sql_search_tables = current_sql_search_tables
sql_catalog.sql_catalog_object_list = current_sql_catalog_object_list
sql_catalog.sql_catalog_security_uid_columns =\
current_sql_catalog_security_uid_columns
self.portal.portal_types.Person.manage_delObjects(
[role.getId() for role in
self.portal.portal_types.Person.contentValues(
portal_type='Role Information')])
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestERP5Catalog)) suite.addTest(unittest.makeSuite(TestERP5Catalog))
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Ivan Tyagov <ivan@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import unittest
from Testing import ZopeTestCase
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.backportUnittest import expectedFailure
from AccessControl.SecurityManagement import newSecurityManager
class TestERP5CatalogSecurityUidOptimization(ERP5TypeTestCase):
"""
TestERP5CatalogSecurityUidOptimization tests security_uid optmization.
It is in a different test than TestERP5Catalog as it requires erp5_security_uid_innodb_catalog
bt5 to be installed in advance.
XXX: Inherit from TestERP5Catalog so we test default and security_uid optmization with same tests.
"""
business_template_list = ['erp5_security_uid_innodb_catalog',
'erp5_full_text_myisam_catalog','erp5_base']
def getBusinessTemplateList(self):
return self.business_template_list
def afterSetUp(self):
self.login()
portal = self.getPortal()
def test_local_roles_group_id_on_role_information(self):
"""Test usage of local_roles_group_id when searching catalog.
"""
sql_connection = self.getSQLConnection()
sql_catalog = self.portal.portal_catalog.getSQLCatalog()
# Add a catalog table (uid, alternate_security_uid)
sql_connection.manage_test(
"""DROP TABLE IF EXISTS alternate_roles_and_users""")
sql_connection.manage_test("""
CREATE TABLE alternate_roles_and_users (
`uid` BIGINT UNSIGNED NOT NULL,
`alternate_security_uid` INT UNSIGNED) """)
# make it a search table
current_sql_search_tables = sql_catalog.sql_search_tables
sql_catalog.sql_search_tables = sql_catalog.sql_search_tables + [
'alternate_roles_and_users']
# Configure sql method to insert this table
sql_catalog.manage_addProduct['ZSQLMethods'].manage_addZSQLMethod(
id='z_catalog_alternate_roles_and_users_list',
title='',
connection_id='erp5_sql_connection',
arguments="\n".join(['uid', 'alternate_security_uid']),
template="""REPLACE INTO alternate_roles_and_users VALUES
<dtml-in prefix="loop" expr="_.range(_.len(uid))">
( <dtml-sqlvar expr="uid[loop_item]" type="int">,
<dtml-sqlvar expr="alternate_security_uid[loop_item]" type="int" optional>
)<dtml-unless sequence-end>,</dtml-unless>
</dtml-in>""")
current_sql_catalog_object_list = sql_catalog.sql_catalog_object_list
sql_catalog.sql_catalog_object_list = \
current_sql_catalog_object_list + \
('z_catalog_alternate_roles_and_users_list',)
# configure Alternate local roles group id to go in alternate_security_uid
current_sql_catalog_security_uid_columns =\
sql_catalog.sql_catalog_security_uid_columns
sql_catalog.sql_catalog_security_uid_columns = (
' | security_uid',
'Alternate | alternate_security_uid', )
# configure security on person, each user will be able to see his own
# person thanks to an Auditor role on "Alternate" local roles group id.
self.portal.portal_types.Person.newContent(
portal_type='Role Information',
role_name='Auditor',
role_base_category_script_id='ERP5Type_getSecurityCategoryFromSelf',
role_base_category='agent',
local_roles_group_id='Alternate')
self.portal.portal_caches.clearAllCache()
self.tic()
try:
# create two persons and users
user1 = self.portal.person_module.newContent(portal_type='Person',
reference='user1')
user1.newContent(portal_type='Assignment').open()
user1.updateLocalRolesOnSecurityGroups()
self.assertEquals(user1.__ac_local_roles__.get('user1'), ['Auditor'])
user2 = self.portal.person_module.newContent(portal_type='Person',
reference='user2')
user2.newContent(portal_type='Assignment').open()
user2.updateLocalRolesOnSecurityGroups()
self.assertEquals(user2.__ac_local_roles__.get('user2'), ['Auditor'])
self.tic()
# security_uid_dict in catalog contains entries for user1 and user2:
user1_alternate_security_uid = sql_catalog.security_uid_dict[
('Alternate', ('user:user1', 'user:user1:Auditor'))]
bob_alternate_security_uid = sql_catalog.security_uid_dict[
('Alternate', ('user:user2', 'user:user2:Auditor'))]
# those entries are in alternate security table
alternate_roles_and_users = sql_connection.manage_test(
"SELECT * from alternate_roles_and_users").dictionaries()
self.assertTrue(dict(uid=user1.getUid(),
alternate_security_uid=user1_alternate_security_uid) in
alternate_roles_and_users)
self.assertTrue(dict(uid=user2.getUid(),
alternate_security_uid=bob_alternate_security_uid) in
alternate_roles_and_users)
# low level check of the security query of a logged in user
self.login('user1')
security_query = self.portal.portal_catalog.getSecurityQuery()
# This query is a complex query wrapping another complex query with a
# criterion on altenate_security_uid. This check is quite low level and
# is subject to change.
security_uid_query = security_query.query_list[0]
alternate_security_query, = [q for q in
security_query.query_list[0].query_list if
q.kw.get('alternate_security_uid')]
self.assertEquals([user1_alternate_security_uid],
alternate_security_query.kw['alternate_security_uid'])
# high level check that that logged in user can see document
self.assertEquals([user1],
[o.getObject() for o in self.portal.portal_catalog(portal_type='Person')])
# also with local_roles= argument which is used in worklists
self.assertEquals([user1],
[o.getObject() for o in self.portal.portal_catalog(portal_type='Person',
local_roles='Auditor')])
# searches still work for other users
self.login('ERP5TypeTestCase')
self.assertSameSet([user1, user2],
[o.getObject() for o in
self.portal.portal_catalog(portal_type='Person')])
finally:
# restore catalog configuration
sql_catalog.sql_search_tables = current_sql_search_tables
sql_catalog.sql_catalog_object_list = current_sql_catalog_object_list
sql_catalog.sql_catalog_security_uid_columns =\
current_sql_catalog_security_uid_columns
self.portal.portal_types.Person.manage_delObjects(
[role.getId() for role in
self.portal.portal_types.Person.contentValues(
portal_type='Role Information')])
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestERP5CatalogSecurityUidOptimization))
return suite
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment