Switch to side-by-side view

--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -11,7 +11,8 @@
 from random import randint
 from hashlib import sha256
 from base64 import b64encode
-from datetime import datetime
+from datetime import datetime, timedelta
+import json
 
 try:
     import ldap
@@ -22,6 +23,7 @@
 from tg import config, flash
 from pylons import g, c
 from webob import exc
+from bson.tz_util import FixedOffset
 
 from ming.utils import LazyProperty
 from ming.orm import state
@@ -334,6 +336,21 @@
         '''
         return []
 
+    def rate_limit(self, user, neighborhood):
+        '''Check the various config-defined project registration rate
+        limits, and if any are exceeded, raise ProjectRatelimitError.
+        '''
+        if security.has_access(neighborhood, 'admin', user=user)():
+            return
+        now = datetime.now().replace(tzinfo=FixedOffset(0, 'UTC'))
+        project_count = len(list(user.my_projects()))
+        rate_limits = json.loads(config.get('project.rate_limits', '{}'))
+        for rate, count in rate_limits.items():
+            user_age = now - user._id.generation_time
+            user_age = (user_age.microseconds + (user_age.seconds + user_age.days * 24 * 3600) * 10**6) / 10**6
+            if user_age < int(rate) and project_count >= count:
+                raise forge_exc.ProjectRatelimitError()
+
     def register_neighborhood_project(self, neighborhood, users, allow_register=False):
         from allura import model as M
         shortname='--init--'
@@ -390,6 +407,8 @@
         if nb_max_projects is not None and count >= nb_max_projects:
             log.exception('Error registering project %s' % project_name)
             raise forge_exc.ProjectOverlimitError()
+
+        self.rate_limit(user, neighborhood)
 
         if not h.re_path_portion.match(shortname.replace('/', '')):
             raise ValueError('Invalid project shortname: %s' % shortname)