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