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

Source Code for Module Products.ZenUtils.Security

  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  import os 
 12  from random import random 
 13  from datetime import datetime 
 14   
 15  from OFS.Folder import Folder 
 16  from Products.PluggableAuthService import plugins 
 17  from Products.PluggableAuthService import interfaces 
 18  from Products.PluggableAuthService import PluggableAuthService 
 19   
 20  from zope import component 
 21  import ZPublisher.interfaces 
 22   
 23  ZENOSS_ROLES = ['ZenUser', 'ZenManager'] 
24 25 26 -def backupACLUserFolder(context):
27 timestamp = datetime.now().strftime('%Y.%d.%m-%H%M%S') 28 randomBit = int(random() * 10000) 29 backupFolderName = 'backup_acl_users_%s-%d' % (timestamp, randomBit) 30 backupFolder = Folder(backupFolderName) 31 backupFolder._setObject('acl_users', context.acl_users) 32 context._setObject(backupFolder.getId(), backupFolder) 33 context._delObject('acl_users') 34 return backupFolderName
35
36 37 -def _createInitialUser(self):
38 """ 39 Note: copied and adapted from AccessControl.User.BasicUser 40 41 If there are no users or only one user in this user folder, 42 populates from the 'inituser' file in the instance home. 43 We have to do this even when there is already a user 44 just in case the initial user ignored the setup messages. 45 We don't do it for more than one user to avoid 46 abuse of this mechanism. 47 Called only by OFS.Application.initialize(). 48 """ 49 from AccessControl.User import readUserAccessFile 50 51 plugins = self.plugins.listPlugins( 52 interfaces.plugins.IUserEnumerationPlugin) 53 # get the user count, but only for functional plugins 54 userCounts = [ len(plugin.listUserInfo()) for id, plugin in plugins if hasattr(plugin, "listUserInfo")] 55 56 if len(userCounts) <= 1: 57 info = readUserAccessFile('inituser') 58 if info: 59 import App.config 60 name, password, domains, remote_user_mode = info 61 userManagers = self.plugins.listPlugins(interfaces.plugins.IUserAdderPlugin) 62 roleManagers = self.plugins.listPlugins(interfaces.plugins.IRolesPlugin) 63 for pluginId, userPlugin in userManagers: 64 # delete user 65 try: 66 userPlugin.removeUser(name) 67 except KeyError: 68 # user doesn't exist 69 pass 70 # recreate user 71 userPlugin.doAddUser(name, password) 72 # add role 73 for pluginId, rolePlugin in roleManagers: 74 rolePlugin.assignRoleToPrincipal('Manager', name) 75 cfg = App.config.getConfiguration() 76 # now that we've loaded from inituser, let's delete the file 77 try: 78 os.remove(os.path.join(cfg.instancehome, 'inituser')) 79 except: 80 pass
81
82 83 -def createPASFolder(context):
84 # check to see if we need to monkey patch PAS to accomodate inituser files 85 pas = PluggableAuthService.PluggableAuthService 86 if not hasattr(pas, '_createInitialUser'): 87 pas._createInitialUser = _createInitialUser 88 89 # create new PAS 90 PluggableAuthService.addPluggableAuthService(context) 91 context.acl_users.title = 'PAS'
92
93 94 -def setupBasciAuthHelper(context):
95 acl = context.acl_users 96 id = 'basicAuthHelper' 97 if not hasattr(acl, id): 98 plugins.HTTPBasicAuthHelper.addHTTPBasicAuthHelper(acl, id) 99 interfaces = [] 100 physPath = '/'.join(context.getPhysicalPath()) 101 if physPath == '': 102 interfaces = ['IExtractionPlugin', 'IChallengePlugin', 103 'ICredentialsResetPlugin'] 104 elif physPath == '/zport': 105 interfaces = ['IExtractionPlugin', 'IChallengePlugin'] 106 acl.basicAuthHelper.manage_activateInterfaces(interfaces)
107
108 109 -def setupCookieHelper(context, primaryAuth=False):
110 acl = context.acl_users 111 id = 'cookieAuthHelper' 112 if not hasattr(acl, id): 113 plugins.CookieAuthHelper.addCookieAuthHelper(acl, id) 114 interfaces = [] 115 # note that we are only enabling CookieAuth for the Zenoss portal 116 # acl_users, not for the root acl_users. 117 physPath = '/'.join(context.getPhysicalPath()) 118 if physPath == '': 119 interfaces = ['IExtractionPlugin'] 120 elif physPath == '/zport': 121 interfaces = ['IExtractionPlugin', 122 'ICredentialsResetPlugin', 123 'IChallengePlugin'] 124 if primaryAuth: 125 interfaces.append('ICredentialsUpdatePlugin') 126 acl.cookieAuthHelper.manage_activateInterfaces(interfaces)
127
128 -def setupSessionHelper(context, primaryAuth=True):
129 acl = context.acl_users 130 id = 'sessionAuthHelper' 131 if not hasattr(acl, id): 132 plugins.SessionAuthHelper.manage_addSessionAuthHelper(acl, id) 133 134 interfaces = ['IExtractionPlugin', 135 'ICredentialsResetPlugin'] 136 if primaryAuth: 137 interfaces.append('ICredentialsUpdatePlugin') 138 acl.sessionAuthHelper.manage_activateInterfaces(interfaces)
139
140 -def activateCookieBasedAuthentication(context):
141 """ 142 This sets cookie authentication as the primary auth 143 mechanism. This means that the users credentials will be stored 144 encoded in a cookie. 145 """ 146 setupCookieHelper(context, primaryAuth=True) 147 setupSessionHelper(context, primaryAuth=False)
148
149 -def activateSessionBasedAuthentication(context):
150 """ 151 Stores the user credentials in the session and the token is sent 152 to the server. The user will be forced to re-login when zope 153 restarts or the session times out. 154 """ 155 setupCookieHelper(context, primaryAuth=False) 156 setupSessionHelper(context, primaryAuth=True)
157
158 -def setupRoleManager(context):
159 acl = context.acl_users 160 id = 'roleManager' 161 if not hasattr(acl, id): 162 plugins.ZODBRoleManager.addZODBRoleManager(acl, id) 163 acl.roleManager.manage_activateInterfaces(['IRolesPlugin', 164 'IRoleEnumerationPlugin', 'IRoleAssignerPlugin']) 165 # setup roles 166 for role in ZENOSS_ROLES: 167 try: 168 acl.roleManager.addRole(role) 169 except KeyError: 170 # that role already exists 171 pass
172
173 174 -def setupUserManager(context):
175 acl = context.acl_users 176 id = 'userManager' 177 if not hasattr(acl, id): 178 plugins.ZODBUserManager.addZODBUserManager(acl, id) 179 acl.userManager.manage_activateInterfaces(['IAuthenticationPlugin', 180 'IUserEnumerationPlugin', 'IUserAdderPlugin'])
181
182 183 -def setupTypeSniffer(context):
184 acl = context.acl_users 185 id = 'requestTypeSniffer' 186 if not hasattr(acl, id): 187 plugins.RequestTypeSniffer.addRequestTypeSnifferPlugin(acl, id) 188 acl.requestTypeSniffer.manage_activateInterfaces(['IRequestTypeSniffer'])
189
190 191 -def setupProtocolChooser(context):
192 acl = context.acl_users 193 id = 'protocolChooser' 194 if not hasattr(acl, id): 195 plugins.ChallengeProtocolChooser.addChallengeProtocolChooserPlugin(acl, 196 id) 197 acl.protocolChooser.manage_activateInterfaces([ 198 'IChallengeProtocolChooser']) 199 protocolMapping = {} 200 # set up non-Browser protocols to use HTTP BasicAuth 201 physPath = '/'.join(context.getPhysicalPath()) 202 if physPath == '': 203 protocolMapping = { 204 'Browser': ['http'], 205 'FTP': ['http'], 206 'WebDAV': ['http'], 207 'XML-RPC': ['http'], 208 } 209 elif physPath == '/zport': 210 protocolMapping = { 211 'FTP': ['http'], 212 'WebDAV': ['http'], 213 'XML-RPC': ['http'], 214 } 215 # we don't want to hard-code plugin names here, so let's do a lookup 216 icookie = plugins.CookieAuthHelper.ICookieAuthHelper 217 ichallenge = interfaces.plugins.IChallengePlugin 218 challenge = [ p for id, p in acl.plugins.listPlugins(ichallenge) ] 219 # valid cooike auth plugins 220 cookiePlugins = [ p for p in challenge if icookie.providedBy(p) ] 221 # we want to move the cookie auth instance above the basic auth listing so 222 # that it is accessed first and we can keep 'Browser' set to any; for 223 # now, let's just get the first match and use that one (there should 224 # really only be one...) 225 cookie = cookiePlugins[0] 226 index = challenge.index(cookie) 227 for i in xrange(index): 228 acl.plugins.movePluginsUp(ichallenge, [cookie.id]) 229 acl.protocolChooser.manage_updateProtocolMapping(protocolMapping)
230
231 232 -def setupPASFolder(context):
233 setupBasciAuthHelper(context) 234 setupCookieHelper(context) 235 setupSessionHelper(context) 236 setupRoleManager(context) 237 setupUserManager(context) 238 setupTypeSniffer(context) 239 # this one has to go last in case any of the protocol mappings need to make 240 # reference to an already-installed plugin 241 setupProtocolChooser(context)
242
243 244 -def replaceACLWithPAS(context, deleteBackup=False):
245 # archive the old "User Folder" 246 backupId = backupACLUserFolder(context) 247 248 # create a new PAS acl_users 249 createPASFolder(context) 250 setupPASFolder(context) 251 252 # set up some convenience vars 253 orig = getattr(context, backupId).acl_users 254 acl = context.acl_users 255 256 # migrate the old user information over to the PAS 257 for u in orig.getUsers(): 258 user, password, domains, roles = (u.name, u.__, u.domains, u.roles) 259 acl.userManager.doAddUser(user, password) 260 for role in roles: 261 acl.roleManager.assignRoleToPrincipal(role, user) 262 # initialize UserSettings for each user 263 try: 264 dmd = context.getPhysicalRoot().zport.dmd 265 dmd.ZenUsers.getUserSettings(user) 266 except AttributeError: 267 # no dmd, or no ZenUsers 268 pass 269 270 # delete backup? 271 if deleteBackup: 272 context._delObject(backupId)
273
274 275 -def migratePAS(context):
276 # check to see if the current acl_users is a PAS instance or not 277 newModule = 'Products.PluggableAuthService.PluggableAuthService' 278 try: 279 acl = context.acl_users 280 # if there's an acl_users object, let's see if theres a login_form 281 # attribute; if there is, we need to delete it 282 if (hasattr(acl, 'cookieAuthHelper') 283 and hasattr(acl.cookieAuthHelper, 'login_form')): 284 acl.cookieAuthHelper._delObject('login_form') 285 except AttributeError: 286 createPASFolder(context) 287 acl = context.acl_users 288 289 if acl.__module__ != newModule: 290 replaceACLWithPAS(context) 291 else: 292 # check to see if there are any missing attributes; we have to make the 293 # dir() call twice, because (when testing in the dmd) the 'plugins' 294 # attribute doesn't show up on the first call. 295 dummy = dir(acl) 296 full = set(dir(acl)) 297 needed = set(['_createInitialUser', 'plugins']) 298 # if any of 'needed' are missing, the PAS has to be recreated 299 if not full.issuperset(needed): 300 backupId = backupACLUserFolder(context) 301 backup = context._getOb(backupId) 302 createPASFolder(context) 303 # now that we have a monkey-patched acl_users, restore the plugins 304 for itemId in backup.objectIds(): 305 acl._setObject(itemId, backup._getOb(itemId)) 306 # delete the (empty) backup 307 context._delObject(backupId) 308 # the next function calls all the setup functions, each of which do an 309 # attriibute check and installs anything that's missing 310 setupPASFolder(context)
311 312 @component.adapter(ZPublisher.interfaces.IPubEnd)
313 -def secureSessionCookie(event):
314 """Zope session cookie should only accesible from the server side""" 315 if '_ZopeId' in event.request.response.cookies and 'http_only' not in event.request.response.cookies['_ZopeId']: 316 event.request.response.cookies['_ZopeId']['http_only'] = True
317