Back to index

plone3  3.1.7
DomainAuthHelper.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights
00004 # Reserved.
00005 #
00006 # This software is subject to the provisions of the Zope Public License,
00007 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this
00008 # distribution.
00009 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00010 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00011 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00012 # FOR A PARTICULAR PURPOSE.
00013 #
00014 ##############################################################################
00015 """ DomainAuthHelper   Authentication Plugin for Domain authentication
00016 """
00017 
00018 __doc__     = """ Authentication Plugin for Domain authentication """
00019 __version__ = '$Revision: 79723 $'[11:-2]
00020 
00021 # General Python imports
00022 import socket, os, time, copy, re
00023 
00024 # General Zope imports
00025 from BTrees.OOBTree import OOBTree
00026 from Globals import InitializeClass
00027 from AccessControl import ClassSecurityInfo
00028 from AccessControl.Permissions import manage_users
00029 
00030 from zope.interface import Interface
00031 
00032 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00033 
00034 # PluggableAuthService imports
00035 from Products.PluggableAuthService.interfaces.plugins import \
00036     IAuthenticationPlugin
00037 from Products.PluggableAuthService.interfaces.plugins import \
00038     IExtractionPlugin
00039 from Products.PluggableAuthService.interfaces.plugins import \
00040     IRolesPlugin
00041 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
00042 from Products.PluggableAuthService.utils import classImplements
00043 
00044 class IDomainAuthHelper(Interface):
00045     """ Marker interface.
00046     """
00047 
00048 _MATCH_EQUALS = 'equals'
00049 _MATCH_ENDSWITH = 'endswith'
00050 _MATCH_REGEX = 'regex'
00051 
00052 manage_addDomainAuthHelperForm = PageTemplateFile(
00053     'www/daAdd', globals(), __name__='manage_addDomainAuthHelperForm' )
00054 
00055 def manage_addDomainAuthHelper(self, id, title='', REQUEST=None):
00056     """ Factory method to instantiate a DomainAuthHelper """
00057     obj = DomainAuthHelper(id, title=title)
00058     self._setObject(id, obj)
00059 
00060     if REQUEST is not None:
00061         qs = 'manage_tabs_message=DomainAuthHelper+added.'
00062         my_url = self.absolute_url()
00063         REQUEST['RESPONSE'].redirect('%s/manage_workspace?%s' % (my_url, qs))
00064 
00065 
00066 class DomainAuthHelper(BasePlugin):
00067     """ Domain Authentication plugin for the PluggableAuthService """
00068     security = ClassSecurityInfo()
00069     meta_type = 'Domain Authentication Plugin'
00070 
00071     security.declareProtected(manage_users, 'manage_map')
00072     manage_map = PageTemplateFile('www/daMatches', globals())
00073 
00074     security.declareProtected(manage_users, 'manage_genericmap')
00075     manage_genericmap = PageTemplateFile('www/daGeneric', globals())
00076 
00077     manage_options = ( BasePlugin.manage_options[:1]
00078                      + ( { 'label'  : 'User Map'
00079                          , 'action' : 'manage_map'
00080                        # , 'help'   : ( 'PluggableAuthService'
00081                        #              ,'matches.stx')
00082                          }
00083                        , { 'label'  : 'Generic Map'
00084                          , 'action' : 'manage_genericmap'
00085                          }
00086                        )
00087                      + BasePlugin.manage_options[1:]
00088                      )
00089 
00090 
00091     def __init__(self, id, title=''):
00092         """ Initialize a new instance """
00093         self.id = id
00094         self.title = title
00095         self._domain_map = OOBTree()
00096 
00097 
00098     security.declarePrivate('extractCredentials')
00099     def extractCredentials(self, request):
00100         """ Extract credentials from 'request'.
00101         """
00102         creds = {}
00103 
00104         remote_host = request.get('REMOTE_HOST', '')
00105         if remote_host:
00106             creds['remote_host'] = request.get('REMOTE_HOST', '')
00107 
00108         try:
00109             remote_address = request.getClientAddr()
00110         except AttributeError:
00111             remote_address = request.get('REMOTE_ADDR', '')
00112 
00113         if remote_host or remote_address:
00114             creds['remote_host'] = remote_host
00115             creds['remote_address'] = remote_address
00116 
00117         return creds
00118 
00119     security.declarePrivate('authenticateCredentials')
00120     def authenticateCredentials(self, credentials):
00121         """ Fulfill AuthenticationPlugin requirements """
00122         login = credentials.get('login', '')
00123         r_host = credentials.get('remote_host', '')
00124         r_address = credentials.get('remote_address', '')
00125         matches = self._findMatches(login, r_host, r_address)
00126 
00127         if len(matches) > 0:
00128             if login:
00129                 return (login, login)
00130             else:
00131                 best_match = matches[0]
00132                 u_name = best_match.get('username', 'remote')
00133                 return ( best_match.get('user_id', u_name)
00134                        , u_name
00135                        )
00136 
00137         return (None, None)
00138 
00139 
00140     security.declarePrivate( 'getRolesForPrincipal' )
00141     def getRolesForPrincipal(self, user, request=None):
00142         """ Fulfill RolesPlugin requirements """
00143         roles = []
00144 
00145         if request is None:
00146             # Without request there is no way I can do anything...
00147             return tuple(roles)
00148 
00149         uname = user.getUserName()
00150 
00151         if uname.find('Remote User') != -1:
00152             uname = ''
00153 
00154         matches = self._findMatches( uname
00155                                    , request.get('REMOTE_HOST', '')
00156                                    , request.getClientAddr()
00157                                    )
00158 
00159         # We want to grab the first match because it is the most specific
00160         if len(matches) > 0:
00161             roles = matches[0].get('roles', [])
00162 
00163         return tuple(roles)
00164 
00165 
00166     security.declarePrivate('_findMatches')
00167     def _findMatches(self, login, r_host='', r_address=''):
00168         """ Find the match """
00169         matches = []
00170 
00171         if not r_host and not r_address:
00172             return tuple(matches)
00173         
00174         all_info = list(self._domain_map.get(login, []))
00175         all_info.extend(self._domain_map.get('', []))
00176         
00177         if not r_host:
00178             try:
00179                 r_host = socket.gethostbyaddr(r_address)[0]
00180             except socket.error: 
00181                 pass
00182 
00183         if not r_address:
00184             try:
00185                 r_address = socket.gethostbyname(r_host)
00186             except socket.error :
00187                 pass
00188 
00189         if not r_host and not r_address:
00190             return tuple(matches)
00191 
00192         candidates = [r_host, r_address]
00193 
00194         for match_info in all_info:
00195             m = []
00196             m_type = match_info['match_type']
00197             m_real = match_info['match_real']
00198 
00199             if m_type == _MATCH_EQUALS:
00200                 m = [match_info for x in candidates if x == m_real]
00201             elif m_type == _MATCH_ENDSWITH:
00202                 m = [match_info for x in candidates if x.endswith(m_real)]
00203             elif m_type == _MATCH_REGEX:
00204                 m = [match_info for x in candidates if m_real.search(x)]
00205 
00206             matches.extend(m)
00207 
00208         return tuple(matches)
00209 
00210 
00211     security.declareProtected(manage_users, 'listMatchTypes')
00212     def listMatchTypes(self):
00213         """ Return a sequence of possible match types """
00214         return (_MATCH_EQUALS, _MATCH_ENDSWITH, _MATCH_REGEX)
00215 
00216 
00217     security.declareProtected(manage_users, 'listMappingsForUser')
00218     def listMappingsForUser(self, user_id=''):
00219         """ List the mappings for a specific user """
00220         result = []
00221         record = self._domain_map.get(user_id, [])
00222 
00223         for match_info in record:
00224             result.append( { 'match_type' : match_info['match_type']
00225                            , 'match_string' : match_info['match_string']
00226                            , 'match_id' : match_info['match_id']
00227                            , 'roles' : match_info['roles']
00228                            , 'username' : match_info['username']
00229                            } )
00230         
00231         return result
00232 
00233 
00234     security.declareProtected(manage_users, 'manage_addMapping')
00235     def manage_addMapping( self
00236                          , user_id=''
00237                          , match_type=''
00238                          , match_string=''
00239                          , username=''
00240                          , roles=[]
00241                          , REQUEST=None
00242                          ):
00243         """ Add a mapping for a user """
00244         msg = ''
00245 
00246         if match_type not in (_MATCH_EQUALS, _MATCH_ENDSWITH, _MATCH_REGEX):
00247             msg = 'Unknown match type %s' % match_type
00248 
00249         if not match_string:
00250             msg = 'No match string specified'
00251 
00252         if match_type == _MATCH_REGEX:
00253             try:
00254                 re.compile(match_string, re.IGNORECASE)
00255             except re.error:
00256                 msg = 'Invalid regular expression %s' % match_string
00257 
00258         if msg:
00259             if REQUEST is not None:
00260                 return self.manage_map(manage_tabs_message=msg)
00261 
00262             raise ValueError, msg
00263 
00264         record = self._domain_map.get(user_id, [])
00265         if match_type == _MATCH_REGEX:
00266             real_match = re.compile(match_string, re.IGNORECASE)
00267         else:
00268             real_match = match_string
00269 
00270         match = { 'match_type' : match_type
00271                 , 'match_string' : match_string
00272                 , 'match_real' : real_match
00273                 , 'match_id' : '%s_%s' % (user_id, str(time.time()))
00274                 , 'username' : user_id or username or 'Remote User'
00275                 , 'roles' : roles
00276                 }
00277 
00278         if match not in record:
00279             record.append(match)
00280         else:
00281             msg = 'Match already exists'
00282 
00283         self._domain_map[user_id] = record
00284 
00285         if REQUEST is not None:
00286             msg = msg or 'Match added.'
00287             if user_id:
00288                 return self.manage_map(manage_tabs_message=msg)
00289             else:
00290                 return self.manage_genericmap(manage_tabs_message=msg)
00291 
00292 
00293     security.declareProtected(manage_users, 'manage_removeMappings')
00294     def manage_removeMappings(self, user_id='', match_ids=[], REQUEST=None):
00295         """ Remove mappings """
00296         msg = ''
00297 
00298         if len(match_ids) < 1:
00299             msg = 'No matches specified'
00300 
00301         record = self._domain_map.get(user_id, [])
00302 
00303         if len(record) < 1:
00304             msg = 'No mappings for user %s' % user_id
00305 
00306         if msg:
00307             if REQUEST is not None:
00308                 return self.manage_map(manage_tabs_message=msg)
00309             else:
00310                 return
00311 
00312         to_delete = [x for x in record if x['match_id'] in match_ids]
00313 
00314         for match in to_delete:
00315             record.remove(match)
00316 
00317         self._domain_map[user_id] = record
00318 
00319         if REQUEST is not None:
00320             msg = 'Matches deleted'
00321             if user_id:
00322                 return self.manage_map(manage_tabs_message=msg)
00323             else:
00324                 return self.manage_genericmap(manage_tabs_message=msg)
00325 
00326 classImplements( DomainAuthHelper
00327                , IDomainAuthHelper
00328                , IExtractionPlugin
00329                , IAuthenticationPlugin
00330                , IRolesPlugin
00331                )
00332 
00333 InitializeClass(DomainAuthHelper)
00334