[#10288] ticket:784 Block project registrations using a T7 country phone number
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index 197f859..1134284 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -199,7 +199,7 @@
     @expose('json:')
     def verify_phone(self, number):
         p = plugin.ProjectRegistrationProvider.get()
-        result = p.verify_phone(c.user, number)
+        result = p.verify_phone(c.user, number, request)
         request_id = result.pop('request_id', None)
         if request_id:
             session['phone_verification.request_id'] = request_id
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index abb5c8c..d71a9f8 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -732,11 +732,22 @@
             return True
         return bool(user.get_tool_data('phone_verification', 'number_hash'))
 
-    def verify_phone(self, user, number):
+    def verify_phone(self, user, number, req):
         ok = {'status': 'ok'}
         if not asbool(config.get('project.verify_phone')):
             return ok
         number = utils.clean_phone_number(number)
+        blocked_prefix = self.t7_phone(number)
+        if blocked_prefix:
+            log.info(
+                'Blocked project registation, phone number is in T7 list. '
+                'User: %s, Phone # hash: %s, Country code: %s, IP: %s, UA: %s',
+                user.username,
+                utils.phone_number_hash(number),
+                blocked_prefix,
+                utils.ip_address(req),
+                req.headers.get('User-Agent'))
+            return {'status': 'error', 'error': 'T7_BLOCKED'}
         return g.phone_service.verify(number)
 
     def check_phone_verification(self, user, request_id, pin, number_hash):
@@ -753,6 +764,13 @@
             h.auditlog_user(msg, user=user)
         return res
 
+    def t7_phone(self, number):
+        t7_prefixes = json.loads(config.get('phone.t7_prefixes', '[]'))
+        for p in t7_prefixes:
+            if number.startswith(p):
+                return p
+        return None
+
     def register_neighborhood_project(self, neighborhood, users, allow_register=False):
         from allura import model as M
         shortname = '--init--'
diff --git a/Allura/allura/nf/allura/css/allura.css b/Allura/allura/nf/allura/css/allura.css
index 6c5c1f4..c89a02c 100644
--- a/Allura/allura/nf/allura/css/allura.css
+++ b/Allura/allura/nf/allura/css/allura.css
@@ -85,5 +85,5 @@
 }
 
 #phone_verification_overlay iframe {
-    height: 250px;
+    height: 280px;
 }
diff --git a/Allura/allura/public/nf/js/phone-verification.js b/Allura/allura/public/nf/js/phone-verification.js
index e6bb8fc..71b5744 100644
--- a/Allura/allura/public/nf/js/phone-verification.js
+++ b/Allura/allura/public/nf/js/phone-verification.js
@@ -71,13 +71,27 @@
       disabled: this.isButtonDisabled()
     };
     var nbsp = String.fromCharCode(160);
+    var error = this.getError(this.props.state.error);
     return dom('div', null,
              dom('label', {className: grid}, this.getLabel()),
              dom('input', input_props),
-             dom('div', {className: grid + ' error-text'}, this.props.state.error || nbsp),
+             dom('div', {className: grid + ' error-text'}, error || nbsp),
              dom('div', {className: grid},
                dom('button', button_props, 'Submit')));
   },
+
+  getError: function(error) {
+    if (error === 'T7_BLOCKED') {
+      var error_text = 'Your request is being denied as it appears the phone ' +
+                       'number provided is from a location banned by our ';
+      var link_attrs = {href: 'http://slashdotmedia.com/terms-of-use',
+                        target: '_blank'};
+      var error_link = dom('a', link_attrs, 'Terms of Use');
+      return dom('span', null, [error_text,  error_link]);
+    } else {
+      return error;
+    }
+  },
   
   handleClick: function() {
     if (!this.isButtonDisabled()) {
diff --git a/Allura/allura/tests/test_plugin.py b/Allura/allura/tests/test_plugin.py
index 64b9695..94cbeef 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -89,6 +89,7 @@
 
 class UserMock(object):
     def __init__(self):
+        self.username = 'test-user'
         self.tool_data = {}
         self._projects = []
 
@@ -144,18 +145,45 @@
     def test_verify_phone_disabled(self, g):
         g.phone_service = Mock(spec=phone.PhoneService)
         with h.push_config(tg.config, **{'project.verify_phone': 'false'}):
-            result = self.p.verify_phone(self.user, '12345')
+            result = self.p.verify_phone(self.user, '12345', None)
             assert_false(g.phone_service.verify.called)
             assert_equal(result, {'status': 'ok'})
 
+    @patch.object(plugin, 'log', autospec=True)
+    @patch.object(plugin, 'g', autospec=True)
+    def test_verify_phone_blocked(self, g, log):
+        g.phone_service = Mock(spec=phone.PhoneService)
+        cfg = {'project.verify_phone': 'true',
+               'phone.t7_prefixes': '["13", "74"]'}
+        with h.push_config(tg.config, **cfg):
+            req = Request.blank('/')
+            req.remote_addr = '1.2.3.4'
+            req.headers['User-Agent'] = 'Chrome'
+            result = self.p.verify_phone(self.user, '13-21-94', req)
+            assert_false(g.phone_service.verify.called)
+            assert_equal(result, {'status': 'error', 'error': 'T7_BLOCKED'})
+            log.info.assert_called_once_with(
+                'Blocked project registation, phone number is in T7 list. '
+                'User: %s, Phone # hash: %s, Country code: %s, IP: %s, UA: %s',
+                'test-user', '43648c7bc5f0fe67466d174876a9ccdf7a84f8c4',
+                '13', '1.2.3.4', 'Chrome')
+
     @patch.object(plugin, 'g')
     def test_verify_phone(self, g):
         g.phone_service = Mock(spec=phone.PhoneService)
         with h.push_config(tg.config, **{'project.verify_phone': 'true'}):
-            result = self.p.verify_phone(self.user, '123 45 45')
+            result = self.p.verify_phone(self.user, '123 45 45', None)
             g.phone_service.verify.assert_called_once_with('1234545')
             assert_equal(result, g.phone_service.verify.return_value)
 
+    def test_t7_phone(self):
+        cfg = {'phone.t7_prefixes': '["13", "74"]'}
+        with h.push_config(tg.config, **cfg):
+            assert_equal(self.p.t7_phone('13 01 24'), '13')
+            assert_equal(self.p.t7_phone('74010203'), '74')
+            assert_equal(self.p.t7_phone('555-555-5555'), None)
+            assert_equal(self.p.t7_phone('38 093 111 11 11'), None)
+
     @patch.object(plugin, 'g')
     def test_check_phone_verification_disabled(self, g):
         g.phone_service = Mock(spec=phone.PhoneService)