Mercurial > p > roundup > code
diff doc/rest.txt @ 7045:ca90f7270cd4 issue2550923_computed_property
merge from main tip. This should fix test failure in Markdown2TestCase.test_string_markdown_code_block_attribute by merging html diff method used in tests.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Mon, 07 Nov 2022 22:58:38 -0500 |
| parents | e7b4ad2c57ac |
| children | 42e68162279b |
line wrap: on
line diff
--- a/doc/rest.txt Thu Apr 21 16:54:17 2022 -0400 +++ b/doc/rest.txt Mon Nov 07 22:58:38 2022 -0500 @@ -1,5 +1,5 @@ .. meta:: - :description language=en: + :description: Documentation on the RESTful interface to the Roundup Issue Tracker. @@ -72,6 +72,11 @@ tracker's config.ini should have ``csrf_enforce_header_x-requested-with = yes`` or ``required``. +If you want to allow Roundup's api to be accessed by an application +that is not hosted at the same origin as Roundup, you must permit +the origin using the ``allowed_api_origins`` setting in +``config.ini``. + Rate Limiting the API ===================== @@ -203,6 +208,48 @@ ``application/x-www-form-urlencoded``. Any other value returns error code 415. +CORS preflight requests +^^^^^^^^^^^^^^^^^^^^^^^ + +CORS preflight requests are done using the OPTIONS method. They +require that REST be enabled. These requests do not make any changes +or get any information from the database. As a result they are +available to the anonymous user and any authenticated user. The user +does not need to have `Rest Access` permissions. Also these requests +bypass CSRF checks except for the Origin header check which is always +run for preflight requests. + +You can permit only allowed ORIGINS by setting ``allowed_api_origins`` +in ``config.ini`` to the list of origins permitted to access your +api. By default only your tracker's origin is allowed. If a preflight +request fails, the api request will be stopped by the browser. + +The following CORS preflight headers are usually added automatically by +the browser and must all be present: + +* `Access-Control-Request-Headers` +* `Access-Control-Request-Method` +* `Origin` + +The 204 response will include the headers: + +* `Access-Control-Allow-Origin` +* `Access-Control-Allow-Headers` +* `Access-Control-Allow-Methods` +* `Access-Control-Allow-Credentials: true` +* `Access-Control-Max-Age: 86400` + +If the endpoint accepts the PATCH verb the header `Accept-Patch` with +valid mime types (usually `application/x-www-form-urlencoded, +multipart/form-data`) will be included. + +It will also include rate limit headers since the request is included +in the rate limit for the URL. The results from the CORS preflight +should be cached for a day so preflight requests are not expected to +cause a problem. If it is an issue, you can see +`Creating Custom Rate Limits`_ +and craft a rate limiter that ignores anonymous OPTIONS requests. + Response Formats ================ @@ -212,6 +259,20 @@ data using the header ``Accept: application/xml`` in your request. Both output formats are similar in structure. +``dicttoxml.py`` should be installed in the Python install directory, +or the file can be added to the Roundup installation directory long +ide ``rest.py``. It can also be enabled on a per tracker basis by +adding ``dicttoxml.py`` to the lib directory in your tracker home (you +may need to create the directory). Then this can be added to +`interfaces.py`_ to enable xml:: + + from roundup import rest + from dicttoxml import dicttoxml as dtox # from tracker_root/lib directory + + rest.dicttoxml = dtox + +.. _interfaces.py: customizing.html#interfaces-py-hooking-into-the-core-of-roundup + The rest interface accepts the http accept header and can include ``q`` values to specify the preferred mechanism. This is the preferred way to specify alternate acceptable response formats. @@ -423,7 +484,7 @@ * - ``status=open`` - Link - find any issue where the name of the status is open. - Note this is not a string match so using nosy=ope will fail. + Note this is not a string match so using status=ope will fail. * - ``nosy=1`` - MultiLink - find any issue where the multilink nosy includes the id 1. @@ -643,12 +704,10 @@ "data": { "collection": [ { - "link": - "https://.../rest/data/issue/1", + "link": "https://.../rest/data/issue/1", "id": "1", "status": { - "link": - "https://.../rest/data/status/1", + "link": "https://.../rest/data/status/1", "id": "1" } }, @@ -1102,8 +1161,7 @@ ] }, "type": "issue", - "link": - "https://example.com/demo/rest/data/issue/23", + "link": "https://example.com/demo/rest/data/issue/23", "id": "23" } } @@ -1266,7 +1324,8 @@ "id": "11", "type": "<class 'str'>", "link": "https://.../demo/rest/data/msg/11/content", - "data": "of has to who pleasure. or of account give because the reprehenderit\neu to quisquam velit, passage, was or...", + "data": "of has to who pleasure. or of account give because the + reprehenderit\neu to quisquam velit, passage, was or...", "@etag": "\"584f82231079e349031bbb853747df1c\"" } } @@ -1407,7 +1466,10 @@ Note the addition of headers for: x-requested-with and referer. This allows the request to pass the CSRF protection mechanism. You may need to add an Origin header if this check is enabled in your tracker's -config.ini (look for csrf_enforce_header_origin). +config.ini (look for csrf_enforce_header_origin). (Note the Origin +header check may have to be disabled if an application is making a +CORS request to the Roundup server. If you have this issue, please +contact the Roundup team using the mailing lists as this is a bug.) A similar curl based retire example is to use:: @@ -1546,7 +1608,7 @@ Adding new rest endpoints ========================= -Add or edit the file interfaces.py at the root of the tracker +Add or edit the file `interfaces.py`_ at the root of the tracker directory. In that file add:: @@ -1801,7 +1863,7 @@ jwt module you will see the error ``Support for jwt disabled.`` 2. create a new role that allows Create access to timelog and edit/view access to an issues' ``times`` property. -3. add support for issuing (and validating) jwts to the rest interface. +3. add support for issuing (and validating) JWTs to the rest interface. This uses the `Adding new rest endpoints`_ mechanism. 4. configure roundup's config.ini [web] jwt_secret with at least 32 random characters of data. (You will get a message @@ -1975,19 +2037,19 @@ return 200, result **Note this is sample code. Use at your own risk.** It breaks a few -rules about jwts (e.g. it allows you to make unlimited lifetime -jwts). If you subscribe to the concept of jwt refresh tokens, this code -will have to be changed as it will only generate jwts with +rules about JWTs (e.g. it allows you to make unlimited lifetime +JWTs). If you subscribe to the concept of JWT refresh tokens, this code +will have to be changed as it will only generate JWTs with username/password authentication. -Currently use of jwts an experiment. If this appeals to you consider +Currently use of JWTs an experiment. If this appeals to you consider providing patches to existing code to: -1. record all jwts created by a user -2. using the record to allow jwts to be revoked and ignored by the +1. record all JWTs created by a user +2. using the record to allow JWTs to be revoked and ignored by the roundup core -3. provide a UI page for managing/revoking jwts -4. provide a rest api for revoking jwts +3. provide a UI page for managing/revoking JWTs +4. provide a rest api for revoking JWTs These end points can be used like:: @@ -1995,23 +2057,23 @@ -H "X-requested-with: rest" \ -H "Content-Type: application/json" \ --data '{"lifetime": "3600", "roles": [ "user:timelog" ] }' \ - https://.../demo/rest/jwt/issue + https://.../demo/rest/JWT/issue (note roles is a json array/list of strings not a string) to get:: { "data": { - "jwt": "eyJ0eXAiOiJK......XxMDb-Q3oCnMpyhxPXMAk" + "JWT": "eyJ0eXAiOiJK......XxMDb-Q3oCnMpyhxPXMAk" } } -The jwt is shortened in the example since it's large. You can validate -a jwt to see if it's still valid using:: +The JWT is shortened in the example since it's large. You can validate +a JWT to see if it's still valid using:: curl -s -H "Referer: https://.../demo/" \ -H "X-requested-with: rest" \ - https://.../demo/rest/jwt/validate?jwt=eyJ0eXAiOiJK...XxMDb-Q3oCnMpyhxPXMAk + https://.../demo/rest/JWT/validate?JWT=eyJ0eXAiOiJK...XxMDb-Q3oCnMpyhxPXMAk (note no login is required) which returns:: @@ -2028,12 +2090,18 @@ } } + +There is an issue for `thoughts on JWT credentials`_ that you can view +for ideas or add your own. + +.. _thoughts on JWT credentials: https://issues.roundup-tracker.org/issue2551064 + Final steps ^^^^^^^^^^^ See the `upgrading directions`_ on how to use the ``updateconfig`` command to generate an updated copy of config.ini using -roundup-admin. Then set the ``jwt_secret`` to at least 32 characters +roundup-admin. Then set the ``JWT_secret`` to at least 32 characters (more is better up to 512 bits). Writing an auditor that uses "db.user.get_roles" to see if the user @@ -2045,7 +2113,7 @@ Lastly you can create a JWT using the end point above and make a rest call to create a new timelog entry and another call to update the -issues times property. If you have other ideas on how jwts can be +issues times property. If you have other ideas on how JWTs can be used, please share on the roundup mailing lists. See: https://sourceforge.net/p/roundup/mailman/ for directions on subscribing and for archives of the lists. @@ -2056,7 +2124,8 @@ You can replace the default rate limiter that is configured using the tracker's ``config.ini``. You can return different rate -limits based on the user, time of day, phase of moon etc. +limits based on the user, time of day, phase of moon, request +method (via self.client.request.command) etc. Assume you add two integer valued properties to the user object. Let's call them ``rate_limit_interval`` and @@ -2099,5 +2168,8 @@ Rate limit tests:: - seq 1 300 | xargs -P 20 -n 1 curl --head -si \ - https://.../rest/data/status/new \# | grep Remaining + seq 1 300 | xargs -P 20 -n 1 curl --head -u user:password -si \ + https://.../rest/data/status/new | grep Remaining + +will show you the number of remaining requests to the REST interface +for the user identified by password.
