diff --git a/apps/common/templates/common/flash_message_standalone.html b/apps/common/templates/common/flash_message_standalone.html new file mode 100644 index 000000000..48cf972c4 --- /dev/null +++ b/apps/common/templates/common/flash_message_standalone.html @@ -0,0 +1,60 @@ +{% load static %} + + + + + + + + {{ title }} + + {% include '_head_css_js.html' %} + + + + + + +
+
+
+
+
+ +

Jumpserver

+
+ {% if errors %} +

+

+ {{ errors }} +
+

+ {% endif %} + + {% if messages %} +

+

+ {{ messages }} +
+

+ {% endif %} +
+
+ 返回 +
+
+
+
+
+
+
+
+ Copyright Jumpserver.org +
+
+ © 2014-2016 +
+
+
+ + diff --git a/apps/common/utils.py b/apps/common/utils.py index f975d4bda..fb4613142 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -12,6 +12,13 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None, ex if external: url = settings.SITE_URL.strip('/') + url - return url + +def get_object_or_none(model, **kwargs): + try: + obj = model.objects.get(**kwargs) + except model.DoesNotExist: + obj = None + return obj + diff --git a/apps/common/views.py b/apps/common/views.py index 91ea44a21..870cfbf79 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -1,3 +1,7 @@ -from django.shortcuts import render +from __future__ import absolute_import, unicode_literals + +from django.shortcuts import render +from django.views.generic import TemplateView + + -# Create your views here. diff --git a/apps/static/css/jumpserver.css b/apps/static/css/jumpserver.css index 18ac6eab9..775d1956a 100644 --- a/apps/static/css/jumpserver.css +++ b/apps/static/css/jumpserver.css @@ -58,3 +58,9 @@ th a { border: 1px solid #1ab394 !important; box-shadow: 0 0 5px rgba(0, 0, 0, 0.3) !important; } + +.passwordBox2 { + max-width: 660px; + margin: 0 auto; + padding: 100px 20px 20px 20px; +} diff --git a/apps/users/models.py b/apps/users/models.py index 79f8e8abd..c9c459ae3 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -193,13 +193,8 @@ class User(AbstractUser): Token.objects.filter(user=self).delete() return Token.objects.create(user=self) - @classmethod - def generate_reset_token(cls, email): - try: - user = cls.objects.get(email=email) - return signing.dumps({'reset': user.id, 'email': user.email}) - except cls.DoesNotExist: - return None + def generate_reset_token(self): + return signing.dumps({'reset': self.id, 'email': self.email}) @classmethod def reset_password(cls, token, new_password, max_age=3600): diff --git a/apps/users/templates/users/forget_password.html b/apps/users/templates/users/forget_password.html new file mode 100644 index 000000000..18edbc26a --- /dev/null +++ b/apps/users/templates/users/forget_password.html @@ -0,0 +1,64 @@ +{% load static %} + + + + + + + + + INSPINIA | Forgot password + + {% include '_head_css_js.html' %} + + + + + +
+
+ +
+
+ + +

忘记密码 ?

+ +

+ {% if errors %} +

{{ errors }}

+ {% endif %} +

+ 输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中 +

+ +
+
+
+ {% csrf_token %} +
+ +
+ + + +
+
+
+
+
+
+
+
+
+ Copyright Jumpserver.org +
+
+ © 2014-2016 +
+
+
+ + + + diff --git a/apps/users/templates/users/login.html b/apps/users/templates/users/login.html index 0c13ebe76..8f556d74d 100644 --- a/apps/users/templates/users/login.html +++ b/apps/users/templates/users/login.html @@ -8,16 +8,11 @@ JumpServer {% include '_head_css_js.html' %} - - - - - + -
@@ -58,7 +53,7 @@
- + Forgot password? diff --git a/apps/users/templates/users/reset_password.html b/apps/users/templates/users/reset_password.html new file mode 100644 index 000000000..b8794ade4 --- /dev/null +++ b/apps/users/templates/users/reset_password.html @@ -0,0 +1,87 @@ +{% load static %} + + + + + + + JumpServer + + {% include '_head_css_js.html' %} + + + + + + + +
+
+ +
+ +

欢迎使用Jumpserver开源跳板机

+ +

+ Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理 +

+ +

+ 我们自五湖四海,我们对开源精神无比敬仰和崇拜,我们对完美、整洁、优雅 无止境的追求 +

+ +

+ 专注自动化运维,努力打造 易用、稳定、安全、自动化 的跳板机, 这是我们的不懈的追求和动力 +

+ +

+ 永远年轻,永远热泪盈眶 stay foolish stay hungry +

+ +
+
+
+
重设密码
+
+ {% csrf_token %} + {% if errors %} +

{{ errors }}

+ {% endif %} +
+ +
+
+ +
+ + + + Forgot password? + + +

+{# Do not have an account?#} +

+{# Create an account#} +
+

+{# Inspinia we app framework base on Bootstrap 3 © 2014#} +

+
+
+
+
+
+
+ Copyright Jumpserver.org +
+
+ © 2014-2016 +
+
+
+ + + + + diff --git a/apps/users/templates/users/reset_password_success.html.bak b/apps/users/templates/users/reset_password_success.html.bak new file mode 100644 index 000000000..bf7961710 --- /dev/null +++ b/apps/users/templates/users/reset_password_success.html.bak @@ -0,0 +1,53 @@ +{% load static %} + + + + + + + + + {{ title }} + + {% include '_head_css_js.html' %} + + + + + + +
+
+
+
+
+ +

Jumpserver

+
+

+

+ 密码重置成功, 请返回登录页面登录系统 . +
+

+
+
+ 返回 +
+
+
+
+
+
+
+
+ Copyright Jumpserver.org +
+
+ © 2014-2016 +
+
+
+ + + + diff --git a/apps/users/urls.py b/apps/users/urls.py index 776ae1e89..d12a27528 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -10,7 +10,10 @@ urlpatterns = [ url(r'^login$', auth_views.login, {'template_name': 'users/login.html'}, name='login'), url(r'^logout$', auth_views.logout, {'template_name': 'users/login.html'}, name='logout'), url(r'^password/forget$', views.UserForgetPasswordView.as_view(), name='forget-password'), - url(r'^password/reset$', views.UserRestPasswordView.as_view(), name='reset-password'), + url(r'^password/forget/sendmail-success$', + views.UserForgetPasswordSendmailSuccessView.as_view(), name='forget-password-sendmail-success'), + url(r'^password/reset$', views.UserResetPasswordView.as_view(), name='reset-password'), + url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(), name='reset-password-success'), url(r'^users$', views.UserListView.as_view(), name='user-list'), url(r'^users/(?P[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'), url(r'^users/add$', views.UserAddView.as_view(), name='user-add'), diff --git a/apps/users/utils.py b/apps/users/utils.py index 99afd2c86..45a464703 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -84,7 +84,38 @@ def user_add_success_next(user): """ % { 'name': user.name, 'rest_password_url': reverse('users:reset-password', external=True), - 'rest_password_token': User.generate_reset_token(user.email), + 'rest_password_token': user.generate_reset_token(), + 'forget_password_url': reverse('users:forget-password', external=True), + 'email': user.email, + 'login_url': reverse('users:login', external=True), + } + + send_mail_async.delay(subject, message, recipient_list, html_message=message) + + +def send_reset_password_mail(user): + subject = '重设密码' + recipient_list = [user.email] + message = """ + 您好 %(name)s: +
+ 您好,请点击下面链接重置密码, 如果不是您申请的, 请关注账号安全 +
+ 请点击这里设置密码 +
+ 这个链接有效期1小时, 超过时间您可以 重新申请 + +
+ --- + +
+ 直接登录 + +
+ """ % { + 'name': user.name, + 'rest_password_url': reverse('users:reset-password', external=True), + 'rest_password_token': user.generate_reset_token(), 'forget_password_url': reverse('users:forget-password', external=True), 'email': user.email, 'login_url': reverse('users:login', external=True), diff --git a/apps/users/views.py b/apps/users/views.py index 833563eec..f79b9509d 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -4,20 +4,23 @@ from __future__ import unicode_literals import logging -from django.shortcuts import get_object_or_404, reverse, render +from django.shortcuts import get_object_or_404, reverse, render, Http404 from django.http import HttpResponseRedirect from django.urls import reverse_lazy from django.db.models import Q -from django.views.generic.base import View +from django.views.generic.base import View, TemplateView from django.views.generic.list import ListView from django.views.generic.edit import CreateView, DeleteView, UpdateView, ProcessFormView, FormView from django.views.generic.detail import DetailView from django.contrib.messages.views import SuccessMessageMixin from django.conf import settings +from django.http import HttpResponseRedirect + +from common.utils import get_object_or_none from .models import User, UserGroup from .forms import UserAddForm, UserUpdateForm, UserGroupForm, UserLoginForm -from .utils import AdminUserRequiredMixin, ssh_key_gen, user_add_success_next +from .utils import AdminUserRequiredMixin, ssh_key_gen, user_add_success_next, send_reset_password_mail logger = logging.getLogger('jumpserver.users.views') @@ -179,9 +182,58 @@ class UserGroupDeleteView(DeleteView): pass -class UserForgetPasswordView(View): - pass +class UserForgetPasswordView(TemplateView): + template_name = 'users/forget_password.html' + + def post(self, request, *args, **kwargs): + email = request.POST.get('email') + print(email) + user = get_object_or_none(User, email=email) + if not user: + return self.get(request, errors='邮件地址错误,请重新输入') + else: + send_reset_password_mail(user) + return HttpResponseRedirect(reverse('users:forget-password-sendmail-success')) -class UserRestPasswordView(View): - pass +class UserForgetPasswordSendmailSuccessView(TemplateView): + template_name = 'common/flash_message_standalone.html' + + def get_context_data(self, **kwargs): + context = { + 'title': '发送重置邮件', + 'messages': '发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)', + 'redirect_url': reverse('users:login'), + } + kwargs.update(context) + return super(UserForgetPasswordSendmailSuccessView, self).get_context_data(**kwargs) + + +class UserResetPasswordSuccessView(TemplateView): + template_name = 'common/flash_message_standalone.html' + + def get_context_data(self, **kwargs): + context = { + 'title': '重设密码成功', + 'messages': '密码重置成功, 请返回登录页面登录系统', + 'redirect_url': reverse('users:login'), + } + kwargs.update(context) + return super(UserResetPasswordSuccessView, self).get_context_data(**kwargs) + + +class UserResetPasswordView(TemplateView): + template_name = 'users/reset_password.html' + + def post(self, request, *args, **kwargs): + password = request.POST.get('password') + password_confirm = request.POST.get('password-confirm') + token = request.GET.get('token') + + if password != password_confirm: + return self.get(request, errors='两次密码不匹配') + + if not User.reset_password(token, password): + return self.get(request, errors='Token不正确或已过期') + + return HttpResponseRedirect(reverse('users:reset-password-success')) diff --git a/run_server.py b/run_server.py new file mode 100644 index 000000000..6c69fea05 --- /dev/null +++ b/run_server.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# + +import threading + + + +