Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
243c3843 | 97 | |
02d80437 AM |
98 | def 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 | ||
128 | EMAIL_FP_VERIFICATION_TEMPLATE = ( | |
129 | u"http://{host}{uri}?" | |
130 | u"userid={userid}&token={fp_verification_key}") | |
131 | ||
243c3843 | 132 | |
63bf10f9 | 133 | def 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) |