You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.5 KiB
190 lines
6.5 KiB
import datetime
|
|
from django.core.mail import EmailMessage
|
|
from django.conf import settings
|
|
from django.contrib.sites.models import Site, RequestSite
|
|
from django.core import signing
|
|
from django.core.mail import send_mail
|
|
from django.template import Context
|
|
from django.core.urlresolvers import reverse, reverse_lazy
|
|
from django.shortcuts import get_object_or_404, redirect
|
|
from django.http import Http404
|
|
from django.template import loader
|
|
from django.utils import timezone
|
|
from django.views import generic
|
|
from django.template.loader import get_template
|
|
from email.MIMEImage import MIMEImage
|
|
|
|
from .forms import PasswordRecoveryForm, PasswordResetForm
|
|
from .utils import get_user_model, get_username
|
|
from .signals import user_recovers_password
|
|
|
|
|
|
class SaltMixin(object):
|
|
salt = 'password_recovery'
|
|
url_salt = 'password_recovery_url'
|
|
|
|
|
|
def loads_with_timestamp(value, salt):
|
|
"""Returns the unsigned value along with its timestamp, the time when it
|
|
got dumped."""
|
|
try:
|
|
signing.loads(value, salt=salt, max_age=-1)
|
|
except signing.SignatureExpired as e:
|
|
age = float(str(e).split('Signature age ')[1].split(' >')[0])
|
|
timestamp = timezone.now() - datetime.timedelta(seconds=age)
|
|
return timestamp, signing.loads(value, salt=salt)
|
|
|
|
|
|
class RecoverDone(SaltMixin, generic.TemplateView):
|
|
template_name = "password_reset/reset_sent.html"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super(RecoverDone, self).get_context_data(**kwargs)
|
|
try:
|
|
ctx['timestamp'], ctx['email'] = loads_with_timestamp(
|
|
self.kwargs['signature'], salt=self.url_salt,
|
|
)
|
|
except signing.BadSignature:
|
|
raise Http404
|
|
return ctx
|
|
recover_done = RecoverDone.as_view()
|
|
|
|
|
|
class Recover(SaltMixin, generic.FormView):
|
|
case_sensitive = True
|
|
form_class = PasswordRecoveryForm
|
|
template_name = 'password_reset/recovery_form.html'
|
|
success_url_name = 'password_reset_sent'
|
|
email_template_name = 'password_reset/recovery_email_expo.html'
|
|
email_subject_template_name = 'password_reset/recovery_email_subject.txt'
|
|
search_fields = ['username', 'email']
|
|
|
|
def get_success_url(self):
|
|
return reverse(self.success_url_name, args=[self.mail_signature])
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs['url'] = self.request.get_full_path()
|
|
return super(Recover, self).get_context_data(**kwargs)
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super(Recover, self).get_form_kwargs()
|
|
kwargs.update({
|
|
'case_sensitive': self.case_sensitive,
|
|
'search_fields': self.search_fields,
|
|
})
|
|
return kwargs
|
|
|
|
def get_site(self):
|
|
if Site._meta.installed:
|
|
return Site.objects.get_current()
|
|
else:
|
|
return RequestSite(self.request)
|
|
|
|
def send_notification(self):
|
|
context = {
|
|
'site': self.get_site(),
|
|
'user': self.user,
|
|
'username': get_username(self.user),
|
|
'token': signing.dumps(self.user.pk, salt=self.salt),
|
|
'secure': self.request.is_secure(),
|
|
}
|
|
|
|
#body = loader.render_to_string(self.email_template_name,
|
|
# context).strip()
|
|
message = get_template(self.email_template_name).render(Context(context))
|
|
subject = loader.render_to_string(self.email_subject_template_name,
|
|
context).strip()
|
|
|
|
msg = EmailMessage(subject, message, settings.DEFAULT_FROM_EMAIL, [self.user.email])
|
|
msg.content_subtype = "html"
|
|
|
|
images =(('/img/logo_reg.png', 'logo'),
|
|
('/img/soc-medias/sm-icon-rss.png', 'rss'),
|
|
('/img/soc-medias/sm-icon-fb.png', 'fb'),
|
|
('/img/soc-medias/sm-icon-lin.png', 'linkedin'),
|
|
('/img/soc-medias/sm-icon-vk.png', 'vk'),
|
|
('/img/soc-medias/sm-icon-twit.png', 'twit'),
|
|
('/img/mail-logo-2.jpg','logo2'))
|
|
for img in images:
|
|
fp = open(settings.STATIC_ROOT+img[0], 'rb')
|
|
msg_img = MIMEImage(fp.read())
|
|
fp.close()
|
|
msg_img.add_header('Content-ID', '<'+img[1]+'>')
|
|
msg.attach(msg_img)
|
|
|
|
|
|
msg.send()
|
|
|
|
#send_mail(subject, body, settings.DEFAULT_FROM_EMAIL,
|
|
# [self.user.email])
|
|
|
|
def form_valid(self, form):
|
|
self.user = form.cleaned_data['user']
|
|
self.send_notification()
|
|
if (
|
|
len(self.search_fields) == 1 and
|
|
self.search_fields[0] == 'username'
|
|
):
|
|
# if we only search by username, don't disclose the user email
|
|
# since it may now be public information.
|
|
email = self.user.username
|
|
else:
|
|
email = self.user.email
|
|
self.mail_signature = signing.dumps(email, salt=self.url_salt)
|
|
return super(Recover, self).form_valid(form)
|
|
recover = Recover.as_view()
|
|
|
|
|
|
class Reset(SaltMixin, generic.FormView):
|
|
form_class = PasswordResetForm
|
|
token_expires = 3600 * 48 # Two days
|
|
template_name = 'password_reset/reset.html'
|
|
success_url = reverse_lazy('password_reset_done')
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
self.args = args
|
|
self.kwargs = kwargs
|
|
|
|
try:
|
|
pk = signing.loads(kwargs['token'], max_age=self.token_expires,
|
|
salt=self.salt)
|
|
except signing.BadSignature:
|
|
return self.invalid()
|
|
|
|
self.user = get_object_or_404(get_user_model(), pk=pk)
|
|
return super(Reset, self).dispatch(request, *args, **kwargs)
|
|
|
|
def invalid(self):
|
|
return self.render_to_response(self.get_context_data(invalid=True))
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super(Reset, self).get_form_kwargs()
|
|
kwargs['user'] = self.user
|
|
return kwargs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super(Reset, self).get_context_data(**kwargs)
|
|
if 'invalid' not in ctx:
|
|
ctx.update({
|
|
'username': get_username(self.user),
|
|
'token': self.kwargs['token'],
|
|
})
|
|
return ctx
|
|
|
|
def form_valid(self, form):
|
|
form.save()
|
|
user_recovers_password.send(
|
|
sender=get_user_model(),
|
|
user=form.user,
|
|
request=self.request
|
|
)
|
|
return redirect(self.get_success_url())
|
|
reset = Reset.as_view()
|
|
|
|
|
|
class ResetDone(generic.TemplateView):
|
|
template_name = 'password_reset/recovery_done.html'
|
|
|
|
|
|
reset_done = ResetDone.as_view()
|
|
|