cleanup
[mediagoblin.git] / mediagoblin / tools / crypto.py
CommitLineData
5907154a
E
1# GNU MediaGoblin -- federated, autonomous media hosting
2# Copyright (C) 2013 MediaGoblin contributors. See AUTHORS.
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
09102e07
BS
17import errno
18import itsdangerous
5907154a 19import logging
09102e07 20import os.path
5907154a 21import random
09102e07 22import tempfile
5907154a
E
23from mediagoblin import mg_globals
24
25_log = logging.getLogger(__name__)
26
27
28# Use the system (hardware-based) random number generator if it exists.
29# -- this optimization is lifted from Django
09102e07 30try:
5907154a 31 getrandbits = random.SystemRandom().getrandbits
09102e07 32except AttributeError:
5907154a
E
33 getrandbits = random.getrandbits
34
35
36__itsda_secret = None
37
38
09102e07 39def load_key(filename):
5907154a 40 global __itsda_secret
09102e07
BS
41 key_file = open(filename)
42 try:
43 __itsda_secret = key_file.read()
44 finally:
45 key_file.close()
5907154a 46
11780855 47
09102e07
BS
48def create_key(key_dir, key_filepath):
49 global __itsda_secret
50 old_umask = os.umask(077)
51 key_file = None
52 try:
53 if not os.path.isdir(key_dir):
54 os.makedirs(key_dir)
11780855 55 _log.info("Created %s", key_dir)
09102e07
BS
56 key = str(getrandbits(192))
57 key_file = tempfile.NamedTemporaryFile(dir=key_dir, suffix='.bin',
58 delete=False)
59 key_file.write(key)
60 key_file.flush()
61 os.rename(key_file.name, key_filepath)
62 key_file.close()
63 finally:
64 os.umask(old_umask)
65 if (key_file is not None) and (not key_file.closed):
66 key_file.close()
67 os.unlink(key_file.name)
68 __itsda_secret = key
69 _log.info("Saved new key for It's Dangerous")
70
11780855 71
09102e07
BS
72def setup_crypto():
73 global __itsda_secret
74 key_dir = mg_globals.app_config["crypto_path"]
75 key_filepath = os.path.join(key_dir, 'itsdangeroussecret.bin')
76 try:
77 load_key(key_filepath)
78 except IOError, error:
79 if error.errno != errno.ENOENT:
80 raise
81 create_key(key_dir, key_filepath)
5907154a 82
11780855 83
5907154a 84def get_timed_signer_url(namespace):
5a8aae3a
E
85 """
86 This gives a basic signing/verifying object.
87
88 The namespace makes sure signed tokens can't be used in
89 a different area. Like using a forgot-password-token as
90 a session cookie.
91
92 Basic usage:
93
94 .. code-block:: python
95
96 _signer = None
97 TOKEN_VALID_DAYS = 10
98 def setup():
99 global _signer
100 _signer = get_timed_signer_url("session cookie")
101 def create_token(obj):
102 return _signer.dumps(obj)
103 def parse_token(token):
104 # This might raise an exception in case
105 # of an invalid token, or an expired token.
106 return _signer.loads(token, max_age=TOKEN_VALID_DAYS*24*3600)
107
108 For more details see
109 http://pythonhosted.org/itsdangerous/#itsdangerous.URLSafeTimedSerializer
110 """
5907154a
E
111 assert __itsda_secret is not None
112 return itsdangerous.URLSafeTimedSerializer(__itsda_secret,
113 salt=namespace)