1
2
3
4
5
6
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
41
42 pas = PluggableAuthService.PluggableAuthService
43 if not hasattr(pas, '_createInitialUser'):
44 pas._createInitialUser = _createInitialUser
45
46
47
48
49 _originalResetCredentials = pas.resetCredentials
54 pas.resetCredentials = _resetCredentials
55
56
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
107
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
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
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