Mercurial > p > roundup > code
view doc/xmlrpc.txt @ 7853:03c1b7ae3a68
issue2551328/issue2551264 unneeded next link and total_count incorrect
Fix: issue2551328 - REST results show next link if number of
results is a multiple of page size. (Found by members of
team 3 in the UMass-Boston CS682 Spring 2024 class.)
issue2551264 - REST X-Total-Count header and @total_size
count incorrect when paginated
These issues arose because we retrieved the exact number of rows
from the database as requested by the user using the @page_size
parameter. With this changeset, we retrieve up to 10 million + 1
rows from the database. If the total number of rows exceeds 10
million, we set the total_count indicators to -1 as an invalid
size. (The max number of requested rows (default 10 million +1)
can be modified by the admin through interfaces.py.)
By retrieving more data than necessary, we can calculate the
total count by adding @page_index*@page_size to the number of
rows returned by the query.
Furthermore, since we return more than @page_size rows, we can
determine the existence of a row at @page_size+1 and use that
information to determine if a next link should be
provided. Previously, a next link was returned if @page_size rows
were retrieved.
This change does not guarantee that the user will get @page_size
rows returned. Access policy filtering occurs after the rows are
returned, and discards rows inaccessible by the user.
Using the current @page_index/@page_size it would be difficult to
have the roundup code refetch data and make sure that a full
@page_size set of rows is returned. E.G. @page_size=100 and 5 of
them are dropped due to access restrictions. We then fetch 10
items and add items 1-4 and 6 (5 is inaccessible). There is no
way to calculate the new database offset at:
@page_index*@page_size + 6 from the URL. We would need to add an
@page_offset=6 or something.
This could work since the client isn't adding 1 to @page_index to
get the next page. Thanks to HATEOAS, the client just uses the
'next' url. But I am not going to cross that bridge without a
concrete use case.
This can also be handled client side by merging a short response
with the next response and re-paginating client side.
Also added extra index markers to the docs to highlight use of
interfaces.py.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Mon, 01 Apr 2024 09:57:16 -0400 |
| parents | e34b69d75ff7 |
| children | 57325fea9982 |
line wrap: on
line source
.. meta:: :description: Documentation on the XMLRPC interface to the Roundup Issue Tracker. Includes sample clients. .. index:: triple: api; xml; remote procedure call pair: api; xmlrpc ========================= XML-RPC access to Roundup ========================= .. contents:: :local: Introduction ============ Version 1.4 of Roundup includes an XML-RPC frontend for remote access. The XML-RPC interface allows a limited subset of commands similar to those found in local `roundup-admin` tool. By default XML-RPC is accessible from ``/xmlrpc`` endpoint: http://username:password@localhost:8000/xmlrpc For demo tracker the URL would be: http://localhost:8917/demo/xmlrpc Enabling XML-RPC ================ There are two ways to run the XML-RPC interface: through roundup itself stand alone roundup-xmlrpc-server Through Roundup --------------- The XML-RPC service is available from the roundup HTTP server under /xmlrpc. To enable this set ``enable_xmlrpc`` to ``yes`` in the ``[web]`` section of the ``config.ini`` file in your tracker. Each user that needs access must include the "Xmlrpc Access" role. To add this new permission to the "User" role you should change your schema.py to add:: db.security.addPermissionToRole('User', 'Xmlrpc Access') This is usually included near where other permissions like "Web Access" or "Email Access" are assigned. Standalone roundup-xmlrpc-server -------------------------------- Using Roundup to access the xmlrpc interface is preferred. Roundup provides better control over who can use the interface. The Roundup XML-RPC standalone server must be started before remote clients can access the tracker via XML-RPC. ``roundup-xmlrpc-server`` is installed in the scripts directory alongside ``roundup-server`` and ``roundup-admin``. When invoked, the location of the tracker instance must be specified. roundup-xmlrpc-server -i ``/path/to/tracker`` The default port is ``8000``. An alternative port can be specified with the ``--port`` switch. Security Consideration ====================== Both the standalone and embedded roundup XML endpoints used the default python XML parser. This parser is know to have security issues. For details see: https://pypi.org/project/defusedxml/. You may wish to use the rest interface which doesn't have the same issues. Patches with tests to roundup to use defusedxml are welcome. .. caution:: The current standalone ``roundup-xmlrpc-server`` implementation does not support SSL. This means that usernames and passwords will be passed in cleartext unless the server is proxied behind another server (such as Apache or lighttpd) that provides SSL. Rate Limiting Failed Logins --------------------------- See the `rest documentation <rest.html#rate-limiting-api-failed-logins>`_ for rate limiting failed logins on the API. There is no login rate limiting for the standalone roundup-xmlrpc-server. Login rate limiting is only for the ``/xmlrpc`` endpoint when the Roundup server is used. The XML-RPC uses the same method as the REST API. Rate limiting is shared between the XMLRPC and REST APIs. Client API ========== The server currently implements seven methods/commands. Each method requires that the user provide a username and password in the HTTP authorization header in order to authenticate the request against the tracker. .. table:: :class: valign-top ======= =================================================================== Command Description ======= =================================================================== schema Fetch tracker schema. list arguments: *classname, [property_name]* List all elements of a given ``classname``. If ``property_name`` is specified, that is the property that will be displayed for each element. If ``property_name`` is not specified the default label property will be used. display arguments: *designator, [property_1, ..., property_N]* Display a single item in the tracker as specified by ``designator`` (e.g. issue20 or user5). The default is to display all properties for the item. Alternatively, a list of properties to display can be specified. create arguments: *classname, arg_1 ... arg_N* Create a new instance of ``classname`` with ``arg_1`` through ``arg_N`` as the values of the new instance. The arguments are name=value pairs (e.g. ``status='3'``). set arguments: *designator, arg_1 ... arg_N* Set the values of an existing item in the tracker as specified by ``designator``. The new values are specified in ``arg_1`` through ``arg_N``. The arguments are name=value pairs (e.g. ``status='3'``). lookup arguments: *classname, key_value* looks up the key_value for the given class. The class needs to have a key and the user needs search permission on the key attribute and id for the given classname. filter arguments: *classname, list or None, attributes* ``list`` is a list of ids to filter. It can be set to None to run filter over all values (requires ``allow_none=True`` when instantiating the ServerProxy). The ``attributes`` are given as a dictionary of name value pairs to search for. See also :ref:`query-tracker`. ======= =================================================================== Sample Python Client ==================== This client will work if you turn off the x-requested-with header and the only CSRF header check you require is the HTTP host header:: >>> import xmlrpclib >>> roundup_server = xmlrpclib.ServerProxy('http://admin:admin@localhost:8917/demo/xmlrpc', allow_none=True) >>> roundup_server.schema() {'user': [['username', '<roundup.hyperdb.String>'], ...], 'issue': [...]} >>> roundup_server.list('user') ['admin', 'anonymous', 'demo'] >>> roundup_server.list('issue', 'id') ['1'] >>> roundup_server.display('issue1') {'assignedto' : None, 'files' : [], 'title' = 'yes, ..... } >>> roundup_server.display('issue1', 'priority', 'status') {'priority' : '1', 'status' : '2'} >>> roundup_server.set('issue1', 'status=3') >>> roundup_server.display('issue1', 'status') {'status' : '3' } >>> roundup_server.create('issue', "title='another bug'", "status=2") '2' >>> roundup_server.filter('user',None,{'username':'adm'}) ['1'] >>> roundup_server.filter('user',['1','2'],{'username':'adm'}) ['1'] >>> roundup_server.filter('user',['2'],{'username':'adm'}) [] >>> roundup_server.filter('user',[],{'username':'adm'}) [] >>> roundup_server.lookup('user','admin') '1' Advanced Python Client Adding anti-csrf Headers =============================================== The one below adds Referer and X-Requested-With headers so it can pass stronger CSRF detection methods. It also generates a fault message from the server and reports it. Note if you are using http rather than https, replace xmlrpclib.SafeTransport with xmlrpclib.Transport:: try: from xmlrpc import client as xmlrpclib # python 3 except ImportError: import xmlrpclib # python 2 hostname="localhost" path="/demo" user_pw="admin:admin" class SpecialTransport(xmlrpclib.SafeTransport): def send_content(self, connection, request_body): connection.putheader("Referer", "https://%s%s/"%(hostname, path)) connection.putheader("Origin", "https://%s"%hostname) connection.putheader("X-Requested-With", "XMLHttpRequest") connection.putheader("Content-Type", "text/xml") connection.putheader("Content-Length", str(len(request_body))) connection.endheaders() if request_body: connection.send(request_body) roundup_server = xmlrpclib.ServerProxy( 'https://%s@%s%s/xmlrpc'%(user_pw,hostname,path), transport=SpecialTransport(), verbose=False, allow_none=True) print(roundup_server.schema()) print(roundup_server.display('user2', 'username')) print(roundup_server.display('issue1', 'status')) print(roundup_server.filter('user',['1','2','3'],{'username':'demo'})) # this will fail with a fault try: print(roundup_server.filter('usr',['0','2','3'],{'username':'demo'})) except Exception as msg: print(msg) modify this script replacing the hostname, path and user_pw with those for your tracker.
