Package Products :: Package ZenUtils :: Package patches :: Module pasmonkey |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.patches.pasmonkey

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  '''
     12  This module contains monkey patches we needed to make to PAS when we switched
     13  from native ZODB-managed authentication to pluggable authentication.
     14
     15  This module needs to be imported by ZenUtils/__init__.py.
     16
     17  Related tickets:
     18    http://dev.zenoss.org/trac/ticket/379
     19    http://dev.zenoss.org/trac/ticket/402
     20    http://dev.zenoss.org/trac/ticket/443
     21    http://dev.zenoss.org/trac/ticket/1042
     22    http://dev.zenoss.org/trac/ticket/4225
     23    http://jira.zenoss.com/jira/browse/ZEN-110
     24  '''
     25
     26  import urllib
     27  import urlparse
     28  from uuid import uuid1
     29  from cgi import parse_qs
     30  from Acquisition import aq_base
     31  from AccessControl.SpecialUsers import emergency_user
     32  from zope.event import notify
     33  from Products.PluggableAuthService import PluggableAuthService
     34  from Products.PluggableAuthService.plugins import CookieAuthHelper
     35  from Products.PluggableAuthService.interfaces.authservice import _noroles
     36  from Products.ZenMessaging.audit import audit
     37  from Products.ZenUtils.events import UserLoggedInEvent, UserLoggedOutEvent
     38  from Products.ZenUtils.Security import _createInitialUser
     39
     40  # monkey patch PAS to allow inituser files, but check to see if we need to
     41  # actually apply the patch, first -- support may have been added at some point
     42  pas = PluggableAuthService.PluggableAuthService
     43  if not hasattr(pas, '_createInitialUser'):
     44      pas._createInitialUser =  _createInitialUser
     45
     46  # Monkey patch PAS to monitor logouts (credential resets).
     47  # Have to check the request object to determine when we have an actual
     48  # logout instead of 'fake' logout (like the logout of the anonymous user)
     49  _originalResetCredentials = pas.resetCredentials
     50 -def _resetCredentials(self, request, response=None):
     51      audit("UI.Authentication.Logout")
     52      notify(UserLoggedOutEvent(self.zport.dmd.ZenUsers.getUserSettings()))
     53      _originalResetCredentials(self, request, response)
     54  pas.resetCredentials = _resetCredentials
     55
     56  # Monkey patch PAS to audit log successful and failed login attempts
     57 -def validate(self, request, auth='', roles=_noroles):
     58      """
     59      Here is a run down of how this method is called and where it ends up returning
     60      in various login situations.
     61
     62      Failure (admin, local, LDAP, and Active Directory)
     63         is_top=0, user_ids=[], name=login, if not is_top: return None (outside loop)
     64         is_top=1, user_ids=[], name=login, return anonymous
     65
     66     Success (admin)
     67        is_top=0, user_ids=[], name=login, if not is_top: return (outside loop)
     68        is_top=1, user_ids=[('admin', 'admin')], name=login, if self._authorizeUser(...): return user
     69
     70      Success (local, LDAP, and Active Directory)
     71         is_top=0, user_ids=[('username', 'username')], name=login, if self._authorizeUser(...): return user
     72      """
     73      plugins = self._getOb( 'plugins' )
     74      is_top = self._isTop()
     75      user_ids = self._extractUserIds(request, plugins)
     76      accessed, container, name, value = self._getObjectContext(request['PUBLISHED'], request)
     77      ipaddress = getattr(request, '_client_addr', 'Unknown')
     78      for user_id, login in user_ids:
     79          user = self._findUser(plugins, user_id, login, request=request)
     80          if aq_base(user) is emergency_user:
     81              if is_top:
     82                  return user
     83              else:
     84                  return None
     85
     86          if self._authorizeUser(user, accessed, container, name, value, roles):
     87              if name == 'login':
     88                  audit('UI.Authentication.Valid', ipaddress=ipaddress)
     89                  notify(UserLoggedInEvent(self.zport.dmd.ZenUsers.getUserSettings()))
     90              return user
     91
     92      if not is_top:
     93          return None
     94
     95      anonymous = self._createAnonymousUser(plugins)
     96      if self._authorizeUser(anonymous, accessed, container, name, value, roles):
     97          if name == 'login':
     98              username_ = request.form.get('__ac_name', 'Unknown')
     99              audit('UI.Authentication.Failed', username_=username_, ipaddress=ipaddress)
    100          return anonymous
    101
    102      return None
    103
    104  pas.validate = validate
    105
    106  # monkey patches for the PAS login form
    107
    108 -def manage_afterAdd(self, item, container):
    109      """We don't want CookieAuthHelper setting the login attribute, we we'll
    110      override manage_afterAdd().
    111
    112      For now, the only thing that manage_afterAdd does is set the login_form
    113      attribute, but we will need to check this after every upgrade of the PAS.
    114      """
    115      pass
    116
    117  CookieAuthHelper.CookieAuthHelper.manage_afterAdd = manage_afterAdd
    118
    119 -def login(self):
    120      """
    121      Set a cookie and redirect to the url that we tried to
    122      authenticate against originally.
    123
    124      FIXME - I don't think we need this any more now that the EULA is gone -EAD
    125      """
    126
    127      request = self.REQUEST
    128      response = request['RESPONSE']
    129
    130      login = request.get('__ac_name', '')
    131      password = request.get('__ac_password', '')
    132      submitted = request.get('submitted', '')
    133
    134      pas_instance = self._getPAS()
    135
    136      if pas_instance is not None:
    137          pas_instance.updateCredentials(request, response, login, password)
    138
    139      came_from = request.form.get('came_from') or ''
    140      if came_from:
    141          parts = urlparse.urlsplit(came_from)
    142          querydict = parse_qs(parts[3])
    143          querydict.pop('terms', None)
    144          if 'submitted' not in querydict.keys():
    145              querydict['submitted'] = submitted
    146          newqs = urllib.urlencode(querydict, doseq=True)
    147          parts = parts[:3] + (newqs,) + parts[4:]
    148          came_from = urlparse.urlunsplit(parts)
    149      else:
    150          submittedQs = 'submitted=%s' % submitted
    151          came_from = '/zport/dmd?%s' % submittedQs
    152      if not self.dmd.acceptedTerms:
    153          url = "%s/zenoss_terms/?came_from=%s" % (
    154                      self.absolute_url(), urllib.quote(came_from))
    155      else:
    156          url = came_from
    157
    158      if self.dmd.uuid is None:
    159          self.dmd.uuid = str(uuid1())
    160      return response.redirect(url)
    161
    162  CookieAuthHelper.CookieAuthHelper.login = login
    163
    164
    165 -def termsCheck(self):
    166      """ Check to see if the user has accepted the Zenoss terms.
    167      """
    168      request = self.REQUEST
    169      response = request['RESPONSE']
    170
    171      acceptStatus = request.form.get('terms') or ''
    172      url = request.form.get('came_from') or self.absolute_url()
    173
    174      if acceptStatus != 'Accept':
    175          self.resetCredentials(request, response)
    176          if '?' in url:
    177              url += '&'
    178          else:
    179              url += '?'
    180          url += 'terms=Decline'
    181      else:
    182          self.dmd.acceptedTerms = True
    183          self.dmd.uuid = str(uuid1())
    184      return response.redirect(url)
    185
    186  CookieAuthHelper.CookieAuthHelper.termsCheck = termsCheck
    187