# -*- coding: utf-8 -*-
## GroupUserFolder
## Copyright (C)2006 Ingeniweb

## 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; see the file COPYING. If not, write to the
## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""

"""
__version__ = "$Revision:  $"
# $Source:  $
# $Id: GRUFTestCase.py 30098 2006-09-08 12:35:01Z encolpe $
__docformat__ = 'restructuredtext'

import os, sys
if __name__ == '__main__':
    execfile(os.path.join(sys.path[0], 'framework.py'))

# Load fixture
from Testing import ZopeTestCase

# Permissions / security
from AccessControl.Permissions import access_contents_information, view, add_documents_images_and_files, change_images_and_files, view_management_screens
from AccessControl.SecurityManagement import newSecurityManager, noSecurityManager, getSecurityManager
from AccessControl import Unauthorized
from AccessControl.User import UnrestrictedUser
from AccessControl import Permission
import urllib



# Set log options if Log module is available
# This is done to set LOG_PROCESSORs to file logs instead of Zope logs
try:
    import Log

    Log.LOG_LEVEL = Log.LOG_DEBUG

    Log.LOG_PROCESSOR = {
        Log.LOG_NONE: Log.LogFile,
        Log.LOG_CRITICAL: Log.LogFile,
        Log.LOG_ERROR: Log.LogFile,
        Log.LOG_WARNING: Log.LogFile,
        Log.LOG_NOTICE: Log.LogFile,
        Log.LOG_DEBUG: Log.LogFile,
        }

    from Log import *
    Log(LOG_NOTICE, "Starting %s at %d debug level" % (os.path.dirname(__file__), LOG_LEVEL, ))

except:
    print "Log module not available"
    LOG_DEBUG = None
    LOG_NOTICE = None
    LOG_WARNING = None
    LOG_ERROR = None
    LOG_CRITICAL = None
    def Log(*args, **kw):
        pass
    raise



class ManagementOpener(urllib.FancyURLopener):
    def prompt_user_passwd(self, host, realm): 
        return ('manager', 'secret')
        
class UnauthorizedOpener(urllib.FancyURLopener):
    def prompt_user_passwd(self, host, realm): 
        raise Unauthorized, 'The URLopener was asked for authentication'


class GRUFTestCase(ZopeTestCase.ZopeTestCase):

    def gruf_setup(self,):
        """
        gruf_setup(self,) => Basic gruf setup
        """
        # Replace default acl_user by a GRUF one
        self.folder.manage_delObjects(['acl_users'])
        self.folder.manage_addProduct['OFSP'].manage_addFolder('testFolder')
        self.gruf_folder = self.folder.testFolder
        self.gruf_folder.manage_addProduct['GroupUserFolder'].manage_addGroupUserFolder()
        self.gruf = self.gruf_folder.acl_users


    def gruf_sources_setup(self,):
        """
        gruf_sources_setup(self,) => this method installs the required sources inside GRUF.
        One can override this in a test case to install other sources and make another bunch of unit tests
        For example, for LDAPUserFolder, use manage_addProduct/LDAPUserFolder/addLDAPUserFolder
        """
        # Use this to replace the default user source
        self.gruf.replaceUserSource("Users", "manage_addProduct/OFSP/manage_addUserFolder")

        # Use this to replace the default group source
        self.gruf.replaceUserSource("Groups", "manage_addProduct/OFSP/manage_addUserFolder")

        # add a second (empty) user source
        self.gruf.addUserSource("manage_addProduct/OFSP/manage_addUserFolder")

    def security_context_setup(self,):
        """
        Build a complex security environment

        It creates:
          * 3 roles, r1, r2 and r3
          * 3 groups: g1, g2, g3
          * n users as follows (roles are between parenthesis)

                      !   g1    ! g2(r1)     ! g3(r2)     ! g4(r2,r3)  !  Resulting roles !
          ------------!---------!------------!------------!------------!------------------!
          u1          !         !            !            !            !   => (no role)   !
          u2          !   X     !            !            !            !   => (no role)   !
          u3          !   X     !      X     !            !            !   => r1          !
          u4          !   X     !      X     !      X     !            !   => r1,r2       !
          u5(r1)      !         !      X     !      X     !            !   => r1,r2       !
          u6(r1)      !         !            !      X     !            !   => r1,r2       !
          u7(r1)      !         !            !            !     X      !   => r1,r2,r3    !
          ---------------------------------------------------------------------------------

        It also creates a 'lr' folder in which g1 group and u3 and u6 are granted r3 role.


        And then, it creates nested groups as follow (-> = belongs to group...):

             u/g   !  belongs to... ! resulting roles                     !
         ----------!----------------!-------------------------------------!
          ng1      !   g1           ! (no role)                           !
          ng2      !   g2, g3       ! r1, r2                              !
          ng3      !   g2, ng2      ! r1, r2                              !
          ng4(r3)  !   g2, ng2      ! r1, r2, r3                          !
          ng5      !   g2, ng4      ! r1, r2, r3                          !
          ng6      !   ng5, ng6     ! r1, r2, r3 (no circ. ref)           !
          u8       !   ng1          ! (no role)                           !
          u9       !   g1, ng2      ! r1, r2                              !
          u10      !   ng2, ng3     ! r1, r2                              !
          u11(r3)  !   ng2, ng3     ! r1, r2, r3                          !
          u12      !   ng5, ng6     ! r1, r2, r3                          !
         -----------------------------------------------------------------!

         Plus we have the following local roles matrix (roles inside parenthesis are implicitly
         user-defined, roles inside brackets are implicitly lr-defined).

         folder
           |
           |-- acl_users (GRUF)
           |
           |                                          r1    r2    r3
           |                             /--
           |                             | group_g1               x
           |-- lr                        | u3        (x)          x
               |                         | u6        (x)   (x)    x
               |                         \--
               |
               |                         /--
               |                         | group_g1              [x]
               |-- sublr                 | u3        (x)    x    [x]
               |                         | u6        (x)    x*   [x]
               |                         \--
               |
               |                         /--
               |                         | group_g1              [x]
               |-- sublr2                | u3        (x)    x    [x]
               |   |                     | u6        (x)    x*   [x]
               |   |                     \--
               |   |
               |   |                     /--
               |   |                     | group_g1              [x]
               |   |-- subsublr2         | u3        (x)   [x]   [x]
               |                         | u6        (x)  [(x)]  [x]
               |                         \--
               |
         (now we block local roles under this branch)
               |
               |                         /--
               |                         | group_g1              
               |-- sublr3                | u3        (x)    x    
                   |                     | u6        (x)    x*   
                   |                     \--
                   |
                   |                     /--
                   |                     | group_g1              
                   |-- subsublr3         | u3        (x)   [x]   
                                         | u6        (x)  [(x)]  
                                         \--
                                         
        *: u6 will have r2 as a localrole AND a userdefined role.
        """
        # Create a few roles
        self.gruf.userFolderAddRole("r1")
        self.gruf.userFolderAddRole("r2")
        self.gruf.userFolderAddRole("r3")

        # Set View permission on those roles
        permissions = self.gruf_folder.ac_inherited_permissions(1)
        for ip in range(len(permissions)):
            name, value = permissions[ip][:2]
            if name == "View":
                break
        p=Permission.Permission(name,value, self.gruf_folder)
        p.setRoles(("r1", "r2", "r3", ))

        # Setup users and groups
        self.security_context_setup_groups()
        self.security_context_setup_users()
        
        # Create a few folders to play with
        self.gruf_folder.manage_addProduct['OFSP'].manage_addFolder('lr')
        lr = self.gruf_folder.lr
        lr.manage_addProduct['OFSP'].manage_addFolder("sublr")
        sublr = self.gruf_folder.lr.sublr
        lr.manage_addProduct['OFSP'].manage_addFolder("sublr2")
        sublr2 = self.gruf_folder.lr.sublr2
        sublr2.manage_addProduct['OFSP'].manage_addFolder("subsublr2")
        subsublr2 = self.gruf_folder.lr.sublr2.subsublr2
        lr.manage_addProduct['OFSP'].manage_addFolder("sublr3")
        sublr3 = self.gruf_folder.lr.sublr3
        sublr3.manage_addProduct['OFSP'].manage_addFolder("subsublr3")
        subsublr3 = self.gruf_folder.lr.sublr3.subsublr3
        self.gruf._acquireLocalRoles(sublr3, 0)
        lr.manage_addLocalRoles("group_g1", ("r3", ))
        lr.manage_addLocalRoles("u3", ("r3", ))
        lr.manage_addLocalRoles("u6", ("r3", ))
        sublr.manage_addLocalRoles("u3", ("r2", ))
        sublr.manage_addLocalRoles("u6", ("r2", ))
        sublr2.manage_addLocalRoles("u3", ("r2", ))
        sublr2.manage_addLocalRoles("u6", ("r2", ))
        sublr3.manage_addLocalRoles("u3", ("r2", ))
        sublr3.manage_addLocalRoles("u6", ("r2", ))
        self.lr = lr
        self.sublr = sublr
        self.sublr2 = sublr2
        self.sublr3 = sublr3
        self.subsublr2 = subsublr2
        self.subsublr3 = subsublr3


    def security_context_setup_users(self,):
        # Create a manager and a few users
        self.gruf.userFolderAddUser('manager', 'secret', ('Manager',), (), (), )
        self.gruf.userFolderAddUser('u1', 'secret', (), (), (), )
        self.gruf.userFolderAddUser('u2', 'secret', (), (), ('g1', ), )
        self.gruf.userFolderAddUser('u3', 'secret', (), (), ('g1', 'g2'), )
        self.gruf.userFolderAddUser('u4', 'secret', (), (), ('g1', 'g2', 'g3'), )
        self.gruf.userFolderAddUser('u5', 'secret', ('r1', ), (), ('g2', 'g3'), )
        self.gruf.userFolderAddUser('u6', 'secret', ('r1', ), (), ('g3', ), )
        self.gruf.userFolderAddUser('u7', 'secret', ('r1', ), (), ('g4', ), )

        # Create nested-groups users
        self.gruf.userFolderAddUser('u8', 'secret', (), (), ('ng1', ), )        
        self.gruf.userFolderAddUser('u9', 'secret', (), (), ('g1', 'ng2', ), )
        self.gruf.userFolderAddUser('u10', 'secret', (), (), ('ng2', 'ng3', ), )        
        self.gruf.userFolderAddUser('u11', 'secret', ('r3', ), (), ('ng2', 'ng3', ), )        
##        self.gruf.userFolderAddUser('u12', 'secret', (), (), ('ng5', 'ng6', ), )        

    def security_context_setup_groups(self,):
        "create groups. We splitted to allow LDAP tests to override this"
        # Create a few groups
        self.gruf.userFolderAddGroup('g1', ())
        self.gruf.userFolderAddGroup('g2', ('r1', ))
        self.gruf.userFolderAddGroup('g3', ('r2', ))
        self.gruf.userFolderAddGroup('g4', ('r2', 'r3', ))

        # Create nested groups
        self.gruf.userFolderAddGroup('ng1', (), ('g1', ))
        self.gruf.userFolderAddGroup('ng2', (), ('g2', 'g3', ))
        self.gruf.userFolderAddGroup('ng3', (), ('g2', 'ng2', ))
        self.gruf.userFolderAddGroup('ng4', ('r3', ), ('g2', 'ng2', ))
        self.gruf.userFolderAddGroup('ng5', (), ('g2', 'ng4', ))
##        self.gruf.userFolderAddGroup('ng6', (), ('ng5', 'ng6', ))

        # Special case of nesting
        self.gruf.userFolderAddGroup('extranet', (), ())
        self.gruf.userFolderAddGroup('intranet', (), ('extranet', ))
        self.gruf.userFolderAddGroup('compta', (), ('intranet', 'extranet' ))


    def afterSetUp(self,):
        """
        afterSetUp(self) => This method is called to create Folder with a GRUF inside.
        """
        self.gruf_setup()
        self.gruf_sources_setup()
        self.security_context_setup()
##        # Need to commit so the ZServer threads see what we've done
##        get_transaction().commit()



    def beforeClose(self):
##        # Commit after cleanup
##        get_transaction().commit()

        # Remove users. This may be useful for non-ZODB user sources.
        self.delete_created_users()

    def delete_created_users(self,):
        self.gruf.userFolderDelUsers([
            "manager",
            "u1",
            "u2",
            "u3",
            "u4",
            "u5",
            "u6",
            "u7",
            "u8",
            "u9",
            "u10",
            "u11",
            "created_user",
            "test_prefix",
            "group_test_prefix",
            ])
        self.gruf.userFolderDelGroups([
            "g1",
            "g2",
            "g3",
            "g4",
            "ng1",
            "ng2",
            "ng3",
            "ng4",
            "ng5",
            "extranet",
            "intranet",
            "compta",
            ])

    def compareRoles(self, target, user, roles):
        """
        compareRoles(self, target, user, roles) => do not raise if user has exactly the specified roles.
        If target is None, test user roles (no local roles)
        """
        u = self.gruf.getUser(user)
        if not u:
            raise RuntimeError, "compareRoles: Invalid user: '%s'" % user
        if target is None:
            actual_roles = filter(lambda x: x not in ('Authenticated', 'Anonymous', ''), list(u.getRoles()))
        else:
            actual_roles = filter(lambda x: x not in ('Authenticated', 'Anonymous', ''), list(u.getRolesInContext(target)))
        actual_roles.sort()
        wished_roles = list(roles)
        wished_roles.sort()
        if actual_roles == wished_roles:
            return 1
        raise RuntimeError, "User %s: Whished roles: %s BUT current roles: %s" % (user, wished_roles, actual_roles)


    def compareGroups(self, user, groups):
        """
        compareGroups(self, user, groups) => do not raise if user has exactly the specified groups.
        """
        u = self.gruf.getUser(user)
        if not u:
            raise RuntimeError, "compareGroups: Invalid user: '%s'" % user
        actual_groups = list(u.getGroups())
        actual_groups.sort()
        wished_groups = map(lambda x: "group_%s" % x, list(groups))
        wished_groups.sort()
        if actual_groups == wished_groups:
            return 1
        raise RuntimeError, "User %s: Whished groups: %s BUT current groups: %s" % (user, wished_groups, actual_groups)


