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 for
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
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
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
= (
69 u
"token={verification_key}")
72 def send_verification_email(user
, request
, email
=None,
75 Send the verification email to users to activate their accounts.
79 - request: the request
84 if not rendered_email
:
85 verification_key
= get_timed_signer_url('mail_verification_token') \
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',
93 verification_key
=verification_key
)})
95 # TODO: There is no error handling in place
97 mg_globals
.app_config
['email_sender_address'],
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!',
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
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
:
134 user
= auth
.create_user(register_form
)
136 # give the user the default privileges
137 user
.all_privileges
+= get_default_privileges(user
)
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
)
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
)
164 _log
.info("User %r not found", username
)
165 hook_handle("auth_fake_login_attempt")
167 if not auth
.check_password(password
, user
.pw_hash
):
168 _log
.warn("Wrong password for %r", username
)
170 _log
.info("Logging %r in", username
)
174 def check_auth_enabled():
175 if not hook_handle('authentication'):
176 _log
.warning('No authentication is enabled')
182 def no_auth_logout(request
):
184 Log out the user if no authentication is enabled, but don't delete
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
):
194 user
.username
= form
.username
.data
195 user
.email
= form
.email
.data