root/RPCAuth/current/RPCAuth.py

Revision 1061 (checked in by mylan, 10 months ago)

Load RPCAuth-0.4/ into RPCAuth/current.

  • Property svn:eol-style set to native
Line 
1 """
2 RPCAuth is a pretraversal hook that checks for xmlrpc calls and
3 tries to extract a username and password from the arguements passed
4 """
5
6 import sys
7 from base64 import encodestring
8 from urllib import quote, unquote
9 from os import path
10 import string
11 from DateTime import DateTime
12 from utils import SimpleItemWithProperties
13 from AccessControl import ClassSecurityInfo, Permissions
14 from ZPublisher import BeforeTraverse
15 import Globals
16 from zLOG import LOG, ERROR
17 from App.Common import package_home
18 from ZPublisher.HTTPRequest import HTTPRequest
19
20 # Constants.
21 ATTEMPT_DISABLED = -1
22 ATTEMPT_NONE = 0
23 ATTEMPT_LOGIN = 1
24 ATTEMPT_CONT = 2
25
26 ModifyRPCAuth = 'Change RPC Auth'
27
28
29 class 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
201 Globals.InitializeClass(RPCAuth)
202
203 def 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
218 def 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 browser.