File horizon-secure_SECRET_KEY.patch of Package openstack-dashboard

diff -ruN a/.gitignore b/.gitignore
--- a/.gitignore	2012-06-05 15:43:17.000000000 +0200
+++ b/.gitignore	2012-06-25 15:36:16.029765418 +0200
@@ -10,6 +10,7 @@
 reports
 horizon.egg-info
 openstack_dashboard/local/local_settings.py
+openstack_dashboard/test/.secret_key_store
 docs/build/
 docs/source/sourcecode
 .venv
diff -ruN a/horizon/tests/utils_tests.py b/horizon/tests/utils_tests.py
--- a/horizon/tests/utils_tests.py	2012-06-05 15:43:17.000000000 +0200
+++ b/horizon/tests/utils_tests.py	2012-06-25 15:36:32.677765799 +0200
@@ -15,8 +15,11 @@
 #    under the License.
 
 
+import os
+
 from horizon import test
 from horizon.utils import validators
+from horizon.utils import secret_key
 
 
 class ValidatorsTests(test.TestCase):
@@ -40,3 +43,26 @@
             self.assertTrue(validators.ipv4_cidr_re.match(cidr))
         for cidr in BAD_CIDRS:
             self.assertFalse(validators.ipv4_cidr_re.match(cidr))
+
+
+class SecretKeyTests(test.TestCase):
+    def test_generate_secret_key(self):
+        key = secret_key.generate_key(32)
+        self.assertEqual(len(key), 32)
+        self.assertNotEqual(key, secret_key.generate_key(32))
+
+    def test_generate_or_read_key_from_file(self):
+        key_file = ".test_secret_key_store"
+        key = secret_key.generate_or_read_from_file(key_file)
+
+        # Consecutive reads should come from the already existing file:
+        self.assertEqual(key, secret_key.generate_or_read_from_file(key_file))
+
+        # Key file only be read/writable by user:
+        self.assertEqual(oct(os.stat(key_file).st_mode & 0777), "0600")
+        os.chmod(key_file, 0777)
+        self.assertRaises(secret_key.FilePermissionError,
+                          secret_key.generate_or_read_from_file, key_file)
+        os.chmod(key_file, 0600)
+
+        os.remove(key_file)  # Cleanup
diff -ruN a/horizon/utils/secret_key.py b/horizon/utils/secret_key.py
--- a/horizon/utils/secret_key.py	1970-01-01 01:00:00.000000000 +0100
+++ b/horizon/utils/secret_key.py	2012-06-25 15:37:13.443766746 +0200
@@ -0,0 +1,68 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 Nebula, Inc.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from __future__ import with_statement  # Python 2.5 compliance
+
+import lockfile
+import random
+import string
+import tempfile
+import os
+
+
+class FilePermissionError(Exception):
+    """The key file permissions are insecure."""
+    pass
+
+
+def generate_key(key_length=64):
+    """Secret key generator.
+
+    The quality of randomness depends on operating system support,
+    see http://docs.python.org/library/random.html#random.SystemRandom.
+    """
+    if hasattr(random, 'SystemRandom'):
+        choice = random.SystemRandom().choice
+    else:
+        choice = random.choice
+    return ''.join(map(lambda x: choice(string.digits + string.letters),
+                   range(key_length)))
+
+
+def generate_or_read_from_file(key_file='.secret_key', key_length=64):
+    """Multiprocess-safe secret key file generator.
+
+    Useful to replace the default (and thus unsafe) SECRET_KEY in settings.py
+    upon first start. Save to use, i.e. when multiple Python interpreters
+    serve the dashboard Django application (e.g. in a mod_wsgi + daemonized
+    environment).  Also checks if file permissions are set correctly and
+    throws an exception if not.
+    """
+    lock = lockfile.FileLock(key_file)
+    with lock:
+        if not os.path.exists(key_file):
+            key = generate_key(key_length)
+            old_umask = os.umask(0177)  # Use '0600' file permissions
+            with open(key_file, 'w') as f:
+                f.write(key)
+            os.umask(old_umask)
+        else:
+            if oct(os.stat(key_file).st_mode & 0777) != '0600':
+                raise FilePermissionError("Insecure key file permissions!")
+            with open(key_file, 'r') as f:
+                key = f.readline()
+        return key
diff -ruN a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example
--- a/openstack_dashboard/local/local_settings.py.example	2012-06-05 15:43:17.000000000 +0200
+++ b/openstack_dashboard/local/local_settings.py.example	2012-06-25 15:38:21.185768316 +0200
@@ -7,9 +7,6 @@
 PROD = False
 USE_SSL = False
 
-# Note: You should change this value
-SECRET_KEY = 'elj1IWiLoWHgcyYxFVLj7cM5rGOOxWl0'
-
 # Specify a regular expression to validate user passwords.
 # HORIZON_CONFIG = {
 #     "password_validator": {
@@ -20,6 +17,18 @@
 
 LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
 
+# Set custom secret key:
+# You can either set it to a specific value or you can let horizion generate a
+# default secret key that is unique on this machine, e.i. regardless of the
+# amount of Python WSGI workers (if used behind Apache+mod_wsgi): However, there
+# may be situations where you would want to set this explicitly, e.g. when
+# multiple dashboard instances are distributed on different machines (usually
+# behind a load-balancer). Either you have to make sure that a session gets all
+# requests routed to the same dashboard instance or you set the same SECRET_KEY
+# for all of them.
+# from horizon.utils import secret_key
+# SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH, '.secret_key_store'))
+
 # We recommend you use memcached for development; otherwise after every reload
 # of the django development server, you will have to login again. To use
 # memcached set CACHE_BACKED to something like 'memcached://127.0.0.1:11211/'
diff -ruN a/tools/pip-requires b/tools/pip-requires
--- a/tools/pip-requires	2012-06-05 15:43:17.000000000 +0200
+++ b/tools/pip-requires	2012-06-25 15:38:34.263768622 +0200
@@ -17,6 +17,9 @@
 xattr
 iso8601
 
+# Horizon Utility Requirements
+lockfile # for SECURE_KEY generation
+
 # Horizon Non-pip Requirements
 -e git+https://github.com/openstack/python-novaclient.git#egg=python-novaclient
 -e git+https://github.com/openstack/python-keystoneclient.git#egg=python-keystoneclient
openSUSE Build Service is sponsored by