diff roundup/cgi/client.py @ 7692:8fb42f41ef10 issue2550923_computed_property

merge in default branch to see if ti clears a travis-ci build error on 2.7 python; default branch builds fine
author John Rouillard <rouilj@ieee.org>
date Mon, 11 Sep 2023 00:10:29 -0400
parents 7102de2c8733
children cc4b11ab2f22
line wrap: on
line diff
--- a/roundup/cgi/client.py	Mon Jul 10 17:31:34 2023 -0400
+++ b/roundup/cgi/client.py	Mon Sep 11 00:10:29 2023 -0400
@@ -4,7 +4,6 @@
 
 import base64
 import binascii
-import cgi
 import codecs
 import email.utils
 import errno
@@ -25,6 +24,7 @@
     class SysCallError(Exception):
         pass
 
+from roundup.anypy.cgi_ import cgi
 import roundup.anypy.email_  # noqa: F401  -- patches for email library code
 import roundup.anypy.random_ as random_   # quality of random checked below
 
@@ -43,8 +43,8 @@
     Redirect, SendFile, SendStaticFile, SeriousError)
 from roundup.cgi.form_parser import FormParser
 
-from roundup.exceptions import LoginError, Reject, RejectRaw, \
-                               Unauthorised, UsageError
+from roundup.exceptions import LoginError, RateLimitExceeded, Reject, \
+                               RejectRaw, Unauthorised, UsageError
 
 from roundup.mailer import Mailer, MessageSendError
 
@@ -531,9 +531,9 @@
         # strip HTTP_PROXY issue2550925 in case
         # PROXY header is set.
         if 'HTTP_PROXY' in self.env:
-            del(self.env['HTTP_PROXY'])
+            del (self.env['HTTP_PROXY'])
         if 'HTTP_PROXY' in os.environ:
-            del(os.environ['HTTP_PROXY'])
+            del (os.environ['HTTP_PROXY'])
 
         xmlrpc_enabled = self.instance.config.WEB_ENABLE_XMLRPC
         rest_enabled = self.instance.config.WEB_ENABLE_REST
@@ -572,7 +572,7 @@
             self.determine_language()
         # Open the database as the correct user.
         try:
-            self.determine_user()
+            self.determine_user(is_api="xmlrpc")
             self.db.tx_Source = "xmlrpc"
             self.db.i18n = self.translator
         except LoginError as msg:
@@ -583,6 +583,14 @@
             self.setHeader("Content-Length", str(len(output)))
             self.write(s2b(output))
             return
+        except RateLimitExceeded as msg:
+            output = xmlrpc_.client.dumps(
+                xmlrpc_.client.Fault(429, "%s" % msg),
+                allow_none=True)
+            self.setHeader("Content-Type", "text/xml")
+            self.setHeader("Content-Length", str(len(output)))
+            self.write(s2b(output))
+            return
 
         if not self.db.security.hasPermission('Xmlrpc Access', self.userid):
             output = xmlrpc_.client.dumps(
@@ -655,13 +663,19 @@
         # Open the database as the correct user.
         # TODO: add everything to RestfulDispatcher
         try:
-            self.determine_user()
+            self.determine_user(is_api="rest")
             self.db.tx_Source = "rest"
             self.db.i18n = self.translator
         except LoginError as err:
             output = s2b("Invalid Login - %s" % str(err))
             self.reject_request(output, status=http_.client.UNAUTHORIZED)
             return
+        except RateLimitExceeded as err:
+            output = s2b("%s" % str(err))
+            # PYTHON2:FIXME http_.client.TOO_MANY_REQUESTS missing
+            # python2 so use numeric code.
+            self.reject_request(output, status=429)
+            return
 
         # verify Origin is allowed on all requests including GET.
         # If a GET, missing origin is allowed  (i.e. same site GET request)
@@ -854,6 +868,8 @@
             except SysCallError:
                 # OpenSSL.SSL.SysCallError is similar to IOError above
                 pass
+            except RateLimitExceeded:
+                raise
 
         except SeriousError as message:
             self.write_html(str(message))
@@ -917,6 +933,9 @@
         except FormError as e:
             self.add_error_message(self._('Form Error: ') + str(e))
             self.write_html(self.renderContext())
+        except RateLimitExceeded as e:
+            self.add_error_message(str(e))
+            self.write_html(self.renderContext())
         except IOError:
             # IOErrors here are due to the client disconnecting before
             # receiving the reply.
@@ -1108,9 +1127,9 @@
             self.make_user_anonymous()
             raise LoginError(str(err))
 
-        return(token)
-
-    def determine_user(self):
+        return (token)
+
+    def determine_user(self, is_api=False):
         """Determine who the user is"""
         self.opendb('admin')
 
@@ -1171,8 +1190,8 @@
                         # So we set the user to anonymous first.
                         self.make_user_anonymous()
                         login = self.get_action_class('login')(self)
-                        login.verifyLogin(username, password)
-                    except LoginError:
+                        login.verifyLogin(username, password, is_api=is_api)
+                    except (LoginError, RateLimitExceeded):
                         self.make_user_anonymous()
                         raise
                     user = username
@@ -1839,7 +1858,7 @@
         # mime type detection is performed in cgi.form_parser
 
         # everything not here is served as 'application/octet-stream'
-        whitelist = [
+        mime_type_allowlist = [
             'text/plain',
             'text/x-csrc',    # .c
             'text/x-chdr',    # .h
@@ -1859,7 +1878,7 @@
         ]
 
         if self.instance.config['WEB_ALLOW_HTML_FILE']:
-            whitelist.append('text/html')
+            mime_type_allowlist.append('text/html')
 
         try:
             mime_type = klass.get(nodeid, 'type')
@@ -1869,7 +1888,7 @@
         if not mime_type:
             mime_type = 'text/plain'
 
-        if mime_type not in whitelist:
+        if mime_type not in mime_type_allowlist:
             mime_type = 'application/octet-stream'
 
         # --/ mime-type security
@@ -2743,7 +2762,7 @@
         """
         if value is None:
             try:
-                del(self.additional_headers[header])
+                del (self.additional_headers[header])
             except KeyError:
                 pass
         else:
@@ -2764,7 +2783,7 @@
             headers['Content-Type'] = 'text/html; charset=utf-8'
 
         if response in [204, 304]:  # has no body so no content-type
-            del(headers['Content-Type'])
+            del (headers['Content-Type'])
 
         headers = list(headers.items())
 

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