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/>.
21 from sqlalchemy
import or_
23 from mediagoblin
import mg_globals
24 from mediagoblin
.auth
import lib
as auth_lib
25 from mediagoblin
.tools
.crypto
import get_timed_signer_url
26 from mediagoblin
.db
.models
import User
27 from mediagoblin
.tools
.mail
import (normalize_email
, send_email
,
29 from mediagoblin
.tools
.template
import render_template
30 from mediagoblin
.tools
.translate
import lazy_pass_to_ugettext
as _
32 _log
= logging
.getLogger(__name__
)
35 def normalize_user_or_email_field(allow_email
=True, allow_user
=True):
37 Check if we were passed a field that matches a username and/or email
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
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.")
47 def _normalize_field(form
, field
):
48 email
= u
'@' in field
.data
49 if email
: # normalize email address casing
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
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
65 EMAIL_VERIFICATION_TEMPLATE
= (
67 u
"token={verification_key}")
70 def send_verification_email(user
, request
, email
=None,
73 Send the verification email to users to activate their accounts.
77 - request: the request
82 if not rendered_email
:
83 verification_key
= get_timed_signer_url('mail_verification_token') \
85 rendered_email
= render_template(
86 request
, 'mediagoblin/auth/verification_email.txt',
87 {'username': user
.username
,
88 'verification_url': EMAIL_VERIFICATION_TEMPLATE
.format(
89 uri
=request
.urlgen('mediagoblin.auth.verify_email',
91 verification_key
=verification_key
)})
93 # TODO: There is no error handling in place
95 mg_globals
.app_config
['email_sender_address'],
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!',
106 def basic_extra_validation(register_form
, *args
):
107 users_with_username
= User
.query
.filter_by(
108 username
=register_form
.data
['username']).count()
109 users_with_email
= User
.query
.filter_by(
110 email
=register_form
.data
['email']).count()
112 extra_validation_passes
= True
114 if users_with_username
:
115 register_form
.username
.errors
.append(
116 _(u
'Sorry, a user with that name already exists.'))
117 extra_validation_passes
= False
119 register_form
.email
.errors
.append(
120 _(u
'Sorry, a user with that email address already exists.'))
121 extra_validation_passes
= False
123 return extra_validation_passes
126 def register_user(request
, register_form
):
127 """ Handle user registration """
128 extra_validation_passes
= basic_extra_validation(register_form
)
130 if extra_validation_passes
:
133 user
.username
= register_form
.data
['username']
134 user
.email
= register_form
.data
['email']
135 user
.pw_hash
= auth_lib
.bcrypt_gen_password_hash(
136 register_form
.password
.data
)
137 user
.verification_key
= unicode(uuid
.uuid4())
141 request
.session
['user_id'] = unicode(user
.id)
142 request
.session
.save()
144 # send verification email
145 email_debug_message(request
)
146 send_verification_email(user
, request
)
153 def check_login_simple(username
, password
, username_might_be_email
=False):
154 search
= (User
.username
== username
)
155 if username_might_be_email
and ('@' in username
):
156 search
= or_(search
, User
.email
== username
)
157 user
= User
.query
.filter(search
).first()
159 _log
.info("User %r not found", username
)
160 auth_lib
.fake_login_attempt()
162 if not auth_lib
.bcrypt_check_password(password
, user
.pw_hash
):
163 _log
.warn("Wrong password for %r", username
)
165 _log
.info("Logging %r in", username
)