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/>.
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
,
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,
39 """Check if we were passed a field that matches a username and/or email
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
47 If is_login is True, does not check the length of username.
50 message
= _(u
'Invalid User name or email address.')
51 nomail_msg
= _(u
"This field does not take email addresses.")
52 nouser_msg
= _(u
"This field requires an email address.")
54 def _normalize_field(form
, field
):
55 email
= u
'@' in field
.data
56 if email
: # normalize email address casing
58 raise wtforms
.ValidationError(nomail_msg
)
59 wtforms
.validators
.Email()(form
, field
)
60 field
.data
= normalize_email(field
.data
)
61 else: # lower case user names
63 raise wtforms
.ValidationError(nouser_msg
)
65 wtforms
.validators
.Length(min=3, max=30)(form
, field
)
66 wtforms
.validators
.Regexp(r
'^[-_\w]+$')(form
, field
)
67 field
.data
= field
.data
.lower()
68 if field
.data
is None: # should not happen, but be cautious anyway
69 raise wtforms
.ValidationError(message
)
70 return _normalize_field
73 EMAIL_VERIFICATION_TEMPLATE
= (
75 u
"token={verification_key}")
78 def send_verification_email(user
, request
, email
=None,
81 Send the verification email to users to activate their accounts.
85 - request: the request
90 if not rendered_email
:
91 verification_key
= get_timed_signer_url('mail_verification_token') \
93 rendered_email
= render_template(
94 request
, 'mediagoblin/auth/verification_email.txt',
95 {'username': user
.username
,
96 'verification_url': EMAIL_VERIFICATION_TEMPLATE
.format(
97 uri
=request
.urlgen('mediagoblin.auth.verify_email',
99 verification_key
=verification_key
)})
101 # TODO: There is no error handling in place
103 mg_globals
.app_config
['email_sender_address'],
106 # Due to the distributed nature of GNU MediaGoblin, we should
107 # find a way to send some additional information about the
108 # specific GNU MediaGoblin instance in the subject line. For
109 # example "GNU MediaGoblin @ Wandborg - [...]".
110 'GNU MediaGoblin - Verify your email!',
114 def basic_extra_validation(register_form
, *args
):
115 users_with_username
= LocalUser
.query
.filter_by(
116 username
=register_form
.username
.data
).count()
117 users_with_email
= LocalUser
.query
.filter_by(
118 email
=register_form
.email
.data
).count()
120 extra_validation_passes
= True
122 if users_with_username
:
123 register_form
.username
.errors
.append(
124 _(u
'Sorry, a user with that name already exists.'))
125 extra_validation_passes
= False
127 register_form
.email
.errors
.append(
128 _(u
'Sorry, a user with that email address already exists.'))
129 extra_validation_passes
= False
131 return extra_validation_passes
134 def register_user(request
, register_form
):
135 """ Handle user registration """
136 extra_validation_passes
= auth
.extra_validation(register_form
)
138 if extra_validation_passes
:
140 user
= auth
.create_user(register_form
)
142 # give the user the default privileges
143 user
.all_privileges
+= get_default_privileges(user
)
147 request
.session
['user_id'] = six
.text_type(user
.id)
148 request
.session
.save()
150 # send verification email
151 email_debug_message(request
)
152 send_verification_email(user
, request
)
158 def get_default_privileges(user
):
159 instance_privilege_scheme
= mg_globals
.app_config
['user_privilege_scheme']
160 default_privileges
= [Privilege
.query
.filter(
161 Privilege
.privilege_name
==privilege_name
).first()
162 for privilege_name
in instance_privilege_scheme
.split(',')]
163 default_privileges
= [privilege
for privilege
in default_privileges
if not privilege
== None]
165 return default_privileges
167 def check_login_simple(username
, password
):
168 user
= auth
.get_user(username
=username
)
170 _log
.info("User %r not found", username
)
171 hook_handle("auth_fake_login_attempt")
173 if not auth
.check_password(password
, user
.pw_hash
):
174 _log
.warn("Wrong password for %r", username
)
176 _log
.info("Logging %r in", username
)
180 def check_auth_enabled():
181 if not hook_handle('authentication'):
182 _log
.warning('No authentication is enabled')
188 def no_auth_logout(request
):
190 Log out the user if no authentication is enabled, but don't delete
193 if not request
.app
.auth
and 'user_id' in request
.session
:
194 del request
.session
['user_id']
195 request
.session
.save()
198 def create_basic_user(form
):
200 user
.username
= form
.username
.data
201 user
.email
= form
.email
.data