Fix #5382 - Add migration and fix so tombstones are removed from collections
[larjonas-mediagoblin.git] / mediagoblin / auth / tools.py
blob5a47dae4c8d7e8a9fa989dc60746428624ab3563
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
18 import logging
20 import six
21 import wtforms
22 from sqlalchemy import or_
24 from mediagoblin import mg_globals
25 from mediagoblin.tools.crypto import get_timed_signer_url
26 from mediagoblin.db.models import LocalUser, 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
34 _log = logging.getLogger(__name__)
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.
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.")
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
67 EMAIL_VERIFICATION_TEMPLATE = (
68 u"{uri}?"
69 u"token={verification_key}")
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.
77 Args:
78 - user: a user object
79 - request: the request
80 """
81 if not email:
82 email = user.email
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)})
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)
108 def basic_extra_validation(register_form, *args):
109 users_with_username = LocalUser.query.filter_by(
110 username=register_form.username.data).count()
111 users_with_email = LocalUser.query.filter_by(
112 email=register_form.email.data).count()
114 extra_validation_passes = True
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
125 return extra_validation_passes
128 def register_user(request, register_form):
129 """ Handle user registration """
130 extra_validation_passes = auth.extra_validation(register_form)
132 if extra_validation_passes:
133 # Create the user
134 user = auth.create_user(register_form)
136 # give the user the default privileges
137 user.all_privileges += get_default_privileges(user)
138 user.save()
140 # log the user in
141 request.session['user_id'] = six.text_type(user.id)
142 request.session.save()
144 # send verification email
145 email_debug_message(request)
146 send_verification_email(user, request)
148 return user
150 return None
152 def get_default_privileges(user):
153 instance_privilege_scheme = mg_globals.app_config['user_privilege_scheme']
154 default_privileges = [Privilege.query.filter(
155 Privilege.privilege_name==privilege_name).first()
156 for privilege_name in instance_privilege_scheme.split(',')]
157 default_privileges = [privilege for privilege in default_privileges if not privilege == None]
159 return default_privileges
161 def check_login_simple(username, password):
162 user = auth.get_user(username=username)
163 if not user:
164 _log.info("User %r not found", username)
165 hook_handle("auth_fake_login_attempt")
166 return None
167 if not auth.check_password(password, user.pw_hash):
168 _log.warn("Wrong password for %r", username)
169 return None
170 _log.info("Logging %r in", username)
171 return user
174 def check_auth_enabled():
175 if not hook_handle('authentication'):
176 _log.warning('No authentication is enabled')
177 return False
178 else:
179 return True
182 def no_auth_logout(request):
184 Log out the user if no authentication is enabled, but don't delete
185 the messages
187 if not request.app.auth and 'user_id' in request.session:
188 del request.session['user_id']
189 request.session.save()
192 def create_basic_user(form):
193 user = LocalUser()
194 user.username = form.username.data
195 user.email = form.email.data
196 user.save()
197 return user