""" RPCAuth is a pretraversal hook that checks for xmlrpc calls and tries to extract a username and password from the arguements passed """ import sys from base64 import encodestring from urllib import quote, unquote from os import path import string from DateTime import DateTime from utils import SimpleItemWithProperties from AccessControl import ClassSecurityInfo, Permissions from ZPublisher import BeforeTraverse import Globals from zLOG import LOG, ERROR from App.Common import package_home from ZPublisher.HTTPRequest import HTTPRequest # Constants. ATTEMPT_DISABLED = -1 ATTEMPT_NONE = 0 ATTEMPT_LOGIN = 1 ATTEMPT_CONT = 2 ModifyRPCAuth = 'Change RPC Auth' class RPCAuth(SimpleItemWithProperties): ''' Reads xmlrpc args during traversal and simulates the HTTP authentication headers. ''' meta_type = 'RPC Auth' security = ClassSecurityInfo() security.declareProtected(ModifyRPCAuth, 'manage_editProperties', 'manage_changeProperties') security.declareProtected(Permissions.view_management_screens, 'manage_propertiesForm') _properties = ({'id':'uname_arg', 'type': 'string', 'mode':'w', 'label':'User name arguement prefix'}, {'id':'pword_arg', 'type': 'string', 'mode':'w', 'label':'User password arguement prefix'}, {'id':'remove_args','type':'boolean','mode':'w', 'label':'Remove password username arguements'}, ) def __init__(self): self.remove_args = 1 self.uname_arg = 'zid' self.pword_arg = 'zpw' self._authProviders = {'absglob':{},'abs':{},'rel':{}} security.declareProtected(ModifyRPCAuth,'addAuthProvider') def addAuthProvider(self,objectPaths,function): """takes a list of paths and a function as an arguements""" aps = getattr(self,'_authProviders',{'absglob':{},'abs':{},'rel':{}}) for name in list(objectPaths): if name[-1] == '/' and name[0] == '/': # absolute glob aps['absglob'][tuple(string.split(name,'/')[1:-1])] = function if name[0] == '/': # absolute url aps['abs'][tuple(string.split(name,'/')[1:])] = function if name[0] != '/' and name[-1] != '/' and '/' in name: # relative aps['rel'][tuple(string.split(name,'/'))] = function self._authProviders = aps self._p_changed = 1 security.declareProtected(ModifyRPCAuth,'listAuthProviders') def listAuthProviders(self): """a list of objects with auth providers""" aps = getattr(self,'_authProviders',{'absglob':{},'abs':{},'rel':{}}) return aps['absglob'].keys()+\ aps['abs'].keys()+\ aps['rel'].keys() security.declareProtected(ModifyRPCAuth,'removeAuthProvider') def removeAuthProvider(self,objectPaths): """remove auth providers objectPaths should be a list""" for objectPath in list(objectPaths): if type(objectPath) == type('s'): objectPath = string.split(objectPath,'/') while '' in objectPath: del objectPath[objectPath.index('')] tuple(objectPath) if objectPath in self._authProviders['absglob'].keys(): del self._authProviders['absglob'][objectPath] elif objectPath in self._authProviders['abs'].keys(): del self._authProviders['abs'][objectPath] elif objectPath in self._authProviders['rel'].keys(): del self._authProviders['rel'][objectPath] def _identify(self,arg_tuple): arg_list = list(arg_tuple) zusername,zpassword = None,None try: for item in arg_tuple: #use the tuple here so we can delete items as we iterate if type(item) == type('') and len(item) > len(self.uname_arg) and item[:len(self.uname_arg)] == self.uname_arg: zusername = item[len(self.uname_arg):] del arg_list[arg_list.index(item)] continue if type(item) == type('') and len(item) > len(self.uname_arg) and item[:len(self.pword_arg)] == self.pword_arg: zpassword = item[len(self.pword_arg):] del arg_list[arg_list.index(item)] if zusername and zpassword: arg_tuple = tuple(arg_list) return zusername,zpassword,arg_tuple else: return None,None,None except 'none': return None def _registryIdenitfy(self,targetPath): targetPath = list(targetPath) targetPath.reverse() targetPath = [a.encode('utf-8') for a in targetPath if type(a) == type(u'')] targetPath = tuple(targetPath) # first check absolute path cuz it's easy if targetPath in self._authProviders['abs'].keys(): return self._authProviders['abs'][targetPath] # check relative paths relPath = targetPath[:] while len(relPath) > 1: if relPath in self._authProviders['rel'].keys(): return self._authProviders['rel'][relPath] relPath= relPath[1:] # check absolute globs globPath = targetPath[:-1] while len(globPath) > 1: if globPath in self._authProviders['absglob'].keys(): return self._authProviders['absglob'][globPath] globPath=globPath[:-1] return None security.declarePrivate('modifyRequest') def modifyRequest(self, req, resp): # Returns flags indicating what the user is trying to do. if req.__class__ is not HTTPRequest: return ATTEMPT_DISABLED if not req._auth: # Attempt to log in. targetPath = tuple(req['TraversalRequestNameStack']) authProvider = self._registryIdenitfy(targetPath) if authProvider: name,pw,arg_tuple = authProvider(req.args) else: name,pw,arg_tuple = self._identify(req.args) if name and pw: ac = encodestring('%s:%s' % (name, pw)) req._auth = 'basic %s' % ac resp._auth = 1 if self.remove_args or authProvider: # if we set remove_args or an authProvider is used req.args = arg_tuple return ATTEMPT_LOGIN return ATTEMPT_NONE def __call__(self, container, req): '''The __before_publishing_traverse__ hook.''' resp = self.REQUEST['RESPONSE'] attempt = self.modifyRequest(req, resp) def _cleanupResponse(self): resp = self.REQUEST['RESPONSE'] try: del resp.unauthorized except: pass try: del resp._unauthorized except: pass return resp security.declarePrivate('unauthorized') def unauthorized(self): resp.unauthorized() def _unauthorized(self): resp._unauthorized() # Installation and removal of traversal hooks. def manage_beforeDelete(self, item, container): if item is self: handle = self.meta_type + '/' + self.getId() BeforeTraverse.unregisterBeforeTraverse(container, handle) def manage_afterAdd(self, item, container): if item is self: handle = self.meta_type + '/' + self.getId() container = container.this() nc = BeforeTraverse.NameCaller(self.getId()) BeforeTraverse.registerBeforeTraverse(container, nc, handle) Globals.InitializeClass(RPCAuth) def manage_addRAForm(self): "this is a form to get the id" return """ Setup RPC Auth Please type the id of the RPC Auth:

""" def manage_addRPCAuth(self, id, REQUEST=None): ' ' ob = RPCAuth() ob.id = id self._setObject(id, ob) if REQUEST is not None: return self.manage_main(self, REQUEST)