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
.db
.models
import User
26 from mediagoblin
.tools
.mail
import (normalize_email
, send_email
,
28 from mediagoblin
.tools
.template
import render_template
29 from mediagoblin
.tools
.translate
import lazy_pass_to_ugettext
as _
31 _log
= logging
.getLogger(__name__
)
34 def normalize_user_or_email_field(allow_email
=True, allow_user
=True):
36 Check if we were passed a field that matches a username and/or email
39 This is useful for fields that can take either a username or email
40 address. Use the parameters if you want to only allow a username for
42 message
= _(u
'Invalid User name or email address.')
43 nomail_msg
= _(u
"This field does not take email addresses.")
44 nouser_msg
= _(u
"This field requires an email address.")
46 def _normalize_field(form
, field
):
47 email
= u
'@' in field
.data
48 if email
: # normalize email address casing
50 raise wtforms
.ValidationError(nomail_msg
)
51 wtforms
.validators
.Email()(form
, field
)
52 field
.data
= normalize_email(field
.data
)
53 else: # lower case user names
55 raise wtforms
.ValidationError(nouser_msg
)
56 wtforms
.validators
.Length(min=3, max=30)(form
, field
)
57 wtforms
.validators
.Regexp(r
'^\w+$')(form
, field
)
58 field
.data
= field
.data
.lower()
59 if field
.data
is None: # should not happen, but be cautious anyway
60 raise wtforms
.ValidationError(message
)
61 return _normalize_field
64 EMAIL_VERIFICATION_TEMPLATE
= (
65 u
"http://{host}{uri}?"
66 u
"userid={userid}&token={verification_key}")
69 def send_verification_email(user
, request
):
71 Send the verification email to users to activate their accounts.
75 - request: the request
77 rendered_email
= render_template(
78 request
, 'mediagoblin/auth/verification_email.txt',
79 {'username': user
.username
,
80 'verification_url': EMAIL_VERIFICATION_TEMPLATE
.format(
82 uri
=request
.urlgen('mediagoblin.auth.verify_email'),
83 userid
=unicode(user
.id),
84 verification_key
=user
.verification_key
)})
86 # TODO: There is no error handling in place
88 mg_globals
.app_config
['email_sender_address'],
91 # Due to the distributed nature of GNU MediaGoblin, we should
92 # find a way to send some additional information about the
93 # specific GNU MediaGoblin instance in the subject line. For
94 # example "GNU MediaGoblin @ Wandborg - [...]".
95 'GNU MediaGoblin - Verify your email!',
99 def basic_extra_validation(register_form
, *args
):
100 users_with_username
= User
.query
.filter_by(
101 username
=register_form
.data
['username']).count()
102 users_with_email
= User
.query
.filter_by(
103 email
=register_form
.data
['email']).count()
105 extra_validation_passes
= True
107 if users_with_username
:
108 register_form
.username
.errors
.append(
109 _(u
'Sorry, a user with that name already exists.'))
110 extra_validation_passes
= False
112 register_form
.email
.errors
.append(
113 _(u
'Sorry, a user with that email address already exists.'))
114 extra_validation_passes
= False
116 return extra_validation_passes
119 def register_user(request
, register_form
):
120 """ Handle user registration """
121 extra_validation_passes
= basic_extra_validation(register_form
)
123 if extra_validation_passes
:
126 user
.username
= register_form
.data
['username']
127 user
.email
= register_form
.data
['email']
128 user
.pw_hash
= auth_lib
.bcrypt_gen_password_hash(
129 register_form
.password
.data
)
130 user
.verification_key
= unicode(uuid
.uuid4())
134 request
.session
['user_id'] = unicode(user
.id)
135 request
.session
.save()
137 # send verification email
138 email_debug_message(request
)
139 send_verification_email(user
, request
)
146 def check_login_simple(username
, password
, username_might_be_email
=False):
147 search
= (User
.username
== username
)
148 if username_might_be_email
and ('@' in username
):
149 search
= or_(search
, User
.email
== username
)
150 user
= User
.query
.filter(search
).first()
152 _log
.info("User %r not found", username
)
153 auth_lib
.fake_login_attempt()
155 if not auth_lib
.bcrypt_check_password(password
, user
.pw_hash
):
156 _log
.warn("Wrong password for %r", username
)
158 _log
.info("Logging %r in", username
)