Mercurial > p > roundup > code
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 |
