Commit | Line | Data |
---|---|---|
7cb7653c RE |
1 | # GNU MediaGoblin -- federated, autonomous media hosting |
2 | # Copyright (C) 2011, 2012 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 | ||
744f1c83 | 17 | import logging |
7cb7653c RE |
18 | import wtforms |
19 | ||
92783bc1 | 20 | from mediagoblin import mg_globals |
69b888c2 | 21 | from mediagoblin.tools.crypto import get_timed_signer_url |
310a44d5 RE |
22 | from mediagoblin.db.models import User |
23 | from mediagoblin.tools.mail import (normalize_email, send_email, | |
24 | email_debug_message) | |
c3e3882e | 25 | from mediagoblin.tools.template import render_template |
d90f44d2 | 26 | from mediagoblin.tools.translate import lazy_pass_to_ugettext as _ |
744f1c83 | 27 | from mediagoblin.tools.pluginapi import hook_handle |
c3e3882e | 28 | from mediagoblin import auth |
bcd10ad6 RE |
29 | |
30 | _log = logging.getLogger(__name__) | |
744f1c83 | 31 | |
7cb7653c RE |
32 | |
33 | def normalize_user_or_email_field(allow_email=True, allow_user=True): | |
34 | """ | |
35 | Check if we were passed a field that matches a username and/or email | |
36 | pattern. | |
37 | ||
38 | This is useful for fields that can take either a username or email | |
39 | address. Use the parameters if you want to only allow a username for | |
40 | instance""" | |
41 | message = _(u'Invalid User name or email address.') | |
42 | nomail_msg = _(u"This field does not take email addresses.") | |
43 | nouser_msg = _(u"This field requires an email address.") | |
44 | ||
45 | def _normalize_field(form, field): | |
46 | email = u'@' in field.data | |
47 | if email: # normalize email address casing | |
48 | if not allow_email: | |
49 | raise wtforms.ValidationError(nomail_msg) | |
50 | wtforms.validators.Email()(form, field) | |
51 | field.data = normalize_email(field.data) | |
52 | else: # lower case user names | |
53 | if not allow_user: | |
54 | raise wtforms.ValidationError(nouser_msg) | |
55 | wtforms.validators.Length(min=3, max=30)(form, field) | |
56 | wtforms.validators.Regexp(r'^\w+$')(form, field) | |
57 | field.data = field.data.lower() | |
58 | if field.data is None: # should not happen, but be cautious anyway | |
59 | raise wtforms.ValidationError(message) | |
60 | return _normalize_field | |
744f1c83 RE |
61 | |
62 | ||
92783bc1 | 63 | EMAIL_VERIFICATION_TEMPLATE = ( |
342f06f7 RE |
64 | u"{uri}?" |
65 | u"token={verification_key}") | |
92783bc1 RE |
66 | |
67 | ||
8087f56b RE |
68 | def send_verification_email(user, request, email=None, |
69 | rendered_email=None): | |
92783bc1 RE |
70 | """ |
71 | Send the verification email to users to activate their accounts. | |
72 | ||
73 | Args: | |
74 | - user: a user object | |
75 | - request: the request | |
76 | """ | |
8087f56b RE |
77 | if not email: |
78 | email = user.email | |
79 | ||
80 | if not rendered_email: | |
342f06f7 RE |
81 | verification_key = get_timed_signer_url('mail_verification_token') \ |
82 | .dumps(user.id) | |
8087f56b RE |
83 | rendered_email = render_template( |
84 | request, 'mediagoblin/auth/verification_email.txt', | |
85 | {'username': user.username, | |
86 | 'verification_url': EMAIL_VERIFICATION_TEMPLATE.format( | |
342f06f7 RE |
87 | uri=request.urlgen('mediagoblin.auth.verify_email', |
88 | qualified=True), | |
89 | verification_key=verification_key)}) | |
92783bc1 RE |
90 | |
91 | # TODO: There is no error handling in place | |
92 | send_email( | |
93 | mg_globals.app_config['email_sender_address'], | |
8087f56b | 94 | [email], |
92783bc1 RE |
95 | # TODO |
96 | # Due to the distributed nature of GNU MediaGoblin, we should | |
97 | # find a way to send some additional information about the | |
98 | # specific GNU MediaGoblin instance in the subject line. For | |
99 | # example "GNU MediaGoblin @ Wandborg - [...]". | |
100 | 'GNU MediaGoblin - Verify your email!', | |
101 | rendered_email) | |
310a44d5 RE |
102 | |
103 | ||
f855efff | 104 | EMAIL_FP_VERIFICATION_TEMPLATE = ( |
61741697 RE |
105 | u"{uri}?" |
106 | u"token={fp_verification_key}") | |
f855efff RE |
107 | |
108 | ||
109 | def send_fp_verification_email(user, request): | |
110 | """ | |
111 | Send the verification email to users to change their password. | |
112 | ||
113 | Args: | |
114 | - user: a user object | |
115 | - request: the request | |
116 | """ | |
61741697 RE |
117 | fp_verification_key = get_timed_signer_url('mail_verification_token') \ |
118 | .dumps(user.id) | |
5adb906a | 119 | |
f855efff RE |
120 | rendered_email = render_template( |
121 | request, 'mediagoblin/auth/fp_verification_email.txt', | |
122 | {'username': user.username, | |
123 | 'verification_url': EMAIL_FP_VERIFICATION_TEMPLATE.format( | |
61741697 RE |
124 | uri=request.urlgen('mediagoblin.auth.verify_forgot_password', |
125 | qualified=True), | |
126 | fp_verification_key=fp_verification_key)}) | |
f855efff RE |
127 | |
128 | # TODO: There is no error handling in place | |
129 | send_email( | |
130 | mg_globals.app_config['email_sender_address'], | |
131 | [user.email], | |
132 | 'GNU MediaGoblin - Change forgotten password!', | |
133 | rendered_email) | |
134 | ||
135 | ||
c3e3882e RE |
136 | def basic_extra_validation(register_form, *args): |
137 | users_with_username = User.query.filter_by( | |
569873d8 | 138 | username=register_form.username.data).count() |
c3e3882e | 139 | users_with_email = User.query.filter_by( |
569873d8 | 140 | email=register_form.email.data).count() |
c3e3882e RE |
141 | |
142 | extra_validation_passes = True | |
143 | ||
144 | if users_with_username: | |
145 | register_form.username.errors.append( | |
146 | _(u'Sorry, a user with that name already exists.')) | |
147 | extra_validation_passes = False | |
148 | if users_with_email: | |
149 | register_form.email.errors.append( | |
150 | _(u'Sorry, a user with that email address already exists.')) | |
151 | extra_validation_passes = False | |
152 | ||
153 | return extra_validation_passes | |
154 | ||
155 | ||
5784c12d RE |
156 | def register_user(request, register_form): |
157 | """ Handle user registration """ | |
158 | extra_validation_passes = auth.extra_validation(register_form) | |
159 | ||
160 | if extra_validation_passes: | |
161 | # Create the user | |
162 | user = auth.create_user(register_form) | |
163 | ||
164 | # log the user in | |
165 | request.session['user_id'] = unicode(user.id) | |
166 | request.session.save() | |
167 | ||
168 | # send verification email | |
169 | email_debug_message(request) | |
170 | send_verification_email(user, request) | |
0578d8b3 | 171 | |
5784c12d RE |
172 | return user |
173 | ||
174 | return None | |
175 | ||
176 | ||
f81206df | 177 | def check_login_simple(username, password): |
b1e02e0a | 178 | user = auth.get_user(username=username) |
1d321f1c RE |
179 | if not user: |
180 | _log.info("User %r not found", username) | |
e4deacd9 | 181 | hook_handle("auth_fake_login_attempt") |
1d321f1c | 182 | return None |
cdc6b571 | 183 | if not auth.check_password(password, user.pw_hash): |
1d321f1c RE |
184 | _log.warn("Wrong password for %r", username) |
185 | return None | |
186 | _log.info("Logging %r in", username) | |
187 | return user | |
bd7fe0c2 RE |
188 | |
189 | ||
bd7fe0c2 | 190 | def check_auth_enabled(): |
e4deacd9 | 191 | if not hook_handle('authentication'): |
bd7fe0c2 RE |
192 | _log.warning('No authentication is enabled') |
193 | return False | |
194 | else: | |
195 | return True | |
196 | ||
197 | ||
198 | def no_auth_logout(request): | |
5101c469 | 199 | """Log out the user if authentication_disabled, but don't delete the messages""" |
8ce8faaf RE |
200 | if not mg_globals.app.auth and 'user_id' in request.session: |
201 | del request.session['user_id'] | |
202 | request.session.save() | |
5adb906a RE |
203 | |
204 | ||
205 | def create_basic_user(form): | |
206 | user = User() | |
207 | user.username = form.username.data | |
208 | user.email = form.email.data | |
209 | user.save() | |
210 | return user |