mirror of https://github.com/jumpserver/jumpserver
Add forget password and reset password
parent
8ff872f41d
commit
3a9cf6c360
|
@ -0,0 +1,60 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>{{ title }}</title>
|
||||
|
||||
{% include '_head_css_js.html' %}
|
||||
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/jumpserver.js" %}"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="gray-bg">
|
||||
<div class="passwordBox2 animated fadeInDown">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="ibox-content">
|
||||
<div>
|
||||
<img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82">
|
||||
<h2 style="display: inline">Jumpserver</h2>
|
||||
</div>
|
||||
{% if errors %}
|
||||
<p>
|
||||
<div class="alert alert-danger">
|
||||
{{ errors }}
|
||||
</div>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if messages %}
|
||||
<p>
|
||||
<div class="alert alert-success">
|
||||
{{ messages }}
|
||||
</div>
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-lg-3">
|
||||
<a href="{{ redirect_url }}" class="btn btn-primary block full-width m-b">返回</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
Copyright Jumpserver.org
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small>© 2014-2016</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>INSPINIA | Forgot password</title>
|
||||
|
||||
{% include '_head_css_js.html' %}
|
||||
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/jumpserver.js" %}"></script>
|
||||
</head>
|
||||
|
||||
<body class="gray-bg">
|
||||
<div class="passwordBox2 animated fadeInDown">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="ibox-content">
|
||||
|
||||
<img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82">
|
||||
<h2 class="font-bold" style="display: inline">忘记密码 ?</h2>
|
||||
|
||||
<h1></h1>
|
||||
{% if errors %}
|
||||
<p class="red-fonts">{{ errors }}</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<form class="m-t" role="form" action="" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<input type="email" name="email" class="form-control" placeholder="Email address" required="">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary block full-width m-b">重置密码</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
Copyright Jumpserver.org
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small>© 2014-2016</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -8,16 +8,11 @@
|
|||
<title> JumpServer </title>
|
||||
<link rel="shortcut icon" href="{% static "img/facio.ico" %}" type="image/x-icon">
|
||||
{% include '_head_css_js.html' %}
|
||||
|
||||
<link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/style.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/animate.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/font-awesome.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/jumpserver.js" %}"></script>
|
||||
</head>
|
||||
|
||||
<body class="gray-bg">
|
||||
|
||||
<div class="loginColumns animated fadeInDown">
|
||||
<div class="row">
|
||||
|
||||
|
@ -58,7 +53,7 @@
|
|||
</div>
|
||||
<button type="submit" class="btn btn-primary block full-width m-b">Login</button>
|
||||
|
||||
<a href="#">
|
||||
<a href="{% url 'users:forget-password' %}">
|
||||
<small>Forgot password?</small>
|
||||
</a>
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title> JumpServer </title>
|
||||
<link rel="shortcut icon" href="{% static "img/facio.ico" %}" type="image/x-icon">
|
||||
{% include '_head_css_js.html' %}
|
||||
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/jumpserver.js" %}"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="gray-bg">
|
||||
|
||||
<div class="loginColumns animated fadeInDown">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6">
|
||||
|
||||
<h2 class="font-bold">欢迎使用Jumpserver开源跳板机</h2>
|
||||
|
||||
<p>
|
||||
Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理
|
||||
</p>
|
||||
|
||||
<p>
|
||||
我们自五湖四海,我们对开源精神无比敬仰和崇拜,我们对完美、整洁、优雅 无止境的追求
|
||||
</p>
|
||||
|
||||
<p>
|
||||
专注自动化运维,努力打造 易用、稳定、安全、自动化 的跳板机, 这是我们的不懈的追求和动力
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<small>永远年轻,永远热泪盈眶 stay foolish stay hungry</small>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="ibox-content">
|
||||
<div><img src="{% static 'img/logo.png' %}" width="82" height="82"> <span class="font-bold text-center" style="font-size: 32px; font-family: inherit">重设密码</span></div>
|
||||
<form class="m-t" role="form" method="post" action="">
|
||||
{% csrf_token %}
|
||||
{% if errors %}
|
||||
<p class="red-fonts">{{ errors }}</p>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" name="password" placeholder="Password" required="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" name="password-confirm" placeholder="Password again" required="">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary block full-width m-b">Setting</button>
|
||||
|
||||
<a href="#">
|
||||
<small>Forgot password?</small>
|
||||
</a>
|
||||
|
||||
<p class="text-muted text-center">
|
||||
{# <small>Do not have an account?</small>#}
|
||||
</p>
|
||||
{# <a class="btn btn-sm btn-white btn-block" href="register.html">Create an account</a>#}
|
||||
</form>
|
||||
<p class="m-t">
|
||||
{# <small>Inspinia we app framework base on Bootstrap 3 © 2014</small>#}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
Copyright Jumpserver.org
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small>© 2014-2016</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>{{ title }}</title>
|
||||
|
||||
{% include '_head_css_js.html' %}
|
||||
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/jumpserver.js" %}"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="gray-bg">
|
||||
<div class="passwordBox2 animated fadeInDown">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="ibox-content">
|
||||
<div>
|
||||
<img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82">
|
||||
<h2 style="display: inline">Jumpserver</h2>
|
||||
</div>
|
||||
<p>
|
||||
<div class="alert alert-success">
|
||||
密码重置成功, 请返回登录页面登录系统 </a>.
|
||||
</div>
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-lg-3">
|
||||
<a href="{% url "users:login" %}" class="btn btn-primary block full-width m-b">返回</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
Copyright Jumpserver.org
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small>© 2014-2016</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -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<pk>[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'),
|
||||
url(r'^users/add$', views.UserAddView.as_view(), name='user-add'),
|
||||
|
|
|
@ -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:
|
||||
</br>
|
||||
您好,请点击下面链接重置密码, 如果不是您申请的, 请关注账号安全
|
||||
</br>
|
||||
<a href="%(rest_password_url)s?token=%(rest_password_token)s">请点击这里设置密码</a>
|
||||
</br>
|
||||
这个链接有效期1小时, 超过时间您可以 <a href="%(forget_password_url)s?email=%(email)s">重新申请</a>
|
||||
|
||||
</br>
|
||||
---
|
||||
|
||||
</br>
|
||||
<a href="%(login_url)s">直接登录</a>
|
||||
|
||||
</br>
|
||||
""" % {
|
||||
'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),
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
|
||||
import threading
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue