Mercurial > p > roundup > code
comparison roundup/cgi/client.py @ 7153:1181157d7cec
Refactor rejecting requests; update tests, xfail test
Added new Client::reject_request method. Deployed throughout
handle_rest() method.
Fix tests to compensate for consistent formatting of errors.
Mark testRestOriginValidation test xfail. Code needed to implement it
fully is only partly written.
Tests for OPTIONS request on a bad attribute and valid and invalid
origin tests added.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Tue, 21 Feb 2023 22:35:58 -0500 |
| parents | 72a54826ff4f |
| children | 89a59e46b3af |
comparison
equal
deleted
inserted
replaced
| 7152:4e0665238617 | 7153:1181157d7cec |
|---|---|
| 638 self.write("") | 638 self.write("") |
| 639 else: | 639 else: |
| 640 self.setHeader("Content-Length", str(len(output))) | 640 self.setHeader("Content-Length", str(len(output))) |
| 641 self.write(output) | 641 self.write(output) |
| 642 | 642 |
| 643 def reject_request(self, message, message_type="text/plain", | |
| 644 status=http_.client.UNAUTHORIZED): | |
| 645 self.response_code = status | |
| 646 self.setHeader("Content-Length", str(len(message))) | |
| 647 self.setHeader("Content-Type", message_type) | |
| 648 self.write(message) | |
| 649 | |
| 643 def handle_rest(self): | 650 def handle_rest(self): |
| 644 # Set the charset and language | 651 # Set the charset and language |
| 645 self.determine_charset() | 652 self.determine_charset() |
| 646 if self.instance.config["WEB_TRANSLATE_REST"]: | 653 if self.instance.config["WEB_TRANSLATE_REST"]: |
| 647 self.determine_language() | 654 self.determine_language() |
| 650 try: | 657 try: |
| 651 self.determine_user() | 658 self.determine_user() |
| 652 self.db.tx_Source = "rest" | 659 self.db.tx_Source = "rest" |
| 653 self.db.i18n = self.translator | 660 self.db.i18n = self.translator |
| 654 except LoginError as err: | 661 except LoginError as err: |
| 655 self.response_code = http_.client.UNAUTHORIZED | |
| 656 output = s2b("Invalid Login - %s" % str(err)) | 662 output = s2b("Invalid Login - %s" % str(err)) |
| 657 self.setHeader("Content-Length", str(len(output))) | 663 self.reject_request(output, status=http_.client.UNAUTHORIZED) |
| 658 self.setHeader("Content-Type", "text/plain") | |
| 659 self.write(output) | |
| 660 return | 664 return |
| 661 | 665 |
| 662 # verify Origin is allowed on all requests including GET. | 666 # verify Origin is allowed on all requests including GET. |
| 663 # If a GET, missing origin is allowed (i.e. same site GET request) | 667 # If a GET, missing origin is allowed (i.e. same site GET request) |
| 664 if not self.is_origin_header_ok(api=True): | 668 if not self.is_origin_header_ok(api=True): |
| 665 # Use code 400. Codes 401 and 403 imply that authentication | |
| 666 # is needed or authenticated person is not authorized. | |
| 667 # Preflight doesn't do authentication. | |
| 668 self.response_code = 400 | |
| 669 | |
| 670 if 'HTTP_ORIGIN' not in self.env: | 669 if 'HTTP_ORIGIN' not in self.env: |
| 671 msg = self._("Required Header Missing") | 670 msg = self._("Required Header Missing") |
| 672 else: | 671 else: |
| 673 msg = self._("Client is not allowed to use Rest Interface.") | 672 msg = self._("Client is not allowed to use Rest Interface.") |
| 674 | 673 |
| 674 # Use code 400. Codes 401 and 403 imply that authentication | |
| 675 # is needed or authenticated person is not authorized. | |
| 676 # Preflight doesn't do authentication. | |
| 675 output = s2b( | 677 output = s2b( |
| 676 '{ "error": { "status": 400, "msg": "%s" } }' % msg) | 678 '{ "error": { "status": 400, "msg": "%s" } }' % msg) |
| 677 self.setHeader("Content-Length", str(len(output))) | 679 self.reject_request(output, |
| 678 self.setHeader("Content-Type", "application/json") | 680 message_type="application/json", |
| 679 self.write(output) | 681 status=400) |
| 680 return | 682 return |
| 681 | 683 |
| 682 # Handle CORS preflight request. We know rest is enabled | 684 # Handle CORS preflight request. We know rest is enabled |
| 683 # because handle_rest is called. Preflight requests | 685 # because handle_rest is called. Preflight requests |
| 684 # are unauthenticated, so no need to check permissions. | 686 # are unauthenticated, so no need to check permissions. |
| 685 if ( self.is_cors_preflight() ): | 687 if ( self.is_cors_preflight() ): |
| 686 self.handle_preflight() | 688 self.handle_preflight() |
| 687 return | 689 return |
| 688 elif not self.db.security.hasPermission('Rest Access', self.userid): | 690 elif not self.db.security.hasPermission('Rest Access', self.userid): |
| 689 self.response_code = 403 | |
| 690 output = s2b('{ "error": { "status": 403, "msg": "Forbidden." } }') | 691 output = s2b('{ "error": { "status": 403, "msg": "Forbidden." } }') |
| 691 self.setHeader("Content-Length", str(len(output))) | 692 self.reject_request(output, |
| 692 self.setHeader("Content-Type", "application/json") | 693 message_type="application/json", |
| 693 self.write(output) | 694 status=403) |
| 694 return | 695 return |
| 695 | 696 |
| 696 self.check_anonymous_access() | 697 self.check_anonymous_access() |
| 697 | 698 |
| 698 try: | 699 try: |
| 701 # raises exception on check failure. | 702 # raises exception on check failure. |
| 702 # Note this returns true for a GET request. | 703 # Note this returns true for a GET request. |
| 703 # Must check supplied Origin header for bad value first. | 704 # Must check supplied Origin header for bad value first. |
| 704 csrf_ok = self.handle_csrf(api=True) | 705 csrf_ok = self.handle_csrf(api=True) |
| 705 except (Unauthorised, UsageError) as msg: | 706 except (Unauthorised, UsageError) as msg: |
| 706 # FIXME should return what the client requests | 707 # FIXME should format return value according to |
| 707 # via accept header. | 708 # client's accept header, so application/xml, text/plain etc.. |
| 708 output = s2b('{ "error": { "status": 400, "msg": "%s"}}' % | 709 output = s2b('{ "error": { "status": 400, "msg": "%s"}}' % |
| 709 str(msg)) | 710 str(msg)) |
| 710 self.response_code = 400 | 711 self.reject_request(output, |
| 711 self.setHeader("Content-Length", str(len(output))) | 712 message_type="application/json", |
| 712 self.setHeader("Content-Type", "application/json") | 713 status=400) |
| 713 self.write(output) | |
| 714 csrf_ok = False # we had an error, failed check | 714 csrf_ok = False # we had an error, failed check |
| 715 return | 715 return |
| 716 | 716 |
| 717 # With the return above the if will never be false, | 717 # With the return above the if will never be false, |
| 718 # Keeping the if so we can remove return to pass | 718 # Keeping the if so we can remove return to pass |
