Commit 5272e987 authored by Ivan Tyagov's avatar Ivan Tyagov

Merge branch 'security_groups'

parents 90fb36be 88e9c81e
......@@ -31,8 +31,8 @@
DELETE FROM roles_and_users\n
<dtml-var sql_delimiter>\n
INSERT INTO roles_and_users (uid, allowedRolesAndUsers) VALUES\n
<dtml-in expr="getPortalObject().portal_catalog.getSQLCatalog().getRoleAndSecurityUidList()">\n
(<dtml-sqlvar sequence-item type="int">,<dtml-sqlvar sequence-key type="string">)<dtml-if sequence-end><dtml-else>,</dtml-if>\n
<dtml-in expr="role" expr="getPortalObject().portal_catalog.getSQLCatalog().getRoleAndSecurityUidList()">\n
(<dtml-sqlvar expr="role_item[0]" type="int">, <dtml-sqlvar expr="role_item[1]" type="string">, <dtml-sqlvar expr="role_item[2]" type="string">)<dtml-if sequence-end><dtml-else>,</dtml-if>\n
\n
</dtml-in>
......
<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
......@@ -545,7 +545,7 @@ class BaseTemplateItem(Implicit, Persistent):
classname = klass.__name__
attr_set = set(('_dav_writelocks', '_filepath', '_owner', 'last_id', 'uid',
'__ac_local_roles__'))
'__ac_local_roles__', '__ac_local_roles_group_id_dict__'))
if export:
if not keep_workflow_history:
attr_set.add('workflow_history')
......@@ -3047,11 +3047,12 @@ class PortalTypeRolesTemplateItem(BaseTemplateItem):
k = k[5:]
elif k == 'role_name':
k, v = 'id', '; '.join(v)
elif k not in ('title', 'description'):
elif k not in ('title', 'description', 'categories'):
k = {'id': 'object_id', # for stable sort
'role_base_category': 'base_category',
'role_base_category_script_id': 'base_category_script',
'role_category': 'category'}.get(k)
'role_category': 'category',
'local_roles_group_id': 'local_roles_group_id'}.get(k)
if not k:
continue
type_role_dict[k] = v
......@@ -3074,7 +3075,7 @@ class PortalTypeRolesTemplateItem(BaseTemplateItem):
xml_data += "\n <property id='%s'>%s</property>" % \
(property, prop_value)
# multi
for property in ('category', 'base_category'):
for property in ('categories', 'category', 'base_category'):
for prop_value in role.get(property, []):
if isinstance(prop_value, str):
prop_value = prop_value.decode('utf-8')
......@@ -3161,7 +3162,7 @@ class PortalTypeRolesTemplateItem(BaseTemplateItem):
path = 'portal_types/%s' % roles_path.split('/', 1)[1]
try:
obj = p.unrestrictedTraverse(path)
setattr(obj, '_roles', [])
obj.manage_delObjects([x.id for x in obj.getRoleInformationList()])
except (NotFound, KeyError):
pass
......@@ -4360,6 +4361,12 @@ class CatalogLocalRoleKeyTemplateItem(CatalogUniqueKeyTemplateItemBase):
key_list_title = 'local_role_key_list'
key_title = 'LocalRole key'
class CatalogSecurityUidColumnTemplateItem(CatalogSearchKeyTemplateItem):
key_list_attr = 'sql_catalog_security_uid_columns'
key_list_title = 'security_uid_column_list'
key_title = 'Security Uid Columns'
class MessageTranslationTemplateItem(BaseTemplateItem):
def build(self, context, **kw):
......@@ -4532,13 +4539,27 @@ class LocalRolesTemplateItem(BaseTemplateItem):
obj = p.unrestrictedTraverse(path.split('/', 1)[1])
local_roles_dict = getattr(obj, '__ac_local_roles__',
{}) or {}
self._objects[path] = (local_roles_dict, )
local_roles_group_id_dict = getattr(
obj, '__ac_local_roles_group_id_dict__', {}) or {}
self._objects[path] = (local_roles_dict, local_roles_group_id_dict)
# Function to generate XML Code Manually
def generateXml(self, path=None):
local_roles_dict = self._objects[path][0]
# local roles
# With local roles groups id, self._object contains for each path a tuple
# containing the dict of local roles and the dict of local roles group ids.
# Before it was only containing the dict of local roles. This method is
# also used on installed business templates to show a diff during
# installation, so it might be called on old format objects.
if len(self._objects[path]) == 2:
# new format
local_roles_dict, local_roles_group_id_dict = self._objects[path]
else:
# old format, before local roles group id
local_roles_group_id_dict = dict()
local_roles_dict, = self._objects[path]
xml_data = '<local_roles_item>'
# local roles
xml_data += '\n <local_roles>'
for key in sorted(local_roles_dict):
xml_data += "\n <role id='%s'>" %(key,)
......@@ -4547,6 +4568,20 @@ class LocalRolesTemplateItem(BaseTemplateItem):
xml_data += "\n <item>%s</item>" %(item,)
xml_data += '\n </role>'
xml_data += '\n </local_roles>'
if local_roles_group_id_dict:
# local roles group id dict (not included by default to be stable with
# old bts)
xml_data += '\n <local_roles_group_id>'
for principal, local_roles_group_id_list in sorted(local_roles_group_id_dict.items()):
xml_data += "\n <principal id='%s'>" % escape(principal)
for local_roles_group_id in local_roles_group_id_list:
for item in local_roles_group_id:
xml_data += "\n <local_roles_group_id>%s</local_roles_group_id>" % \
escape(item)
xml_data += "\n </principal>"
xml_data += '\n </local_roles_group_id>'
xml_data += '\n</local_roles_item>'
if isinstance(xml_data, unicode):
xml_data = xml_data.encode('utf8')
......@@ -4571,7 +4606,15 @@ class LocalRolesTemplateItem(BaseTemplateItem):
id = role.get('id')
item_type_list = [item.text for item in role]
local_roles_dict[id] = item_type_list
self._objects['local_roles/%s' % (file_name[:-4],)] = (local_roles_dict, )
# local roles group id
local_roles_group_id_dict = {}
for principal in xml.findall('//principal'):
local_roles_group_id_dict[principal.get('id')] = set([tuple(
[group_id.text for group_id in
principal.findall('./local_roles_group_id')])])
self._objects['local_roles/%s' % (file_name[:-4],)] = (
local_roles_dict, local_roles_group_id_dict)
def install(self, context, trashbin, **kw):
update_dict = kw.get('object_to_update')
......@@ -4585,8 +4628,22 @@ class LocalRolesTemplateItem(BaseTemplateItem):
continue
path = roles_path.split('/')[1:]
obj = p.unrestrictedTraverse(path)
local_roles_dict = self._objects[roles_path][0]
# again we might be installing an business template in format before
# existance of local roles group id.
if len(self._objects[roles_path]) == 2:
local_roles_dict, local_roles_group_id_dict = self._objects[roles_path]
else:
local_roles_group_id_dict = dict()
local_roles_dict, = self._objects[roles_path]
setattr(obj, '__ac_local_roles__', local_roles_dict)
if local_roles_group_id_dict:
setattr(obj, '__ac_local_roles_group_id_dict__',
local_roles_group_id_dict)
# we try to have __ac_local_roles_group_id_dict__ set only if
# it is actually defining something else than default
elif getattr(aq_base(obj), '__ac_local_roles_group_id_dict__',
None) is not None:
delattr(obj, '__ac_local_roles_group_id_dict__')
obj.reindexObject()
def uninstall(self, context, object_path=None, **kw):
......@@ -4748,6 +4805,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
'_catalog_scriptable_key_item',
'_catalog_role_key_item',
'_catalog_local_role_key_item',
'_catalog_security_uid_column_item',
]
def __init__(self, *args, **kw):
......@@ -4910,6 +4968,14 @@ Business Template is a set of definitions, such as skins, portal types and categ
self._catalog_local_role_key_item = \
CatalogLocalRoleKeyTemplateItem(
self.getTemplateCatalogLocalRoleKeyList())
try:
self._catalog_security_uid_column_item = \
CatalogSecurityUidColumnTemplateItem(
self.getTemplateCatalogSecurityUidColumnList())
except AttributeError:
# be backwards compatible with old zope instances which
# do not contain recent version of erp5_property_sheets
pass
security.declareProtected(Permissions.ManagePortal, 'build')
def build(self, no_action=0):
......@@ -5523,7 +5589,12 @@ Business Template is a set of definitions, such as skins, portal types and categ
(SimpleItem.SimpleItem,), {'__module__': module_id}))
for item_name in self._item_name_list:
getattr(self, item_name).importFile(bta)
item_object = getattr(self, item_name, None)
# this check is due to backwards compatability when there can be a
# difference between install erp5_property_sheets (esp. BusinessTemplate
# property sheet)
if item_object is not None:
item_object.importFile(bta)
# Remove temporary modules created above to allow import of real modules
# (during the installation).
......@@ -5631,6 +5702,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
'CatalogScriptableKey' : '_catalog_scriptable_key_item',
'CatalogRoleKey' : '_catalog_role_key_item',
'CatalogLocalRoleKey' : '_catalog_local_role_key_item',
'CatalogSecurityUidColumn' : '_catalog_security_uid_column_item',
}
object_id = REQUEST.object_id
......@@ -5693,6 +5765,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
'_catalog_scriptable_key_item',
'_catalog_role_key_item',
'_catalog_local_role_key_item',
'_catalog_security_uid_column_item',
'_portal_type_allowed_content_type_item',
'_portal_type_hidden_content_type_item',
'_portal_type_property_sheet_item',
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Category" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_folders_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Copy_or_Move_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>local_role_group</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Category</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Local Role Group</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
......@@ -91,6 +91,7 @@
<string>my_template_catalog_topic_key_list</string>
<string>my_template_catalog_scriptable_key_list</string>
<string>my_template_catalog_local_role_key_list</string>
<string>my_template_catalog_security_uid_column_list</string>
</list>
</value>
</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="LinesField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_template_catalog_security_uid_column_list</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>line_too_long</string> </key>
<value> <string>A line was too long.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>You entered too many characters.</string> </value>
</item>
<item>
<key> <string>too_many_lines</string> </key>
<value> <string>You entered too many lines.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>5</int> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Security Uid Columns</string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string encoding="cdata"><![CDATA[
<br />
]]></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>40</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -98,6 +98,7 @@
<string>my_title</string>
<string>my_role_name_list</string>
<string>my_condition</string>
<string>my_local_roles_group_id</string>
</list>
</value>
</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ListField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_local_roles_group_id</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>unknown_selection</string> </key>
<value> <string>You selected an item that was not in the list.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Local Roles Group</string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(x,x) for x in context.getPortalObject().portal_catalog.getSQLCatalog().getSQLCatalogSecurityUidGroupsColumnsDict().keys()]</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -16,6 +16,7 @@ destination_project
destination_section
destination_trade
elementary_type
local_role_group
mapping
order
origin
......
......@@ -14,8 +14,7 @@
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>security_uid\r\n
optimised_roles_and_users</string> </value>
<value> <string>optimised_roles_and_users</string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
......@@ -56,16 +55,14 @@ optimised_roles_and_users</string> </value>
<value> <string encoding="cdata"><![CDATA[
<dtml-let row_list="[]">\n
<dtml-in prefix="loop" expr="_.range(_.len(security_uid))">\n
<dtml-if expr="optimised_roles_and_users[loop_item]">\n
<dtml-in prefix="role" expr="optimised_roles_and_users[loop_item]">\n
<dtml-call expr="row_list.append([security_uid[loop_item], role_item])">\n
</dtml-in>\n
</dtml-if>\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[2]])">\n
</dtml-in>\n
</dtml-in>\n
<dtml-if expr="row_list">\n
INSERT INTO\n
roles_and_users\n
roles_and_users(uid, 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">)\n
......
......@@ -6,6 +6,12 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_col</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
......
......@@ -52,10 +52,10 @@
<key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
SELECT \n
DISTINCT uid \n
SELECT\n
DISTINCT uid\n
FROM \n
roles_and_users \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
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/lines</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>template_catalog_security_uid_column_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: ()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Category Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>local_role_group_category</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Category Property</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -6597,6 +6597,90 @@ class TestBusinessTemplate(BusinessTemplateMixin):
new_bt5_obj.build()
template_tool.export(new_bt5_obj)
def test_local_roles_group_id(self):
"""Tests that roles definition defining local roles group ids are properly
exported and installed.
"""
# change security uid columns
sql_catalog = self.portal.portal_catalog.getSQLCatalog()
saved_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',
)
# add category
self.portal.portal_categories.local_role_group.newContent(
portal_type='Category',
reference = 'Alternate',
id = 'Alternate')
types_tool = self.portal.portal_types
object_type = types_tool.newContent('Geek Object', 'Base Type',
type_class='Person')
types_tool.newContent('Geek Module', 'Base Type',
type_class='Folder',
type_filter_content_type=1,
type_allowed_content_type_list=('Geek Object',), )
self.portal.newContent(portal_type='Geek Module', id='geek_module')
new_object = self.portal.geek_module.newContent(
portal_type='Geek Object', id='1')
# simulate role assignment
new_object.__ac_local_roles__ = dict(group=['Assignee'])
initial___ac_local_roles_group_id_dict__ = dict(Alternate=set([('group', 'Assignee')]))
new_object.__ac_local_roles_group_id_dict__ = initial___ac_local_roles_group_id_dict__
self.tic()
object_type.newContent(portal_type='Role Information',
local_role_group_value=self.portal.portal_categories.local_role_group.Alternate.getRelativeUrl(),
role_name_list=('Assignee', ))
bt = self.portal.portal_templates.newContent(
portal_type='Business Template',
title=self.id(),
template_local_roles_list=('geek_module/1',),
template_path_list=('geek_module/1',),
template_portal_type_role_list=('Geek Object',),)
self.tic()
bt.build()
self.tic()
export_dir = tempfile.mkdtemp()
try:
bt.export(path=export_dir, local=True)
self.tic()
new_bt = self.portal.portal_templates.download(
url='file://%s' % export_dir)
finally:
shutil.rmtree(export_dir)
# uninstall role information and paths
object_type.manage_delObjects([x.id for x in object_type.getRoleInformationList()])
self.portal.geek_module.manage_delObjects(['1'])
self.tic()
new_bt.install()
try:
role, = object_type.getRoleInformationList()
self.assertEquals(self.portal.portal_categories.local_role_group.Alternate,
role.getLocalRoleGroupValue())
path = self.portal.geek_module['1']
self.assertEquals([('group', ['Assignee'],)], [item for item in
path.__ac_local_roles__.items() if item[1] != ['Owner']])
self.assertEquals(initial___ac_local_roles_group_id_dict__,
path.__ac_local_roles_group_id_dict__)
finally:
# restore state
sql_catalog.sql_catalog_security_uid_columns = \
saved_sql_catalog_security_uid_columns
types_tool.manage_delObjects(['Geek Object', 'Geek Module'])
self.portal.manage_delObjects(['geek_module'])
self.tic()
def test_BusinessTemplateWithTest(self):
sequence_list = SequenceList()
sequence_string = '\
......
......@@ -101,52 +101,82 @@ class IndexableObjectWrapper(object):
getSQLCatalogRoleKeysList())
getUserById = portal.acl_users.getUserById
allowed_dict = dict()
# For each local role of a user:
# If the local role grants View permission, add it.
# Every addition implies 2 lines:
# user:<user_id>
# user:<user_id>:<role_id>
# A line must not be present twice in final result.
allowed = set(rolesForPermissionOn('View', ob))
allowed_role_set = set(rolesForPermissionOn('View', ob))
# XXX the permission name is included by default for verbose
# logging of security errors, but the catalog does not need to
# index it. Unfortunately, rolesForPermissionOn does not have
# an option to disable this behavior at calling time, so
# discard it explicitly.
allowed.discard('_View_Permission')
allowed_role_set.discard('_View_Permission')
# XXX Owner is hardcoded, in order to prevent searching for user on the
# site root.
allowed.discard('Owner')
add = allowed.add
allowed_role_set.discard('Owner')
# XXX make this a method of base ?
local_roles_group_id_group_id = getattr(ob,
'__ac_local_roles_group_id_dict__', dict())
allowed_by_local_roles_group_id = {}
allowed_by_local_roles_group_id[''] = allowed_role_set
user_role_dict = {}
user_view_permission_role_dict = {}
optimized_role_set = set()
# First parse optimized roles and build optimized_role_set
for role_definition_group, user_and_role_list in local_roles_group_id_group_id.items():
try:
group_allowed_set = allowed_by_local_roles_group_id[role_definition_group]
except KeyError:
allowed_by_local_roles_group_id[role_definition_group] = group_allowed_set = set()
for user, role in user_and_role_list:
prefix = 'user:' + user
group_allowed_set.update((prefix, '%s:%s' % (prefix, role)))
optimized_role_set.add((user, role))
# Then parse other roles
for user, roles in localroles.iteritems():
prefix = 'user:' + user
for role in roles:
if (role in role_dict) and (getUserById(user) is not None):
# If role is monovalued, check if key is a user.
# If not, continue to index it in roles_and_users table.
user_role_dict[role] = user
if role in allowed:
if (user, role) not in optimized_role_set:
user_role_dict[role] = user # Only add to user_role_dict if not in optimized_role_set (double check)
if role in allowed_role_set:
user_view_permission_role_dict[role] = user
elif role in allowed:
add(prefix)
add(prefix + ':' + role)
self._cache_result = result = (sorted(allowed), user_role_dict,
elif role in allowed_role_set:
for group in local_roles_group_id_group_id.get(user, ('', )):
try:
group_allowed_set = allowed_by_local_roles_group_id[group]
except KeyError:
allowed_by_local_roles_group_id[group] = group_allowed_set = set()
if (user, role) not in optimized_role_set:
# add only if not already added to optimized_role_set to avoid polluting indexation table
group_allowed_set.update((prefix, '%s:%s' % (prefix, role)))
# sort `allowed` principals
sorted_allowed_by_local_roles_group_id = {}
for local_roles_group_id, allowed in \
allowed_by_local_roles_group_id.items():
sorted_allowed_by_local_roles_group_id[local_roles_group_id] = tuple(
sorted(allowed))
self._cache_result = result = (sorted_allowed_by_local_roles_group_id,
user_role_dict,
user_view_permission_role_dict)
return result
def allowedRolesAndUsers(self):
"""
Return a list of roles and users with View permission.
Used by Portal Catalog to filter out items you're not allowed to see.
WARNING (XXX): some user base local role association is currently
being stored (ex. to be determined). This should be prevented or it will
make the table explode. To analyse the symptoms, look at the
user_and_roles table. You will find some user:foo values
which are not necessary.
def getLocalRolesGroupIdDict(self):
"""Returns a mapping of local roles group id to roles and users with View
permission.
"""
return self._getSecurityParameterList()[0]
......@@ -456,13 +486,12 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
allowedRolesAndUsers = new_allowedRolesAndUsers
role_column_dict = new_role_column_dict
return allowedRolesAndUsers, role_column_dict, local_role_column_dict
def getSecurityUidListAndRoleColumnDict(self, sql_catalog_id=None, **kw):
def getSecurityUidDictAndRoleColumnDict(self, sql_catalog_id=None, **kw):
"""
Return a list of security Uids and a dictionnary containing available
role columns.
Return a dict of local_roles_group_id -> security Uids and a
dictionnary containing available role columns.
XXX: This method always uses default catalog. This should not break a
site as long as security uids are considered consistent among all
......@@ -477,30 +506,40 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
cache_key = tuple(allowedRolesAndUsers)
tv = getTransactionalVariable()
try:
security_uid_cache = tv['getSecurityUidListAndRoleColumnDict']
security_uid_cache = tv['getSecurityUidDictAndRoleColumnDict']
except KeyError:
security_uid_cache = tv['getSecurityUidListAndRoleColumnDict'] = {}
security_uid_cache = tv['getSecurityUidDictAndRoleColumnDict'] = {}
try:
security_uid_list = security_uid_cache[cache_key]
security_uid_dict = security_uid_cache[cache_key]
except KeyError:
if method is None:
warnings.warn("The usage of allowedRolesAndUsers is "\
"deprecated. Please update your catalog "\
"business template.", DeprecationWarning)
security_uid_list = [x.security_uid for x in \
security_uid_dict = {None: [x.security_uid for x in \
self.unrestrictedSearchResults(
allowedRolesAndUsers=allowedRolesAndUsers,
select_expression="security_uid",
group_by_expression="security_uid")]
group_by_expression="security_uid")] }
else:
# XXX: What with this string transformation ?! Souldn't it be done in
# dtml instead ?
# dtml instead ? ... yes, but how to be bw compatible ?
allowedRolesAndUsers = [sqlquote(role) for role in allowedRolesAndUsers]
security_uid_list = [x.uid for x in method(security_roles_list = allowedRolesAndUsers)]
security_uid_cache[cache_key] = security_uid_list
security_uid_dict = dict()
for brain in method(security_roles_list=allowedRolesAndUsers):
try:
local_roles_group_id = brain.local_roles_group_id
except AttributeError:
# backwards compatability in cases when catalog use default schema
local_roles_group_id = ''
security_uid_dict.setdefault(local_roles_group_id,
[]).append(brain.uid)
security_uid_cache[cache_key] = security_uid_dict
else:
security_uid_list = []
return security_uid_list, role_column_dict, local_role_column_dict
security_uid_dict = []
return security_uid_dict, role_column_dict, local_role_column_dict
security.declarePublic('getSecurityQuery')
def getSecurityQuery(self, query=None, sql_catalog_id=None, **kw):
......@@ -516,24 +555,42 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
# We need no security check for super user.
return query
original_query = query
security_uid_list, role_column_dict, local_role_column_dict = \
self.getSecurityUidListAndRoleColumnDict(
sql_catalog_id=sql_catalog_id, **kw)
security_uid_dict, role_column_dict, local_role_column_dict = \
self.getSecurityUidDictAndRoleColumnDict(sql_catalog_id=sql_catalog_id, **kw)
role_query = None
security_uid_query = None
if role_column_dict:
query_list = []
for key, value in role_column_dict.items():
new_query = Query(**{key : value})
query_list.append(new_query)
operator_kw = {'operator': 'OR'}
query = ComplexQuery(*query_list, **operator_kw)
# If security_uid_list is empty, adding it to criterions will only
# result in "false or [...]", so avoid useless overhead by not
# adding it at all.
if security_uid_list:
query = ComplexQuery(Query(security_uid=security_uid_list, operator='IN'),
query, operator='OR')
elif security_uid_list:
query = Query(security_uid=security_uid_list, operator='IN')
role_query = ComplexQuery(*query_list, **operator_kw)
if security_uid_dict:
catalog_security_uid_groups_columns_dict = \
self.getSQLCatalog().getSQLCatalogSecurityUidGroupsColumnsDict()
query_list = []
for local_roles_group_id, security_uid_list in\
security_uid_dict.iteritems():
assert security_uid_list
query_list.append(Query(**{
catalog_security_uid_groups_columns_dict[local_roles_group_id]:
security_uid_list,
'operator': 'IN'}))
security_uid_query = ComplexQuery(*query_list, operator='OR')
if role_query:
if security_uid_query:
# merge
query = ComplexQuery(security_uid_query, role_query, operator='OR')
else:
query = role_query
elif security_uid_query:
query = security_uid_query
else:
# XXX A false query has to be generated.
# As it is not possible to use SQLKey for now, pass impossible value
......@@ -672,16 +729,28 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
else:
document_w = w
(security_uid, optimised_roles_and_users) = catalog.getSecurityUid(document_w)
#LOG('catalog_object optimised_roles_and_users', 0, str(optimised_roles_and_users))
# XXX we should build vars begore building the wrapper
(security_uid_dict, optimised_roles_and_users) = \
catalog.getSecurityUidDict(document_w)
w.optimised_roles_and_users = optimised_roles_and_users
catalog_security_uid_groups_columns_dict = \
catalog.getSQLCatalogSecurityUidGroupsColumnsDict()
default_security_uid_column = catalog_security_uid_groups_columns_dict['']
for local_roles_group_id, security_uid in security_uid_dict.items():
catalog_column = catalog_security_uid_groups_columns_dict.get(
local_roles_group_id, default_security_uid_column)
setattr(w, catalog_column, security_uid)
# XXX we should build vars begore building the wrapper
predicate_property_dict = catalog.getPredicatePropertyDict(object)
if predicate_property_dict is not None:
w.predicate_property_dict = predicate_property_dict
else:
w.predicate_property_dict = {}
w.security_uid = security_uid
(subject_set_uid, optimised_subject_list) = catalog.getSubjectSetUid(document_w)
w.optimised_subject_list = optimised_subject_list
w.subject_set_uid = subject_set_uid
......
# -*- 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', )
# add category
self.portal.portal_categories.local_role_group.newContent(
portal_type='Category',
reference = 'Alternate',
id = 'Alternate')
# 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_role_group_value=self.portal.portal_categories.local_role_group.Alternate.getRelativeUrl())
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
......@@ -580,6 +580,29 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
self.assertFalse('Assignee' in user.getRolesInContext(obj))
self.abort()
def testLocalRolesGroupId(self):
"""Assigning a role with local roles group id.
"""
self.portal.portal_categories.local_role_group.newContent(
portal_type='Category',
reference = 'Alternate',
id = 'Alternate')
self._getTypeInfo().newContent(portal_type='Role Information',
role_name='Assignor',
local_role_group_value=self.portal.portal_categories.local_role_group.Alternate.getRelativeUrl(),
role_category=self.defined_category)
self.loginAsUser(self.username)
user = getSecurityManager().getUser()
obj = self._makeOne()
self.assertEqual(['Assignor'], obj.__ac_local_roles__.get('F1_G1_S1'))
self.assertTrue('Assignor' in user.getRolesInContext(obj))
self.assertEqual(set([('F1_G1_S1', 'Assignor')]),
obj.__ac_local_roles_group_id_dict__.get('Alternate'))
self.abort()
def testDynamicLocalRole(self):
"""Test simple case of setting a dynamic role.
The site category is not defined explictly the role, and will have the
......
......@@ -183,5 +183,4 @@ class RoleInformation(XMLObject):
return group_id_role_dict
InitializeClass(RoleInformation)
......@@ -89,8 +89,26 @@ class LocalRoleAssignorMixIn(object):
else:
user_name = getSecurityManager().getUser().getId()
group_id_role_dict = self.getLocalRolesFor(ob, user_name)
group_id_role_dict = {}
local_roles_group_id_group_id = {}
# Merge results from applicable roles
for role_generator in self.getFilteredRoleListFor(ob):
local_roles_group_id = ''
if getattr(role_generator, 'getLocalRoleGroupValue', None) is not None:
# only some role generators like 'Role Information' support it
local_role_group = role_generator.getLocalRoleGroupValue()
if local_role_group is not None:
# role definitions use category to classify different types of local roles
# so use their categories' reference
local_roles_group_id = local_role_group.getReference() or local_role_group.getId()
for group_id, role_list \
in role_generator.getLocalRolesFor(ob, user_name).iteritems():
group_id_role_dict.setdefault(group_id, set()).update(role_list)
if local_roles_group_id:
for role in role_list:
# Feed local_roles_group_id_group_id with local roles assigned to a group
local_roles_group_id_group_id.setdefault(local_roles_group_id, set()).update(((group_id, role),))
## Update role assignments to groups
# Save the owner
for group, role_list in (ob.__ac_local_roles__ or {}).iteritems():
......@@ -101,24 +119,17 @@ class LocalRoleAssignorMixIn(object):
for group, role_list in group_id_role_dict.iteritems():
if role_list:
ac_local_roles[group] = list(role_list)
if local_roles_group_id_group_id:
ob.__ac_local_roles_group_id_dict__ = local_roles_group_id_group_id
elif getattr(aq_base(ob),
'__ac_local_roles_group_id_dict__', None) is not None:
delattr(ob, '__ac_local_roles_group_id_dict__')
## Make sure that the object is reindexed
if reindex:
ob.reindexObjectSecurity()
security.declarePrivate("getLocalRolesFor")
def getLocalRolesFor(self, ob, user_name=None):
"""Compute the security that should be applied on an object
Returned value is a dict: {groud_id: role_name_set, ...}
"""
group_id_role_dict = {}
# Merge results from applicable roles
for role in self.getFilteredRoleListFor(ob):
for group_id, role_list \
in role.getLocalRolesFor(ob, user_name).iteritems():
group_id_role_dict.setdefault(group_id, set()).update(role_list)
return group_id_role_dict
security.declarePrivate('getFilteredRoleListFor')
def getFilteredRoleListFor(self, ob=None):
"""Return all role generators applicable to the object."""
......
......@@ -39,7 +39,7 @@ class ILocalRoleGenerator(Interface):
Returned value is a dict: {groud_id: role_name_set, ...}
"""
class ILocalRoleAssignor(ILocalRoleGenerator):
class ILocalRoleAssignor(Interface):
"""
"""
def updateLocalRolesOnDocument(ob, user_name=None, reindex=True):
......
......@@ -36,6 +36,7 @@ from Persistence import Persistent
from Products.ERP5Type.Globals import PersistentMapping
from itertools import izip
from MySQLdb import ProgrammingError, OperationalError
from DateTime import DateTime
def DCWorkflowDefinition_notifyWorkflowMethod(self, ob, transition_list, args=None, kw=None):
'''
......@@ -89,7 +90,6 @@ DCWorkflowDefinition.notifySuccess = DCWorkflowDefinition_notifySuccess
WORKLIST_METADATA_KEY = 'metadata'
SECURITY_PARAMETER_ID = 'local_roles'
SECURITY_COLUMN_ID = 'security_uid'
COUNT_COLUMN_TITLE = 'count'
class ExclusionList(list):
......@@ -145,7 +145,7 @@ def updateWorklistSetDict(worklist_set_dict, workflow_worklist_key, valid_criter
[workflow_worklist_key] = valid_criterion_dict
def groupWorklistListByCondition(worklist_dict, sql_catalog,
getSecurityUidListAndRoleColumnDict=None):
getSecurityUidDictAndRoleColumnDict=None):
"""
Get a list of dict of WorklistVariableMatchDict grouped by compatible
conditions.
......@@ -185,7 +185,7 @@ def groupWorklistListByCondition(worklist_dict, sql_catalog,
for workflow_id, worklist in worklist_dict.iteritems():
for worklist_id, worklist_match_dict in worklist.iteritems():
workflow_worklist_key = '/'.join((workflow_id, worklist_id))
if getSecurityUidListAndRoleColumnDict is None:
if getSecurityUidDictAndRoleColumnDict is None:
valid_criterion_dict, metadata = getValidCriterionDict(
worklist_match_dict=worklist_match_dict,
sql_catalog=sql_catalog,
......@@ -201,15 +201,18 @@ def groupWorklistListByCondition(worklist_dict, sql_catalog,
security_kw = {}
if len(security_parameter):
security_kw[SECURITY_PARAMETER_ID] = security_parameter
uid_list, role_column_dict, local_role_column_dict = \
getSecurityUidListAndRoleColumnDict(**security_kw)
uid_dict, role_column_dict, local_role_column_dict = \
getSecurityUidDictAndRoleColumnDict(**security_kw)
for key, value in local_role_column_dict.items():
worklist_match_dict[key] = [value]
if len(uid_list):
uid_list.sort()
role_column_dict[SECURITY_COLUMN_ID] = uid_list
catalog_security_uid_groups_columns_dict = \
sql_catalog.getSQLCatalogSecurityUidGroupsColumnsDict()
for local_roles_group_id, uid_list in uid_dict.iteritems():
role_column_dict[
catalog_security_uid_groups_columns_dict[local_roles_group_id]] = uid_list
# Make sure every item is a list - or a tuple
for security_column_id in role_column_dict.iterkeys():
value = role_column_dict[security_column_id]
......@@ -271,6 +274,8 @@ def generateNestedQuery(priority_list, criterion_dict,
**{my_criterion_id: criterion_value})
if isinstance(criterion_value, ExclusionTuple):
query = NegatedQuery(query)
query = ComplexQuery(operator='OR',
*(query, Query(**{my_criterion_id: None})))
append(ComplexQuery(query, subcriterion_query, operator='AND'))
else:
possible_value_list = tuple()
......@@ -296,6 +301,8 @@ def generateNestedQuery(priority_list, criterion_dict,
if len(impossible_value_list):
query = Query(operator='IN', **{my_criterion_id: impossible_value_list})
query = NegatedQuery(query)
query = ComplexQuery(operator='OR',
*(query, Query(**{my_criterion_id: None})))
value_query_list.append(query)
append(ComplexQuery(operator='AND', *value_query_list))
if len(query_list):
......@@ -334,6 +341,17 @@ def getWorklistListQuery(grouped_worklist_dict):
assert COUNT_COLUMN_TITLE not in total_criterion_id_dict
return (total_criterion_id_list, query)
# XXX: see ZMySQLDA/db.py:DB.defs
# This dict is used to tell which cast to apply to worklist parameters to get
# values comparable with SQL result content.
_sql_cast_dict = {
'i': long,
'l': long,
'n': float,
'd': DateTime,
}
_sql_cast_fallback = str
def sumCatalogResultByWorklist(grouped_worklist_dict, catalog_result):
"""
Return a dict regrouping each worklist's result, extracting it from
......@@ -354,8 +372,8 @@ def sumCatalogResultByWorklist(grouped_worklist_dict, catalog_result):
if len(catalog_result) > 0:
# Transtype all worklist definitions where needed
criterion_id_list = []
class_dict = dict(((name, value.__class__) for name, value in \
izip(catalog_result.names(), catalog_result[0])))
class_dict = dict(((name, _sql_cast_dict.get(x['type'], _sql_cast_fallback))
for name, x in catalog_result.data_dictionary().iteritems()))
for criterion_dict in grouped_worklist_dict.itervalues():
for criterion_id, criterion_value_list in criterion_dict.iteritems():
if type(criterion_value_list) is not ExclusionList:
......@@ -462,8 +480,8 @@ def WorkflowTool_listActions(self, info=None, object=None, src__=False):
else:
search_result = portal_catalog.unrestrictedSearchResults
select_expression_prefix = 'count(*) as %s' % (COUNT_COLUMN_TITLE, )
getSecurityUidListAndRoleColumnDict = \
portal_catalog.getSecurityUidListAndRoleColumnDict
getSecurityUidDictAndRoleColumnDict = \
portal_catalog.getSecurityUidDictAndRoleColumnDict
security_query_cache_dict = {}
def _getWorklistActionList():
worklist_result_dict = {}
......@@ -474,8 +492,8 @@ def WorkflowTool_listActions(self, info=None, object=None, src__=False):
groupWorklistListByCondition(
worklist_dict=worklist_dict,
sql_catalog=sql_catalog,
getSecurityUidListAndRoleColumnDict=\
getSecurityUidListAndRoleColumnDict)
getSecurityUidDictAndRoleColumnDict=\
getSecurityUidDictAndRoleColumnDict)
if src__:
action_list = []
for grouped_worklist_dict in worklist_list_grouped_by_condition:
......@@ -587,7 +605,8 @@ def WorkflowTool_refreshWorklistCache(self):
sql_catalog = portal_catalog.getSQLCatalog()
table_column_id_set = ImmutableSet(
[COUNT_COLUMN_TITLE] + self.Base_getWorklistTableColumnIDList())
security_column_id_list = ['security_uid'] + \
security_column_id_list = list(
sql_catalog.getSQLCatalogSecurityUidGroupsColumnsDict().values()) + \
[x[1] for x in sql_catalog.getSQLCatalogRoleKeysList()] + \
[x[1] for x in sql_catalog.getSQLCatalogLocalRoleKeysList()]
(worklist_list_grouped_by_condition, worklist_metadata) = \
......
......@@ -524,6 +524,14 @@ class Catalog(Folder,
'a monovalued local role',
'type': 'lines',
'mode': 'w' },
{ 'id': 'sql_catalog_security_uid_columns',
'title': 'Security Uid Columns',
'description': 'A list of mappings "local_roles_group_id | security_uid_column"'
' local_roles_group_id will be the name of a local roles'
' group, and security_uid_column is the corresponding'
' column in catalog table',
'type': 'lines',
'mode': 'w' },
{ 'id': 'sql_catalog_table_vote_scripts',
'title': 'Table vote scripts',
'description': 'Scripts helping column mapping resolution',
......@@ -575,6 +583,7 @@ class Catalog(Folder,
sql_catalog_scriptable_keys = ()
sql_catalog_role_keys = ()
sql_catalog_local_role_keys = ()
sql_catalog_security_uid_columns = (' | security_uid',)
sql_catalog_table_vote_scripts = ()
sql_catalog_raise_error_on_uid_check = True
......@@ -626,6 +635,18 @@ class Catalog(Folder,
role_key_dict[role.strip()] = column.strip()
return role_key_dict.items()
def getSQLCatalogSecurityUidGroupsColumnsDict(self):
"""
Return a mapping of local_roles_group_id name to the name of the column
storing corresponding security_uid.
The default mappiny is {'': 'security_uid'}
"""
local_roles_group_id_dict = {}
for local_roles_group_id_key in self.sql_catalog_security_uid_columns:
local_roles_group_id, column = local_roles_group_id_key.split('|')
local_roles_group_id_dict[local_roles_group_id.strip()] = column.strip()
return local_roles_group_id_dict
def getSQLCatalogLocalRoleKeysList(self):
"""
Return the list of local role keys.
......@@ -765,61 +786,73 @@ class Catalog(Folder,
self.subject_set_uid_dict = OIBTree()
self.subject_set_uid_index = None
security.declarePrivate('getSecurityUid')
def getSecurityUid(self, wrapped_object):
security.declarePrivate('getSecurityUidDict')
def getSecurityUidDict(self, wrapped_object):
"""
Cache a uid for each security permission
Return a tuple with a security uid (string) and a new tuple content the
roles and users if not exist already.
With the roles of object, search the security_uid associate in the
catalog_innodb:
- if the security not exist a security uid is generated with id_tool
or security_uid_index property and
return the new security_uid and the tuple contains the new roles
to add the roles in roles_and_user table of the database.
- if the security exist the security uid is returned and the second
element is None for not recreate the security in roles_and_user
table of the database.
We try to create a unique security (to reduce number of lines)
and to assign security only to root document
returns a tuple with a dict of security uid by local group id, and a tuple
containing optimised_roles_and_users that might have been created.
"""
# Get security information
allowed_roles_and_users = tuple(wrapped_object.allowedRolesAndUsers())
# Make sure no duplicates
if getattr(aq_base(self), 'security_uid_dict', None) is None:
self._clearSecurityCache()
elif self.security_uid_dict.has_key(allowed_roles_and_users):
return (self.security_uid_dict[allowed_roles_and_users], None)
# If the id_tool is there, it is better to use it, it allows
# to create many new security uids by the same time
# because with this tool we are sure that we will have 2 different
# uids if two instances are doing this code in the same time
id_tool = getattr(self.getPortalObject(), 'portal_ids', None)
if id_tool is not None:
default = 1
# We must keep compatibility with existing sites
previous_security_uid = getattr(self, 'security_uid_index', None)
if previous_security_uid is not None:
# At some point, it was a Length
if isinstance(previous_security_uid, Length):
default = previous_security_uid() + 1
optimised_roles_and_users = []
local_roles_group_id_to_security_uid_mapping= dict()
# Get security information
for local_roles_group_id, allowed_roles_and_users in\
wrapped_object.getLocalRolesGroupIdDict().iteritems():
allowed_roles_and_users = tuple(sorted(allowed_roles_and_users))
key = (local_roles_group_id, allowed_roles_and_users)
if self.security_uid_dict.has_key(key):
local_roles_group_id_to_security_uid_mapping[local_roles_group_id] \
= self.security_uid_dict[key]
elif self.security_uid_dict.has_key(allowed_roles_and_users)\
and not local_roles_group_id:
# This key is present in security_uid_dict without
# local_roles_group_id, it has been inserted before
# local_roles_group_id were introduced.
local_roles_group_id_to_security_uid_mapping[local_roles_group_id] = \
self.security_uid_dict[allowed_roles_and_users]
else:
# If the id_tool is there, it is better to use it, it allows
# to create many new security uids by the same time
# because with this tool we are sure that we will have 2 different
# uids if two instances are doing this code in the same time
if id_tool is not None:
default = 1
# We must keep compatibility with existing sites
previous_security_uid = getattr(self, 'security_uid_index', None)
if previous_security_uid is not None:
# At some point, it was a Length
if isinstance(previous_security_uid, Length):
default = previous_security_uid() + 1
else:
default = previous_security_uid
security_uid = int(id_tool.generateNewId(id_generator='uid',
id_group='security_uid_index', default=default))
else:
default = previous_security_uid
security_uid = int(id_tool.generateNewId(id_generator='uid',
id_group='security_uid_index', default=default))
else:
previous_security_uid = getattr(self, 'security_uid_index', None)
if previous_security_uid is None:
previous_security_uid = 0
# At some point, it was a Length
if isinstance(previous_security_uid, Length):
previous_security_uid = previous_security_uid()
security_uid = previous_security_uid + 1
self.security_uid_index = security_uid
self.security_uid_dict[allowed_roles_and_users] = security_uid
return (security_uid, allowed_roles_and_users)
previous_security_uid = getattr(self, 'security_uid_index', None)
if previous_security_uid is None:
previous_security_uid = 0
# At some point, it was a Length
if isinstance(previous_security_uid, Length):
previous_security_uid = previous_security_uid()
security_uid = previous_security_uid + 1
self.security_uid_index = security_uid
self.security_uid_dict[key] = security_uid
local_roles_group_id_to_security_uid_mapping[local_roles_group_id]\
= security_uid
# If some optimised_roles_and_users are returned by this method it
# means that new entries will have to be added to roles_and_users table.
for user in allowed_roles_and_users:
optimised_roles_and_users.append((security_uid, local_roles_group_id, user))
return (local_roles_group_id_to_security_uid_mapping, optimised_roles_and_users)
def getRoleAndSecurityUidList(self):
"""
......@@ -828,7 +861,8 @@ class Catalog(Folder,
"""
result = []
extend = result.extend
for role_list, security_uid in getattr(aq_base(self), 'security_uid_dict', {}).iteritems():
for role_list, security_uid in getattr(
aq_base(self), 'security_uid_dict', {}).iteritems():
extend([(role, security_uid) for role in role_list])
return result
......
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