source: products/RPCAuth/current/RPCAuth.py

Last change on this file was 22, checked in by myroslav, 14 years ago

Tagging release

  • Property svn:eol-style set to native
File size: 8.2 KB
Line 
1"""
2RPCAuth is a pretraversal hook that checks for xmlrpc calls and
3tries to extract a username and password from the arguements passed
4"""
5
6import sys
7from base64 import encodestring
8from urllib import quote, unquote
9from os import path
10import string
11from DateTime import DateTime
12from utils import SimpleItemWithProperties
13from AccessControl import ClassSecurityInfo, Permissions
14from ZPublisher import BeforeTraverse
15import Globals
16from zLOG import LOG, ERROR
17from App.Common import package_home
18from ZPublisher.HTTPRequest import HTTPRequest
19
20# Constants.
21ATTEMPT_DISABLED = -1
22ATTEMPT_NONE = 0
23ATTEMPT_LOGIN = 1
24ATTEMPT_CONT = 2
25
26ModifyRPCAuth = 'Change RPC Auth'
27
28
29class RPCAuth(SimpleItemWithProperties):
30    '''
31    Reads xmlrpc args during traversal and simulates the HTTP
32    authentication headers.
33    '''
34    meta_type = 'RPC Auth'
35    security = ClassSecurityInfo()
36    security.declareProtected(ModifyRPCAuth,
37                              'manage_editProperties',
38                              'manage_changeProperties')
39    security.declareProtected(Permissions.view_management_screens,
40                              'manage_propertiesForm')
41
42
43    _properties = ({'id':'uname_arg', 'type': 'string', 'mode':'w',
44                    'label':'User name arguement prefix'},
45                   {'id':'pword_arg', 'type': 'string', 'mode':'w',
46                    'label':'User password arguement prefix'},
47                   {'id':'remove_args','type':'boolean','mode':'w',
48                    'label':'Remove password username arguements'},
49                   )
50
51    def __init__(self):
52        self.remove_args = 1
53        self.uname_arg = 'zid'
54        self.pword_arg = 'zpw'
55        self._authProviders = {'absglob':{},'abs':{},'rel':{}}
56
57
58    security.declareProtected(ModifyRPCAuth,'addAuthProvider')
59    def addAuthProvider(self,objectPaths,function):
60        """takes a list of paths and a function as an arguements"""
61        aps = getattr(self,'_authProviders',{'absglob':{},'abs':{},'rel':{}})
62        for name in list(objectPaths):
63            if name[-1] == '/' and name[0] == '/': # absolute glob
64                aps['absglob'][tuple(string.split(name,'/')[1:-1])] = function
65            if name[0] == '/':  # absolute url
66                aps['abs'][tuple(string.split(name,'/')[1:])] = function
67            if name[0] != '/' and name[-1] != '/' and '/' in name: # relative
68                aps['rel'][tuple(string.split(name,'/'))] = function
69        self._authProviders = aps
70        self._p_changed = 1
71
72    security.declareProtected(ModifyRPCAuth,'listAuthProviders')
73    def listAuthProviders(self):
74        """a list of objects with auth providers"""
75        aps = getattr(self,'_authProviders',{'absglob':{},'abs':{},'rel':{}})
76        return aps['absglob'].keys()+\
77               aps['abs'].keys()+\
78               aps['rel'].keys()
79
80    security.declareProtected(ModifyRPCAuth,'removeAuthProvider')
81    def removeAuthProvider(self,objectPaths):
82        """remove auth providers objectPaths should be a list"""
83        for objectPath in list(objectPaths):
84                if type(objectPath) == type('s'):
85                    objectPath = string.split(objectPath,'/')
86                    while '' in objectPath: del objectPath[objectPath.index('')]
87                    tuple(objectPath)
88                if objectPath in self._authProviders['absglob'].keys():
89                    del self._authProviders['absglob'][objectPath]
90                elif objectPath in self._authProviders['abs'].keys():
91                    del self._authProviders['abs'][objectPath]
92                elif objectPath in self._authProviders['rel'].keys():
93                    del self._authProviders['rel'][objectPath]
94
95
96    def _identify(self,arg_tuple):
97        arg_list = list(arg_tuple)
98        zusername,zpassword = None,None
99        try:
100            for item in arg_tuple: #use the tuple here so we can delete items as we iterate
101                if type(item) == type('') and len(item) > len(self.uname_arg) and item[:len(self.uname_arg)] == self.uname_arg:
102                    zusername = item[len(self.uname_arg):]
103                    del arg_list[arg_list.index(item)]
104                    continue
105                if type(item) == type('') and len(item) > len(self.uname_arg) and item[:len(self.pword_arg)] == self.pword_arg:
106                    zpassword = item[len(self.pword_arg):]
107                    del arg_list[arg_list.index(item)]
108            if zusername and zpassword:
109                arg_tuple = tuple(arg_list)
110                return zusername,zpassword,arg_tuple
111            else:
112                return None,None,None
113        except 'none':
114            return None
115
116    def _registryIdenitfy(self,targetPath):
117        targetPath = list(targetPath)
118        targetPath.reverse()
119        targetPath = [a.encode('utf-8') for a in targetPath
120                      if type(a) == type(u'')]
121        targetPath = tuple(targetPath)
122        # first check absolute path cuz it's easy
123        if targetPath in self._authProviders['abs'].keys():
124            return self._authProviders['abs'][targetPath]
125        # check relative paths
126        relPath = targetPath[:]
127        while len(relPath) > 1:
128            if relPath in self._authProviders['rel'].keys():
129                return self._authProviders['rel'][relPath]
130            relPath= relPath[1:]
131        # check absolute globs
132        globPath = targetPath[:-1]
133        while len(globPath) > 1:
134            if globPath in self._authProviders['absglob'].keys():
135                return self._authProviders['absglob'][globPath]
136            globPath=globPath[:-1]
137        return None
138
139    security.declarePrivate('modifyRequest')
140    def modifyRequest(self, req, resp):
141        # Returns flags indicating what the user is trying to do.
142
143        if req.__class__ is not HTTPRequest:
144            return ATTEMPT_DISABLED
145
146        if not req._auth:
147                # Attempt to log in.
148                targetPath = tuple(req['TraversalRequestNameStack'])
149                authProvider = self._registryIdenitfy(targetPath)
150                if authProvider:
151                    name,pw,arg_tuple = authProvider(req.args)
152                else:
153                    name,pw,arg_tuple = self._identify(req.args)
154                if name and pw:
155                    ac = encodestring('%s:%s' % (name, pw))
156                    req._auth = 'basic %s' % ac
157                    resp._auth = 1
158                    if self.remove_args or authProvider: # if we set remove_args or an authProvider is used
159                        req.args = arg_tuple
160                    return ATTEMPT_LOGIN
161        return ATTEMPT_NONE
162
163    def __call__(self, container, req):
164        '''The __before_publishing_traverse__ hook.'''
165        resp = self.REQUEST['RESPONSE']
166        attempt = self.modifyRequest(req, resp)
167
168
169    def _cleanupResponse(self):
170        resp = self.REQUEST['RESPONSE']
171        try: del resp.unauthorized
172        except: pass
173        try: del resp._unauthorized
174        except: pass
175        return resp
176
177
178
179    security.declarePrivate('unauthorized')
180    def unauthorized(self):
181        resp.unauthorized()
182
183    def _unauthorized(self):
184        resp._unauthorized()
185
186
187    # Installation and removal of traversal hooks.
188
189    def manage_beforeDelete(self, item, container):
190        if item is self:
191            handle = self.meta_type + '/' + self.getId()
192            BeforeTraverse.unregisterBeforeTraverse(container, handle)
193
194    def manage_afterAdd(self, item, container):
195        if item is self:
196            handle = self.meta_type + '/' + self.getId()
197            container = container.this()
198            nc = BeforeTraverse.NameCaller(self.getId())
199            BeforeTraverse.registerBeforeTraverse(container, nc, handle)
200
201Globals.InitializeClass(RPCAuth)
202
203def manage_addRAForm(self):
204        "this is a form to get the id"
205        return """<html>
206        <head>
207           <title>Setup RPC Auth</title>
208        </head>
209        <body>
210        Please type the id of the RPC Auth:<br>
211            <form name="form" action="manage_addRPCAuth"><br>
212            <input type="text" name="id"><br>
213            <input type="submit" value="add">
214            </form>
215        </body>
216        </html>"""
217
218def manage_addRPCAuth(self, id, REQUEST=None):
219    ' '
220    ob = RPCAuth()
221    ob.id = id
222    self._setObject(id, ob)
223    if REQUEST is not None:
224        return self.manage_main(self, REQUEST)
Note: See TracBrowser for help on using the repository browser.