no need for user might be email kwarg in check_login_simple
[mediagoblin.git] / mediagoblin / auth / tools.py
CommitLineData
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 17import logging
1d321f1c 18
7cb7653c
RE
19import wtforms
20
92783bc1 21from mediagoblin import mg_globals
310a44d5
RE
22from mediagoblin.db.models import User
23from mediagoblin.tools.mail import (normalize_email, send_email,
24 email_debug_message)
7cb7653c 25from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
c3e3882e 26from mediagoblin.tools.template import render_template
744f1c83 27from mediagoblin.tools.pluginapi import hook_handle
c3e3882e 28from mediagoblin import auth
bcd10ad6
RE
29
30_log = logging.getLogger(__name__)
744f1c83
RE
31
32_log = logging.getLogger(__name__)
7cb7653c 33
117a27a3
RE
34_log = logging.getLogger(__name__)
35
7cb7653c
RE
36
37def 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
744f1c83
RE
65
66
f644293e
RE
67class AuthError(Exception):
68 def __init__(self):
69 self.value = 'No Authentication Plugin is enabled and no_auth = false'\
70 ' in config!'
71
72 def __str__(self):
73 return repr(self.value)
74
75
744f1c83
RE
76def check_auth_enabled():
77 no_auth = mg_globals.app_config['no_auth']
c3e3882e 78 auth_plugin = hook_handle('authentication')
744f1c83
RE
79
80 if no_auth == 'false' and not auth_plugin:
f644293e 81 raise AuthError
744f1c83
RE
82
83 if no_auth == 'true' and not auth_plugin:
84 _log.warning('No authentication is enabled')
85 return False
86 else:
87 return True
c9dec8b3
RE
88
89
90def no_auth_logout(request):
91 """Log out the user if in no_auth mode"""
92 if not mg_globals.app.auth:
93 request.session.delete()
c3e3882e
RE
94
95
92783bc1
RE
96EMAIL_VERIFICATION_TEMPLATE = (
97 u"http://{host}{uri}?"
98 u"userid={userid}&token={verification_key}")
99
100
101def send_verification_email(user, request):
102 """
103 Send the verification email to users to activate their accounts.
104
105 Args:
106 - user: a user object
107 - request: the request
108 """
109 rendered_email = render_template(
110 request, 'mediagoblin/auth/verification_email.txt',
111 {'username': user.username,
112 'verification_url': EMAIL_VERIFICATION_TEMPLATE.format(
113 host=request.host,
114 uri=request.urlgen('mediagoblin.auth.verify_email'),
115 userid=unicode(user.id),
116 verification_key=user.verification_key)})
117
118 # TODO: There is no error handling in place
119 send_email(
120 mg_globals.app_config['email_sender_address'],
121 [user.email],
122 # TODO
123 # Due to the distributed nature of GNU MediaGoblin, we should
124 # find a way to send some additional information about the
125 # specific GNU MediaGoblin instance in the subject line. For
126 # example "GNU MediaGoblin @ Wandborg - [...]".
127 'GNU MediaGoblin - Verify your email!',
128 rendered_email)
310a44d5
RE
129
130
f855efff
RE
131EMAIL_FP_VERIFICATION_TEMPLATE = (
132 u"http://{host}{uri}?"
133 u"userid={userid}&token={fp_verification_key}")
134
135
136def send_fp_verification_email(user, request):
137 """
138 Send the verification email to users to change their password.
139
140 Args:
141 - user: a user object
142 - request: the request
143 """
144 rendered_email = render_template(
145 request, 'mediagoblin/auth/fp_verification_email.txt',
146 {'username': user.username,
147 'verification_url': EMAIL_FP_VERIFICATION_TEMPLATE.format(
148 host=request.host,
149 uri=request.urlgen('mediagoblin.auth.verify_forgot_password'),
150 userid=unicode(user.id),
151 fp_verification_key=user.fp_verification_key)})
152
153 # TODO: There is no error handling in place
154 send_email(
155 mg_globals.app_config['email_sender_address'],
156 [user.email],
157 'GNU MediaGoblin - Change forgotten password!',
158 rendered_email)
159
160
c3e3882e
RE
161def basic_extra_validation(register_form, *args):
162 users_with_username = User.query.filter_by(
569873d8 163 username=register_form.username.data).count()
c3e3882e 164 users_with_email = User.query.filter_by(
569873d8 165 email=register_form.email.data).count()
c3e3882e
RE
166
167 extra_validation_passes = True
168
169 if users_with_username:
170 register_form.username.errors.append(
171 _(u'Sorry, a user with that name already exists.'))
172 extra_validation_passes = False
173 if users_with_email:
174 register_form.email.errors.append(
175 _(u'Sorry, a user with that email address already exists.'))
176 extra_validation_passes = False
177
178 return extra_validation_passes
179
180
5784c12d
RE
181def register_user(request, register_form):
182 """ Handle user registration """
183 extra_validation_passes = auth.extra_validation(register_form)
184
185 if extra_validation_passes:
186 # Create the user
187 user = auth.create_user(register_form)
188
189 # log the user in
190 request.session['user_id'] = unicode(user.id)
191 request.session.save()
192
193 # send verification email
194 email_debug_message(request)
195 send_verification_email(user, request)
196 return user
197
198 return None
199
200
f81206df 201def check_login_simple(username, password):
cdc6b571 202 user = auth.get_user(username)
1d321f1c
RE
203 if not user:
204 _log.info("User %r not found", username)
cdc6b571 205 auth.fake_login_attempt()
1d321f1c 206 return None
cdc6b571 207 if not auth.check_password(password, user.pw_hash):
1d321f1c
RE
208 _log.warn("Wrong password for %r", username)
209 return None
210 _log.info("Logging %r in", username)
211 return user