tools.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. # GNU MediaGoblin -- federated, autonomous media hosting
  2. # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
  3. #
  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.
  8. #
  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.
  13. #
  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/>.
  16. import logging
  17. import six
  18. import wtforms
  19. from sqlalchemy import or_
  20. from mediagoblin import mg_globals
  21. from mediagoblin.tools.crypto import get_timed_signer_url
  22. from mediagoblin.db.models import User, Privilege
  23. from mediagoblin.tools.mail import (normalize_email, send_email,
  24. email_debug_message)
  25. from mediagoblin.tools.template import render_template
  26. from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
  27. from mediagoblin.tools.pluginapi import hook_handle
  28. from mediagoblin import auth
  29. _log = logging.getLogger(__name__)
  30. def normalize_user_or_email_field(allow_email=True, allow_user=True):
  31. """
  32. Check if we were passed a field that matches a username and/or email
  33. pattern.
  34. This is useful for fields that can take either a username or email
  35. address. Use the parameters if you want to only allow a username for
  36. instance"""
  37. message = _(u'Invalid User name or email address.')
  38. nomail_msg = _(u"This field does not take email addresses.")
  39. nouser_msg = _(u"This field requires an email address.")
  40. def _normalize_field(form, field):
  41. email = u'@' in field.data
  42. if email: # normalize email address casing
  43. if not allow_email:
  44. raise wtforms.ValidationError(nomail_msg)
  45. wtforms.validators.Email()(form, field)
  46. field.data = normalize_email(field.data)
  47. else: # lower case user names
  48. if not allow_user:
  49. raise wtforms.ValidationError(nouser_msg)
  50. wtforms.validators.Length(min=3, max=30)(form, field)
  51. wtforms.validators.Regexp(r'^\w+$')(form, field)
  52. field.data = field.data.lower()
  53. if field.data is None: # should not happen, but be cautious anyway
  54. raise wtforms.ValidationError(message)
  55. return _normalize_field
  56. EMAIL_VERIFICATION_TEMPLATE = (
  57. u"{uri}?"
  58. u"token={verification_key}")
  59. def send_verification_email(user, request, email=None,
  60. rendered_email=None):
  61. """
  62. Send the verification email to users to activate their accounts.
  63. Args:
  64. - user: a user object
  65. - request: the request
  66. """
  67. if not email:
  68. email = user.email
  69. if not rendered_email:
  70. verification_key = get_timed_signer_url('mail_verification_token') \
  71. .dumps(user.id)
  72. rendered_email = render_template(
  73. request, 'mediagoblin/auth/verification_email.txt',
  74. {'username': user.username,
  75. 'verification_url': EMAIL_VERIFICATION_TEMPLATE.format(
  76. uri=request.urlgen('mediagoblin.auth.verify_email',
  77. qualified=True),
  78. verification_key=verification_key)})
  79. # TODO: There is no error handling in place
  80. send_email(
  81. mg_globals.app_config['email_sender_address'],
  82. [email],
  83. # TODO
  84. # Due to the distributed nature of GNU MediaGoblin, we should
  85. # find a way to send some additional information about the
  86. # specific GNU MediaGoblin instance in the subject line. For
  87. # example "GNU MediaGoblin @ Wandborg - [...]".
  88. 'GNU MediaGoblin - Verify your email!',
  89. rendered_email)
  90. def basic_extra_validation(register_form, *args):
  91. users_with_username = User.query.filter_by(
  92. username=register_form.username.data).count()
  93. users_with_email = User.query.filter_by(
  94. email=register_form.email.data).count()
  95. extra_validation_passes = True
  96. if users_with_username:
  97. register_form.username.errors.append(
  98. _(u'Sorry, a user with that name already exists.'))
  99. extra_validation_passes = False
  100. if users_with_email:
  101. register_form.email.errors.append(
  102. _(u'Sorry, a user with that email address already exists.'))
  103. extra_validation_passes = False
  104. return extra_validation_passes
  105. def register_user(request, register_form):
  106. """ Handle user registration """
  107. extra_validation_passes = auth.extra_validation(register_form)
  108. if extra_validation_passes:
  109. # Create the user
  110. user = auth.create_user(register_form)
  111. # give the user the default privileges
  112. user.all_privileges += get_default_privileges(user)
  113. user.save()
  114. # log the user in
  115. request.session['user_id'] = six.text_type(user.id)
  116. request.session.save()
  117. # send verification email
  118. email_debug_message(request)
  119. send_verification_email(user, request)
  120. return user
  121. return None
  122. def get_default_privileges(user):
  123. instance_privilege_scheme = mg_globals.app_config['user_privilege_scheme']
  124. default_privileges = [Privilege.query.filter(
  125. Privilege.privilege_name==privilege_name).first()
  126. for privilege_name in instance_privilege_scheme.split(',')]
  127. default_privileges = [privilege for privilege in default_privileges if not privilege == None]
  128. return default_privileges
  129. def check_login_simple(username, password):
  130. user = auth.get_user(username=username)
  131. if not user:
  132. _log.info("User %r not found", username)
  133. hook_handle("auth_fake_login_attempt")
  134. return None
  135. if not auth.check_password(password, user.pw_hash):
  136. _log.warn("Wrong password for %r", username)
  137. return None
  138. _log.info("Logging %r in", username)
  139. return user
  140. def check_auth_enabled():
  141. if not hook_handle('authentication'):
  142. _log.warning('No authentication is enabled')
  143. return False
  144. else:
  145. return True
  146. def no_auth_logout(request):
  147. """
  148. Log out the user if no authentication is enabled, but don't delete
  149. the messages
  150. """
  151. if not request.app.auth and 'user_id' in request.session:
  152. del request.session['user_id']
  153. request.session.save()
  154. def create_basic_user(form):
  155. user = User()
  156. user.username = form.username.data
  157. user.email = form.email.data
  158. user.save()
  159. return user