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