comparison roundup/xmlrpc.py @ 3828:ba6ba8d6bcc1

Initial checkin for new xmlrpc frontend.
author Stefan Seefeld <stefan@seefeld.name>
date Thu, 22 Mar 2007 02:07:44 +0000
parents
children cf6c45201980
comparison
equal deleted inserted replaced
3827:5e21117e38b2 3828:ba6ba8d6bcc1
1 #
2 # Copyright (C) 2007 Stefan Seefeld
3 # All rights reserved.
4 # Licensed to the public under the terms of the GNU LGPL (>= 2),
5 # see the file COPYING for details.
6 #
7
8
9 import base64
10 import roundup.instance
11 from roundup import hyperdb
12 from roundup.cgi.exceptions import *
13 from roundup.admin import UsageError
14 from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
15
16 class RoundupRequestHandler(SimpleXMLRPCRequestHandler):
17 """A SimpleXMLRPCRequestHandler with support for basic
18 HTTP Authentication."""
19
20 def do_POST(self):
21 """Extract username and password from authorization header."""
22
23 # Try to extract username and password from HTTP Authentication.
24 self.username = None
25 self.password = None
26 authorization = self.headers.get('authorization', ' ')
27 scheme, challenge = authorization.split(' ', 1)
28
29 if scheme.lower() == 'basic':
30 decoded = base64.decodestring(challenge)
31 self.username, self.password = decoded.split(':')
32
33 SimpleXMLRPCRequestHandler.do_POST(self)
34
35 def _dispatch(self, method, params):
36 """Inject username and password into function arguments."""
37
38 # Add username and password to function arguments
39 params = [self.username, self.password] + list(params)
40 return self.server._dispatch(method, params)
41
42
43 class RoundupRequest:
44 """Little helper class to handle common per-request tasks such
45 as authentication and login."""
46
47 def __init__(self, tracker, username, password):
48 """Open the database for the given tracker, using the given
49 username and password."""
50
51 self.tracker = tracker
52 self.db = self.tracker.open('admin')
53 try:
54 userid = self.db.user.lookup(username)
55 except KeyError: # No such user
56 raise Unauthorised, 'Invalid user.'
57 stored = self.db.user.get(userid, 'password')
58 if stored != password: # Wrong password
59 raise Unauthorised, 'Invalid user.'
60 self.db.setCurrentUser(username)
61
62 def __del__(self):
63 """Close the database, after committing any changes, if needed."""
64
65 if getattr(self, 'db'):
66 try:
67 if self.db.transactions:
68 self.db.commit()
69 finally:
70 self.db.close()
71
72
73 def get_class(self, classname):
74 """Return the class for the given classname."""
75
76 try:
77 return self.db.getclass(classname)
78 except KeyError:
79 raise UsageError, 'no such class "%s"'%classname
80
81 def props_from_args(self, cl, args):
82 """Construct a list of properties from the given arguments,
83 and return them after validation."""
84
85 props = {}
86 for arg in args:
87 if arg.find('=') == -1:
88 raise UsageError, 'argument "%s" not propname=value'%arg
89 l = arg.split('=')
90 if len(l) < 2:
91 raise UsageError, 'argument "%s" not propname=value'%arg
92 key, value = l[0], '='.join(l[1:])
93 if value:
94 try:
95 props[key] = hyperdb.rawToHyperdb(self.db, cl, None,
96 key, value)
97 except hyperdb.HyperdbValueError, message:
98 raise UsageError, message
99 else:
100 props[key] = None
101
102 return props
103
104
105 #The server object
106 class RoundupServer:
107 """The RoundupServer provides the interface accessible through
108 the Python XMLRPC mapping. All methods take an additional username
109 and password argument so each request can be authenticated."""
110
111 def __init__(self, tracker, verbose = False):
112 self.tracker = roundup.instance.open(tracker)
113 self.verbose = verbose
114
115 def list(self, username, password, classname, propname = None):
116
117 r = RoundupRequest(self.tracker, username, password)
118 cl = r.get_class(classname)
119 if not propname:
120 propname = cl.labelprop()
121 return [cl.get(id, propname) for id in cl.list()]
122
123 def display(self, username, password, designator, *properties):
124
125 r = RoundupRequest(self.tracker, username, password)
126 classname, nodeid = hyperdb.splitDesignator(designator)
127 cl = r.get_class(classname)
128 props = properties and list(properties) or cl.properties.keys()
129 props.sort()
130 return dict([(property, cl.get(nodeid, property))
131 for property in props])
132
133 def create(self, username, password, classname, *args):
134
135 r = RoundupRequest(self.tracker, username, password)
136 cl = r.get_class(classname)
137
138 # convert types
139 props = r.props_from_args(cl, args)
140
141 # check for the key property
142 key = cl.getkey()
143 if key and not props.has_key(key):
144 raise UsageError, 'you must provide the "%s" property.'%key
145
146 # do the actual create
147 try:
148 return cl.create(**props)
149 except (TypeError, IndexError, ValueError), message:
150 raise UsageError, message
151
152 def set(self, username, password, designator, *args):
153
154 r = RoundupRequest(self.tracker, username, password)
155 classname, itemid = hyperdb.splitDesignator(designator)
156 cl = r.get_class(classname)
157
158 # convert types
159 props = r.props_from_args(cl, args)
160 try:
161 cl.set(itemid, **props)
162 except (TypeError, IndexError, ValueError), message:
163 raise UsageError, message
164
165

Roundup Issue Tracker: http://roundup-tracker.org/