1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 Free Software Foundation, Inc
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.
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.
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/>.
22 from mediagoblin
.util
import send_email
23 from mediagoblin
import globals as mgoblin_globals
26 def bcrypt_check_password(raw_pass
, stored_hash
, extra_salt
=None):
28 Check to see if this password matches.
31 - raw_pass: user submitted password to check for authenticity.
32 - stored_hash: The hash of the raw password (and possibly extra
33 salt) to check against
34 - extra_salt: (optional) If this password is with stored with a
35 non-database extra salt (probably in the config file) for extra
36 security, factor this into the check.
39 True or False depending on success.
42 raw_pass
= u
"%s:%s" % (extra_salt
, raw_pass
)
44 hashed_pass
= bcrypt
.hashpw(raw_pass
, stored_hash
)
46 # Reduce risk of timing attacks by hashing again with a random
47 # number (thx to zooko on this advice, which I hopefully
48 # incorporated right.)
51 rand_salt
= bcrypt
.gensalt(5)
52 randplus_stored_hash
= bcrypt
.hashpw(stored_hash
, rand_salt
)
53 randplus_hashed_pass
= bcrypt
.hashpw(hashed_pass
, rand_salt
)
55 return randplus_stored_hash
== randplus_hashed_pass
58 def bcrypt_gen_password_hash(raw_pass
, extra_salt
=None):
60 Generate a salt for this new password.
63 - raw_pass: user submitted password
64 - extra_salt: (optional) If this password is with stored with a
65 non-database extra salt
68 raw_pass
= u
"%s:%s" % (extra_salt
, raw_pass
)
70 return unicode(bcrypt
.hashpw(raw_pass
, bcrypt
.gensalt()))
73 def fake_login_attempt():
75 Pretend we're trying to login.
77 Nothing actually happens here, we're just trying to take up some
78 time, approximately the same amount of time as
79 bcrypt_check_password, so as to avoid figuring out what users are
80 on the system by intentionally faking logins a bunch of times.
82 rand_salt
= bcrypt
.gensalt(5)
84 hashed_pass
= bcrypt
.hashpw(str(random
.random()), rand_salt
)
86 randplus_stored_hash
= bcrypt
.hashpw(str(random
.random()), rand_salt
)
87 randplus_hashed_pass
= bcrypt
.hashpw(hashed_pass
, rand_salt
)
89 randplus_stored_hash
== randplus_hashed_pass
92 EMAIL_VERIFICATION_TEMPLATE
= (
93 u
"http://{host}{uri}?"
94 u
"userid={userid}&token={verification_key}")
96 def send_verification_email(user
, request
):
98 Send the verification email to users to activate their accounts.
101 - user: a user object
102 - request: the request
105 email_template
= request
.template_env
.get_template(
106 'mediagoblin/auth/verification_email.txt')
108 # TODO: There is no error handling in place
110 mgoblin_globals
.email_sender_address
,
113 # Due to the distributed nature of GNU MediaGoblin, we should
114 # find a way to send some additional information about the
115 # specific GNU MediaGoblin instance in the subject line. For
116 # example "GNU MediaGoblin @ Wandborg - [...]".
117 'GNU MediaGoblin - Verify your email!',
118 email_template
.render(
119 username
=user
['username'],
120 verification_url
=EMAIL_VERIFICATION_TEMPLATE
.format(
122 uri
=request
.urlgen('mediagoblin.auth.verify_email'),
123 userid
=unicode(user
['_id']),
124 verification_key
=user
['verification_key'])))