##############################################################################
#
# Copyright (c) 2001 Zope Foundation and Contributors
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this
# distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" DelegatingMultiPlugin   Shim to use any User Folder with the
                            PluggableAuthenticationService
"""
 
__doc__     = """ Delegating User Folder shim module """
__version__ = '$Revision$'[11:-2]
 
# General Python imports
import copy, os
from urllib import quote_plus
 
# Zope imports
from Acquisition import aq_base
from OFS.Folder import Folder
from App.class_init import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.SpecialUsers import emergency_user
 
from zope.interface import Interface
from AccessControl import AuthEncoding
 
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
 
from Products.PluggableAuthService.interfaces.plugins import \
    IAuthenticationPlugin
from Products.PluggableAuthService.interfaces.plugins import \
    IUserEnumerationPlugin
from Products.PluggableAuthService.interfaces.plugins import \
    IRolesPlugin
from Products.PluggableAuthService.interfaces.plugins import \
    ICredentialsUpdatePlugin
from Products.PluggableAuthService.interfaces.plugins import \
    ICredentialsResetPlugin
from Products.PluggableAuthService.interfaces.plugins import \
    IPropertiesPlugin
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.PluggableAuthService.utils import classImplements
 
class IDelegatingMultiPlugin(Interface):
    """ Marker interface.
    """
 
manage_addDelegatingMultiPluginForm = PageTemplateFile(
    'www/dmpAdd', globals(), __name__='manage_addDelegatingMultiPluginForm' )
 
 
def manage_addDelegatingMultiPlugin( self
                                   , id
                                   , title=''
                                   , delegate_path=''
                                   , REQUEST=None
                                   ):
    """ Factory method to instantiate a DelegatingMultiPlugin """
    # Make sure we really are working in our container (the
    # PluggableAuthService object)
    self = self.this()
 
    # Instantiate the folderish adapter object
    lmp = DelegatingMultiPlugin( id, title=title
                               , delegate_path=delegate_path )
    self._setObject(id, lmp)
 
    if REQUEST is not None:
        REQUEST.RESPONSE.redirect('%s/manage_main' % self.absolute_url())
 
 
 
class DelegatingMultiPlugin(Folder, BasePlugin):
    """ The adapter that mediates between the PAS and the DelegatingUserFolder
    """
    security = ClassSecurityInfo()
    meta_type = 'Delegating Multi Plugin'
 
    manage_options = ( BasePlugin.manage_options[:1]
                     + Folder.manage_options
                     )
 
    _properties = ( { 'id' : 'delegate'
                    , 'label' : ' Delegate Path'
                    , 'type' : 'string'
                    , 'mode' : 'w'
                    }
                  ,
                  )
 
    def __init__(self, id, title='', delegate_path=''):
        """ Initialize a new instance """
        self.id = id
        self.title = title
        self.delegate = delegate_path
 
 
    security.declarePrivate('_getUserFolder')
    def _getUserFolder(self):
        """ Safely retrieve a User Folder to work with """
        uf = getattr(aq_base(self), 'acl_users', None)
 
        if uf is None and self.delegate:
            uf = self.unrestrictedTraverse(self.delegate)
 
        return uf
 
 
    security.declarePrivate('authenticateCredentials')
    def authenticateCredentials(self, credentials):
        """ Fulfill AuthenticationPlugin requirements """
        acl = self._getUserFolder()
        login = credentials.get('login', '')
        password = credentials.get('password', '')
 
        if not acl or not login or not password:
            return (None, None)
 
        if login == emergency_user.getUserName() and \
                AuthEncoding.pw_validate(emergency_user._getPassword(), password):
            return ( login, login )
 
        user = acl.getUser(login)
 
        if user is None:
            return (None, None)
 
        elif user and AuthEncoding.pw_validate(user._getPassword(),
                                               password):
            return ( user.getId(), login )
 
        return (None, None)
 
 
    security.declarePrivate('updateCredentials')
    def updateCredentials(self, request, response, login, new_password):
        """ Fulfill CredentialsUpdatePlugin requirements """
        # Need to at least remove user from cache
        pass
 
 
    security.declarePrivate('resetCredentials')
    def resetCredentials(self, request, response):
        """ Fulfill CredentialsResetPlugin requirements """
        # Remove user from cache?
        pass
 
 
    security.declarePrivate('getPropertiesForUser')
    def getPropertiesForUser(self, user, request=None):
        """ Fullfill PropertiesPlugin requirements """
        acl = self._getUserFolder()
 
        if acl is None:
            return {}
 
        user = acl.getUserById(user.getId())
 
        if user is None:
            return {}
 
        # XXX WAAA
        return copy.deepcopy(user.__dict__)
 
 
    security.declarePrivate('getRolesForPrincipal')
    def getRolesForPrincipal(self, user, request=None):
        """ Fullfill RolesPlugin requirements """
        acl = self._getUserFolder()
 
        if acl is None:
            return ()
 
        user = acl.getUserById(user.getId())
 
        if user is None:
            return ()
 
        return tuple(user.getRoles())
 
 
    security.declarePrivate('enumerateUsers')
    def enumerateUsers( self
                      , id=None
                      , login=None
                      , exact_match=0
                      , sort_by=None
                      , max_results=None
                      , **kw
                      ):
        """ Fulfill the EnumerationPlugin requirements """
        result = []
        acl = self._getUserFolder()
        plugin_id = self.getId()
        edit_url = '%s/%s/manage_userrecords' % (plugin_id, acl.getId())
 
        if acl is None:
            return ()
 
        if exact_match:
            if id:
                user = acl.getUserById(id)
            elif login:
                user = acl.getUser(login)
            else:
                msg = 'Exact Match specified but no ID or Login given'
                raise ValueError, msg
 
            if user is not None:
                result.append( { 'id' : user.getId()
                               , 'login' : user.getUserName()
                               , 'pluginid' : plugin_id
                               , 'editurl' : '%s' % edit_url
                               } )
        else:
            l_results = []
            seen = []
            # XXX WAAAAA!!!!
            all_users = acl.getUsers()
 
            for user in all_users:
                if id:
                    if user.getId().find(id) != -1:
                        result.append( { 'login' : user.getUserName()
                                       , 'id' : user.getId()
                                       , 'pluginid' : plugin_id
                                       } )
                elif login:
                    if user.getUserName().find(login) != -1:
                        result.append( { 'login' : user.getUserName()
                                       , 'id' : user.getId()
                                       , 'pluginid' : plugin_id
                                       } )
 
            if sort_by is not None:
                result.sort(lambda a, b: cmp( a.get(sort_by, '').lower()
                                            , b.get(sort_by, '').lower()
                                            ) )
 
            if max_results is not None:
                try:
                    max_results = int(max_results)
                    result = result[:max_results+1]
                except ValueError:
                    pass
 
        return tuple(result)
 
classImplements( DelegatingMultiPlugin
               , IDelegatingMultiPlugin
               , IAuthenticationPlugin
               , IUserEnumerationPlugin
               , IRolesPlugin
               , ICredentialsUpdatePlugin
               , ICredentialsResetPlugin
               , IPropertiesPlugin
               )
 
 
InitializeClass(DelegatingMultiPlugin)