From 0738287dc4096d5209f94d12e57552c2e155c26d Mon Sep 17 00:00:00 2001
From: Patrick Uiterwijk <puiterwijk@redhat.com>
Date: Sep 30 2016 13:22:38 +0000
Subject: [PATCH 1/3] Check SSH keys before writing them out


This is needed because Gitolite will abort all ACL and keyfile regeneration
if there is a single invalid key in its keydir.

Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>

---

diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py
index 8473bd1..0364bc5 100644
--- a/pagure/lib/__init__.py
+++ b/pagure/lib/__init__.py
@@ -25,6 +25,7 @@ import markdown
 import os
 import shutil
 import tempfile
+import subprocess
 import urlparse
 import uuid
 
@@ -181,6 +182,18 @@ def search_user(session, username=None, email=None, token=None, pattern=None):
     return output
 
 
+def is_valid_ssh_key(key):
+    key = key.strip()
+    if not key:
+        return None
+    proc = subprocess.Popen(['/usr/bin/ssh-keygen', '-l', '-f', '-'],
+                            stdin=subprocess.PIPE,
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.PIPE)
+    proc.communicate(key)
+    return proc.returncode == 0
+
+
 def create_user_ssh_keys_on_disk(user, gitolite_keydir):
     ''' Create the ssh keys for the user on the specific folder.
 
@@ -211,6 +224,8 @@ def create_user_ssh_keys_on_disk(user, gitolite_keydir):
         for i in range(len(keys)):
             if not keys[i]:
                 continue
+            if not is_valid_ssh_key(keys[i]):
+                continue
             keyline_dir = os.path.join(gitolite_keydir, 'keys_%i' % i)
             if not os.path.exists(keyline_dir):
                 os.mkdir(keyline_dir)

From f8d59053aaa1576773b94235d4c09c3e0339a181 Mon Sep 17 00:00:00 2001
From: Patrick Uiterwijk <puiterwijk@redhat.com>
Date: Sep 30 2016 13:22:38 +0000
Subject: [PATCH 2/3] Add a validator to check ssh keys on edit


Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>

---

diff --git a/pagure/forms.py b/pagure/forms.py
index 219e425..344d74c 100644
--- a/pagure/forms.py
+++ b/pagure/forms.py
@@ -20,6 +20,7 @@ import wtforms
 import tempfile
 
 import pagure
+import pagure.lib
 
 
 STRICT_REGEX = '^[a-zA-Z0-9-_]+$'
@@ -68,6 +69,11 @@ def file_virus_validator(form, field):
             raise wtforms.ValidationError('Error scanning uploaded file')
 
 
+def ssh_key_validator(form, field):
+    if not pagure.lib.are_valid_ssh_keys(field.data):
+        raise wtforms.ValidationError('Invalid SSH keys')
+
+
 class ProjectFormSimplified(wtf.Form):
     ''' Form to edit the description of a project. '''
     description = wtforms.TextField(
@@ -336,7 +342,8 @@ class UserSettingsForm(wtf.Form):
     ''' Form to create or edit project. '''
     ssh_key = wtforms.TextAreaField(
         'Public SSH key <span class="error">*</span>',
-        [wtforms.validators.Required()]
+        [wtforms.validators.Required(),
+         ssh_key_validator]
     )
 
 
diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py
index 0364bc5..57df7a1 100644
--- a/pagure/lib/__init__.py
+++ b/pagure/lib/__init__.py
@@ -194,6 +194,11 @@ def is_valid_ssh_key(key):
     return proc.returncode == 0
 
 
+def are_valid_ssh_keys(keys):
+    return all([is_valid_ssh_key(key) is not False
+                for key in keys.split('\n')])
+
+
 def create_user_ssh_keys_on_disk(user, gitolite_keydir):
     ''' Create the ssh keys for the user on the specific folder.
 

From f888fd9de0103687c30910f9ffdf42f55f8470ce Mon Sep 17 00:00:00 2001
From: Patrick Uiterwijk <puiterwijk@redhat.com>
Date: Sep 30 2016 13:22:38 +0000
Subject: [PATCH 3/3] Add test case for SSH key checker


Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>

---

diff --git a/tests/test_pagure_flask_ui_app.py b/tests/test_pagure_flask_ui_app.py
index eb08845..3293c99 100644
--- a/tests/test_pagure_flask_ui_app.py
+++ b/tests/test_pagure_flask_ui_app.py
@@ -395,7 +395,7 @@ class PagureFlaskApptests(tests.Modeltests):
                 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
 
             data = {
-                'ssh_key': 'this is my ssh key',
+                'ssh_key': 'blah'
             }
 
             output = self.app.post('/settings/', data=data)
@@ -403,24 +403,40 @@ class PagureFlaskApptests(tests.Modeltests):
             self.assertIn(
                 '<div class="card-header">\n          Basic Information\n'
                 '      </div>', output.data)
-            self.assertIn(
-                '<textarea class="form-control" id="ssh_key" name="ssh_key">'
-                'this is my ssh key</textarea>', output.data)
 
             data['csrf_token'] =  csrf_token
 
             output = self.app.post(
                 '/settings/', data=data, follow_redirects=True)
             self.assertEqual(output.status_code, 200)
-            self.assertTrue(
-                '</button>\n                      Public ssh key updated'
-                in output.data)
+            self.assertIn('Invalid SSH keys', output.data)
+            self.assertIn(
+                '<div class="card-header">\n          Basic Information\n'
+                '      </div>', output.data)
+            self.assertIn('>blah</textarea>', output.data)
+
+            csrf_token = output.data.split(
+                'name="csrf_token" type="hidden" value="')[1].split('">')[0]
+
+            data = {
+                'ssh_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDUkub32fZnNI'
+                           '1zJYs43vhhx3c6IcYo4yzhw1gQ37BLhrrNeS6x8l5PKX4J8ZP5'
+                           '1XhViPaLbeOpl94Vm5VSCbLy0xtY9KwLhMkbKj7g6vvfxLm2sT'
+                           'Osb15j4jzIkUYYgIE7cHhZMCLWR6UA1c1HEzo6mewMDsvpQ9wk'
+                           'cDnAuXjK3Q==',
+                'csrf_token': csrf_token
+            }
+
+            output = self.app.post(
+                '/settings/', data=data, follow_redirects=True)
+            self.assertEqual(output.status_code, 200)
+            self.assertIn('Public ssh key updated', output.data)
             self.assertIn(
                 '<div class="card-header">\n          Basic Information\n'
                 '      </div>', output.data)
             self.assertIn(
                 '<textarea class="form-control" id="ssh_key" name="ssh_key">'
-                'this is my ssh key</textarea>', output.data)
+                'ssh-rsa AAAA', output.data)
 
             ast.return_value = True
             output = self.app.get('/settings/')

