It's 2012 all up in here
[mediagoblin.git] / mediagoblin / auth / lib.py
CommitLineData
8e1e744d 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
6755f50e
CAW
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
17import os
c15c9843 18import random
6755f50e 19
51479a1d
CAW
20import bcrypt
21
152a3bfa 22from mediagoblin.tools.mail import send_email
ae3bc7fa 23from mediagoblin.tools.template import render_template
6e7ce8d1 24from mediagoblin import mg_globals
02d80437 25
6755f50e
CAW
26
27def bcrypt_check_password(raw_pass, stored_hash, extra_salt=None):
28 """
29 Check to see if this password matches.
30
31 Args:
32 - raw_pass: user submitted password to check for authenticity.
33 - stored_hash: The hash of the raw password (and possibly extra
34 salt) to check against
35 - extra_salt: (optional) If this password is with stored with a
36 non-database extra salt (probably in the config file) for extra
37 security, factor this into the check.
38
39 Returns:
40 True or False depending on success.
41 """
42 if extra_salt:
43 raw_pass = u"%s:%s" % (extra_salt, raw_pass)
44
45 hashed_pass = bcrypt.hashpw(raw_pass, stored_hash)
46
47 # Reduce risk of timing attacks by hashing again with a random
48 # number (thx to zooko on this advice, which I hopefully
49 # incorporated right.)
50 #
25ba955e 51 # See also:
6755f50e
CAW
52 rand_salt = bcrypt.gensalt(5)
53 randplus_stored_hash = bcrypt.hashpw(stored_hash, rand_salt)
54 randplus_hashed_pass = bcrypt.hashpw(hashed_pass, rand_salt)
55
56 return randplus_stored_hash == randplus_hashed_pass
57
58
59def bcrypt_gen_password_hash(raw_pass, extra_salt=None):
60 """
61 Generate a salt for this new password.
62
63 Args:
64 - raw_pass: user submitted password
65 - extra_salt: (optional) If this password is with stored with a
66 non-database extra salt
67 """
68 if extra_salt:
69 raw_pass = u"%s:%s" % (extra_salt, raw_pass)
70
e0bc23d3 71 return unicode(bcrypt.hashpw(raw_pass, bcrypt.gensalt()))
c15c9843
CAW
72
73
74def fake_login_attempt():
75 """
76 Pretend we're trying to login.
77
78 Nothing actually happens here, we're just trying to take up some
51479a1d
CAW
79 time, approximately the same amount of time as
80 bcrypt_check_password, so as to avoid figuring out what users are
81 on the system by intentionally faking logins a bunch of times.
c15c9843
CAW
82 """
83 rand_salt = bcrypt.gensalt(5)
84
85 hashed_pass = bcrypt.hashpw(str(random.random()), rand_salt)
86
87 randplus_stored_hash = bcrypt.hashpw(str(random.random()), rand_salt)
88 randplus_hashed_pass = bcrypt.hashpw(hashed_pass, rand_salt)
89
90 randplus_stored_hash == randplus_hashed_pass
02d80437
AM
91
92
2c3fd5c5
CAW
93EMAIL_VERIFICATION_TEMPLATE = (
94 u"http://{host}{uri}?"
95 u"userid={userid}&token={verification_key}")
96
243c3843 97
02d80437
AM
98def send_verification_email(user, request):
99 """
100 Send the verification email to users to activate their accounts.
101
102 Args:
103 - user: a user object
25ba955e 104 - request: the request
02d80437 105 """
23cc15c9
CAW
106 rendered_email = render_template(
107 request, 'mediagoblin/auth/verification_email.txt',
5a4e3ff1 108 {'username': user.username,
23cc15c9
CAW
109 'verification_url': EMAIL_VERIFICATION_TEMPLATE.format(
110 host=request.host,
111 uri=request.urlgen('mediagoblin.auth.verify_email'),
eabe6b67 112 userid=unicode(user._id),
00bb9550 113 verification_key=user.verification_key)})
02d80437
AM
114
115 # TODO: There is no error handling in place
116 send_email(
6ae8b541 117 mg_globals.app_config['email_sender_address'],
809cbfc5 118 [user.email],
02d80437
AM
119 # TODO
120 # Due to the distributed nature of GNU MediaGoblin, we should
25ba955e
AV
121 # find a way to send some additional information about the
122 # specific GNU MediaGoblin instance in the subject line. For
123 # example "GNU MediaGoblin @ Wandborg - [...]".
02d80437 124 'GNU MediaGoblin - Verify your email!',
23cc15c9 125 rendered_email)
25ba955e
AV
126
127
128EMAIL_FP_VERIFICATION_TEMPLATE = (
129 u"http://{host}{uri}?"
130 u"userid={userid}&token={fp_verification_key}")
131
243c3843 132
63bf10f9 133def send_fp_verification_email(user, request):
25ba955e
AV
134 """
135 Send the verification email to users to change their password.
136
137 Args:
138 - user: a user object
139 - request: the request
140 """
141 rendered_email = render_template(
142 request, 'mediagoblin/auth/fp_verification_email.txt',
5a4e3ff1 143 {'username': user.username,
25ba955e
AV
144 'verification_url': EMAIL_FP_VERIFICATION_TEMPLATE.format(
145 host=request.host,
146 uri=request.urlgen('mediagoblin.auth.verify_forgot_password'),
eabe6b67 147 userid=unicode(user._id),
dc39e455 148 fp_verification_key=user.fp_verification_key)})
25ba955e
AV
149
150 # TODO: There is no error handling in place
151 send_email(
f85909c0 152 mg_globals.app_config['email_sender_address'],
809cbfc5 153 [user.email],
25ba955e
AV
154 'GNU MediaGoblin - Change forgotten password!',
155 rendered_email)