Commit | Line | Data |
---|---|---|
8e1e744d | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
12a100e4 | 2 | # Copyright (C) 2011 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 | ||
17 | import os | |
c15c9843 | 18 | import random |
6755f50e | 19 | |
51479a1d CAW |
20 | import bcrypt |
21 | ||
152a3bfa | 22 | from mediagoblin.tools.mail import send_email |
ae3bc7fa | 23 | from mediagoblin.tools.template import render_template |
6e7ce8d1 | 24 | from mediagoblin import mg_globals |
02d80437 | 25 | |
6755f50e CAW |
26 | |
27 | def 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 | ||
59 | def 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 | ||
74 | def 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 |
93 | EMAIL_VERIFICATION_TEMPLATE = ( |
94 | u"http://{host}{uri}?" | |
95 | u"userid={userid}&token={verification_key}") | |
96 | ||
02d80437 AM |
97 | def send_verification_email(user, request): |
98 | """ | |
99 | Send the verification email to users to activate their accounts. | |
100 | ||
101 | Args: | |
102 | - user: a user object | |
25ba955e | 103 | - request: the request |
02d80437 | 104 | """ |
23cc15c9 CAW |
105 | rendered_email = render_template( |
106 | request, 'mediagoblin/auth/verification_email.txt', | |
107 | {'username': user['username'], | |
108 | 'verification_url': EMAIL_VERIFICATION_TEMPLATE.format( | |
109 | host=request.host, | |
110 | uri=request.urlgen('mediagoblin.auth.verify_email'), | |
111 | userid=unicode(user['_id']), | |
112 | verification_key=user['verification_key'])}) | |
02d80437 AM |
113 | |
114 | # TODO: There is no error handling in place | |
115 | send_email( | |
6ae8b541 | 116 | mg_globals.app_config['email_sender_address'], |
02d80437 AM |
117 | [user['email']], |
118 | # TODO | |
119 | # Due to the distributed nature of GNU MediaGoblin, we should | |
25ba955e AV |
120 | # find a way to send some additional information about the |
121 | # specific GNU MediaGoblin instance in the subject line. For | |
122 | # example "GNU MediaGoblin @ Wandborg - [...]". | |
02d80437 | 123 | 'GNU MediaGoblin - Verify your email!', |
23cc15c9 | 124 | rendered_email) |
25ba955e AV |
125 | |
126 | ||
127 | EMAIL_FP_VERIFICATION_TEMPLATE = ( | |
128 | u"http://{host}{uri}?" | |
129 | u"userid={userid}&token={fp_verification_key}") | |
130 | ||
63bf10f9 | 131 | def send_fp_verification_email(user, request): |
25ba955e AV |
132 | """ |
133 | Send the verification email to users to change their password. | |
134 | ||
135 | Args: | |
136 | - user: a user object | |
137 | - request: the request | |
138 | """ | |
139 | rendered_email = render_template( | |
140 | request, 'mediagoblin/auth/fp_verification_email.txt', | |
141 | {'username': user['username'], | |
142 | 'verification_url': EMAIL_FP_VERIFICATION_TEMPLATE.format( | |
143 | host=request.host, | |
144 | uri=request.urlgen('mediagoblin.auth.verify_forgot_password'), | |
145 | userid=unicode(user['_id']), | |
146 | fp_verification_key=user['fp_verification_key'])}) | |
147 | ||
148 | # TODO: There is no error handling in place | |
149 | send_email( | |
f85909c0 | 150 | mg_globals.app_config['email_sender_address'], |
25ba955e AV |
151 | [user['email']], |
152 | 'GNU MediaGoblin - Change forgotten password!', | |
153 | rendered_email) | |
154 |