[Erp5-report] r16498 - /erp5/trunk/products/ERP5Type/tests/SecurityTestCase.py

nobody at svn.erp5.org nobody at svn.erp5.org
Thu Sep 20 13:48:39 CEST 2007


Author: jerome
Date: Thu Sep 20 13:48:38 2007
New Revision: 16498

URL: http://svn.erp5.org?rev=16498&view=rev
Log:
A test case extending ERP5TypeTestCase for testing security. 
Contains methods like assertUserCanModifyDocument, assertUserCanPassWorkflowTransition


Added:
    erp5/trunk/products/ERP5Type/tests/SecurityTestCase.py

Added: erp5/trunk/products/ERP5Type/tests/SecurityTestCase.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/tests/SecurityTestCase.py?rev=16498&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/tests/SecurityTestCase.py (added)
+++ erp5/trunk/products/ERP5Type/tests/SecurityTestCase.py Thu Sep 20 13:48:38 2007
@@ -1,0 +1,259 @@
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SA and Contributors. All Rights Reserved.
+#    Jerome Perrin <jerome at 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.
+#
+##############################################################################
+
+"""Base Class for security tests using ERP5Security and DCWorkflow
+"""
+
+from AccessControl.SecurityManagement import newSecurityManager
+from AccessControl.SecurityManagement import getSecurityManager
+from AccessControl.SecurityManagement import setSecurityManager
+from Products.DCWorkflow.Transitions import TRIGGER_USER_ACTION
+
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from Products.ERP5Type import Permissions
+
+from Products.DCWorkflow import Guard
+def formatNameUnion(names):
+  names = list(names)
+  if len(names) == 2:
+    return ' or '.join(names)
+  elif len(names) > 2:
+    names[-1] = ' or ' + names[-1]
+  return '; '.join(names)
+Guard.formatNameUnion = formatNameUnion
+
+
+class AssertPermissionMethod(object):
+  """A method object to check that a user have a permission on a document.
+  """
+  def __init__(self, permission_name):
+    self._permission_name = permission_name
+  
+  def __get__(self, instance, cls=None):
+    self._instance = instance
+    return self
+
+  def __call__(self, username, document):
+    sm = getSecurityManager()
+    try:
+      self._instance._loginAsUser(username)
+      user = getSecurityManager().getUser()
+      if not user.has_permission(self._permission_name, document):
+        groups = []
+        if hasattr(user, 'getGroups'):
+          groups = user.getGroups()
+        self._instance.fail(
+          'User %s does NOT have %s permission on %s (user roles: [%s], '
+          'roles needed: [%s], existing local roles: %s, '
+          'your user groups: [%s])' %
+          (username, self._permission_name, document,
+           ', '.join(user.getRolesInContext(document)),
+           ', '.join([x['name'] for x in
+                      document.rolesOfPermission(self._permission_name)
+                      if x['selected']]),
+           repr(document.get_local_roles()),
+           ', '.join(groups)))
+    finally:
+      setSecurityManager(sm)
+
+
+class AssertNoPermissionMethod(object):
+  """A method object to check that a user does not have a permission on a
+  document.
+  """
+  def __init__(self, permission_name):
+    self._permission_name = permission_name
+  
+  def __get__(self, instance, cls=None):
+    self._instance = instance
+    return self
+
+  def __call__(self, username, document):
+    sm = getSecurityManager()
+    try:
+      self._instance._loginAsUser(username)
+      user = getSecurityManager().getUser()
+      if user.has_permission(self._permission_name, document):
+        self._instance.fail(
+          'User %s has %s permission on %s (roles: [%s])' %
+          (username, self._permission_name, document,
+            ', '.join(user.getRolesInContext(document))))
+    except:
+      setSecurityManager(sm)
+
+
+class SecurityTestCase(ERP5TypeTestCase):
+  """Base class for security tests.
+  """
+  def _setup(self):
+    """set up, create categories and users."""
+    ERP5TypeTestCase._setup(self)
+    self.login()
+    self.portal = self.getPortal()
+    self.workflow_tool = self.portal.portal_workflow
+  
+  def tearDown(self):
+    """Clean up for next test.
+    """
+    self.tic()
+    get_transaction().abort()
+    self.portal.portal_caches.clearAllCache()
+    ERP5TypeTestCase.tearDown(self)
+
+  def _loginAsUser(self, username):
+    """Login as a given username. The user must exist.
+    """
+    uf = self.getPortal().acl_users
+    user = uf.getUserById(username)
+    self.assertNotEquals(user, None, 'No user %s' % username)
+    newSecurityManager(None, user.__of__(uf))
+  
+  # Permission methods
+  failIfUserCanViewDocument = AssertNoPermissionMethod(
+                                     Permissions.View)
+  failIfUserCanAccessDocument = AssertNoPermissionMethod(
+                                     Permissions.AccessContentsInformation)
+  failIfUserCanModifyDocument = AssertNoPermissionMethod(
+                                     Permissions.ModifyPortalContent)
+  failIfUserCanAddDocument = AssertNoPermissionMethod(
+                                     Permissions.AddPortalContent)
+
+  def failIfUserHavePermissionOnDocument(self, permission_name, username, document):
+    """Fail If the user have a permission on document.
+    XXX why isn't it a method object ?
+    """
+    method = AssertNoPermissionMethod(permission_name)
+    method._instance = self
+    return method(username, document)
+
+  failUnlessUserCanViewDocument = assertUserCanViewDocument =\
+                AssertPermissionMethod(Permissions.View)
+  failUnlessUserCanAccessDocument = assertUserCanAccessDocument =\
+                AssertPermissionMethod(Permissions.AccessContentsInformation)
+  failUnlessUserCanModifyDocument = assertUserCanModifyDocument = \
+                AssertPermissionMethod(Permissions.ModifyPortalContent)
+  failUnlessUserCanAddDocument = assertUserCanAddDocument =\
+                AssertPermissionMethod(Permissions.AddPortalContent)
+
+  def failUnlessUserHavePermissionOnDocument(self, permission_name, username, document):
+    """Fail Unless the user have a permission on document."""
+    method = AssertPermissionMethod(permission_name)
+    method._instance = self
+    return method(username, document)
+  assertUserHavePermissionOnDocument = failUnlessUserHavePermissionOnDocument
+
+  # Workflow Transition Methods
+  def failIfUserCanPassWorkflowTransition(self, username, transition, document):
+    """Fails if the user can pass the workflow transition on the document."""
+    sm = getSecurityManager()
+    try:
+      self._loginAsUser(username)
+      user = getSecurityManager().getUser()
+      valid_transistion_list =[ai['id'] for ai in
+                            self.workflow_tool.listActions(object=document) if
+                            ai['category'] == 'workflow']
+      if transition in valid_transistion_list:
+        self.fail('User %s can pass %s transition on %s. Roles: [%s]' % (
+                  username, transition, document,
+                  ", ".join(user.getRolesInContext(document))))
+    finally:
+      setSecurityManager(sm)
+
+  def failUnlessUserCanPassWorkflowTransition(self, username,
+                                              transition, document):
+    """Fails unless the user can pass the workflow transition on the document."""
+    sm = getSecurityManager()
+    try:
+      self._loginAsUser(username)
+      user = getSecurityManager().getUser()
+      valid_transistion_list =[ai['id'] for ai in
+                            self.workflow_tool.listActions(object=document) if
+                            ai['category'] == 'workflow']
+      if transition not in valid_transistion_list:
+        # Build a comprehensive error message
+        workflow_states_description = []
+        workflow_transitions_description = []
+        for wf in self.workflow_tool.getWorkflowsFor(document) or []:
+          if wf.getId() == 'edit_workflow':
+            continue
+          for wf_transition_id in wf._getWorkflowStateOf(
+                                                document).getTransitions():
+            wf_transition = wf.transitions[wf_transition_id]
+            if wf_transition.trigger_type == TRIGGER_USER_ACTION:
+              workflow_transitions_description.append(
+                "%s%s[%s]: %s" % (
+                  wf_transition_id == transition and "* " or "  ",
+                  wf_transition_id, wf.getId(),
+                  wf_transition.getGuardSummary()))
+
+          workflow_states_description.append("%s on %s" % (
+                  wf._getWorkflowStateOf(document, id_only=1), wf.getId()))
+
+        document_description = "%s at %s (%s)" % (
+              document.getPortalType(), document.getPath(),
+              ", ".join(workflow_states_description))
+
+        self.fail('User %s can NOT pass %s transition on %s.\n '
+                  'Roles: [%s]\n Available transitions:\n\t%s' % ( username,
+                  transition, document_description,
+                  ", ".join(user.getRolesInContext(document)),
+                  "\n\t".join(workflow_transitions_description)))
+    finally:
+      setSecurityManager(sm)
+
+  assertUserCanPassWorkflowTransition = failUnlessUserCanPassWorkflowTransition
+
+  # Simple check for an user Role
+  def failIfUserHaveRoleOnDocument(self, username, role, document):
+    """Fails if the user have the role on the document."""
+    sm = getSecurityManager()
+    try:
+      self._loginAsUser(username)
+      user = getSecurityManager().getUser()
+      if role in user.getRolesInContext(document):
+        self.fail('User %s have %s role on %s at %s' % (
+          username, role, document.getPortalType(), document.getRelativeUrl()))
+    finally:
+      setSecurityManager(sm)
+
+  def failUnlessUserHaveRoleOnDocument(self, username, role, document):
+    """Fails if the user does not have the role on the document."""
+    sm = getSecurityManager()
+    try:
+      self._loginAsUser(username)
+      user = getSecurityManager().getUser()
+      if role not in user.getRolesInContext(document):
+        self.fail('User %s does not have %s role on %s at %s '
+                  '(user roles: %s)' % ( username, role,
+                  document.getPortalType(), document.getRelativeUrl(),
+                  user.getRolesInContext(document)))
+    finally:
+      setSecurityManager(sm)
+  
+  assertUserHaveRoleOnDocument = failUnlessUserHaveRoleOnDocument
+




More information about the Erp5-report mailing list