add admin|sys user model

pull/530/head
wangyong 2016-09-04 17:50:30 +08:00
commit 0e8e88fac0
40 changed files with 1316 additions and 368 deletions

View File

@ -1,13 +1,14 @@
# coding:utf-8 # coding:utf-8
from __future__ import unicode_literals from __future__ import unicode_literals, absolute_import
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _
class AssetGroup(models.Model): class AssetGroup(models.Model):
name = models.CharField(max_length=64, unique=True) name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=u"创建者") created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.CharField(max_length=128, blank=True, null=True) comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -17,16 +18,16 @@ class AssetGroup(models.Model):
class IDC(models.Model): class IDC(models.Model):
name = models.CharField(max_length=32, verbose_name=u'机房名称') name = models.CharField(max_length=32, verbose_name=_('Name'))
bandwidth = models.CharField(max_length=32, blank=True, verbose_name=u'机房带宽') bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth'))
contact = models.CharField(max_length=16, blank=True, verbose_name=u'联系人') contact = models.CharField(max_length=16, blank=True, verbose_name=_('Contact'))
phone = models.CharField(max_length=32, blank=True, verbose_name=u'联系电话') phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))
address = models.CharField(max_length=128, blank=True, verbose_name=u"机房地址") address = models.CharField(max_length=128, blank=True, verbose_name=_("Address"))
network = models.TextField(blank=True, verbose_name=u"IP地址段") network = models.TextField(blank=True, verbose_name=_('Network'))
date_added = models.DateField(auto_now=True, null=True) date_added = models.DateField(auto_now=True, null=True, verbose_name=_('Date added'))
operator = models.CharField(max_length=32, blank=True, verbose_name=u"运营商") operator = models.CharField(max_length=32, blank=True, verbose_name=_('Operator'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=u"创建者") created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.CharField(max_length=128, blank=True, verbose_name=u"备注") comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -38,9 +39,9 @@ class IDC(models.Model):
class AssetExtend(models.Model): class AssetExtend(models.Model):
key = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'key') key = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'key')
value = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'value') value = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'value')
created_by = models.CharField(max_length=32, blank=True, verbose_name=u"创建者") created_by = models.CharField(max_length=32, blank=True, verbose_name=u"Created by")
date_added = models.DateTimeField(auto_now=True, null=True, blank=True) date_added = models.DateTimeField(auto_now=True, null=True, blank=True)
comment = models.CharField(max_length=128, blank=True, verbose_name=u"备注") comment = models.CharField(max_length=128, blank=True, verbose_name=u"Comment")
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -50,37 +51,37 @@ class AssetExtend(models.Model):
class Asset(models.Model): class Asset(models.Model):
ip = models.CharField(max_length=32, null=True, blank=True, verbose_name="资产IP") ip = models.CharField(max_length=32, blank=True, verbose_name=_('IP'))
other_ip = models.CharField(max_length=255, null=True, blank=True, verbose_name="其他IP") other_ip = models.CharField(max_length=255, blank=True, verbose_name=_('Other IP'))
remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=u'远控卡IP') remote_card_ip = models.CharField(max_length=16, blank=True, verbose_name=_('Remote card IP'))
hostname = models.CharField(max_length=128, unique=True, null=True, blank=True, verbose_name=u"主机名") hostname = models.CharField(max_length=128, unique=True, blank=True, verbose_name=_('Hostname'))
port = models.IntegerField(null=True, blank=True, verbose_name=u"端口") port = models.IntegerField(blank=True, verbose_name=_('Port'))
group = models.ManyToManyField(AssetGroup, null=True, blank=True, verbose_name=u"所属主机组") groups = models.ManyToManyField(AssetGroup, blank=True, verbose_name=_('Asset groups'))
admin_user = models.ForeignKey(AdminUser, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=u'管理用户') username = models.CharField(max_length=16, blank=True, verbose_name=_('Admin user'))
sys_user = models.ManyToManyField(AssetExtend, null=True, blank=True, verbose_name="系统用户") password = models.CharField(max_length=256, blank=True, verbose_name=_("Admin password"))
username = models.CharField(max_length=16, null=True, blank=True, verbose_name=u"管理用户名") admin_user = models.ForeignKey(AdminUser, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin User"))
password = models.CharField(max_length=256, null=True, blank=True, verbose_name=u"密码") sys_user = models.ManyToManyField(SysUser, null=True, blank=True, verbose_name=_("Sys User"))
idc = models.ForeignKey(IDC, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=u'机房') idc = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_('IDC'))
mac_addr = models.CharField(max_length=20, null=True, blank=True, verbose_name=u"MAC地址") mac_addr = models.CharField(max_length=20, blank=True, verbose_name=_("Mac address"))
brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'硬件厂商型号') brand = models.CharField(max_length=64, blank=True, verbose_name=_('Brand'))
cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'CPU') cpu = models.CharField(max_length=64, blank=True, verbose_name=_('CPU'))
memory = models.CharField(max_length=128, null=True, blank=True, verbose_name=u'内存') memory = models.CharField(max_length=128, blank=True, verbose_name=_('Memory'))
disk = models.CharField(max_length=1024, null=True, blank=True, verbose_name=u'硬盘') disk = models.CharField(max_length=1024, blank=True, verbose_name=_('Disk'))
os = models.CharField(max_length=128, null=True, blank=True, verbose_name=u'系统信息') os = models.CharField(max_length=128, blank=True, verbose_name=_('OS'))
cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=u'机柜号') cabinet_no = models.CharField(max_length=32, blank=True, verbose_name=_('Cabinet number'))
cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=u'资产位置') cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position'))
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=u'资产编号') number = models.CharField(max_length=32, blank=True, unique=True, verbose_name=_('Asset number'))
status = models.ManyToManyField(AssetExtend, null=True, blank=True, status = models.ManyToManyField(AssetExtend, blank=True,
related_name="asset_status_extend", verbose_name="资产状态") related_name="asset_status_extend", verbose_name=_('Asset status'))
type = models.ManyToManyField(AssetExtend, null=True, blank=True, type = models.ManyToManyField(AssetExtend, blank=True,
related_name="asset_type_extend", verbose_name="资产类型") related_name="asset_type_extend", verbose_name=_('Asset type'))
env = models.ManyToManyField(AssetExtend, null=True, blank=True, env = models.ManyToManyField(AssetExtend, blank=True,
related_name="asset_env_extend", verbose_name="资产环境") related_name="asset_env_extend", verbose_name=_('Asset environment'))
sn = models.CharField(max_length=128, null=True, blank=True, verbose_name=u"SN编号") sn = models.CharField(max_length=128, blank=True, unique=True, verbose_name=_('Serial number'))
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=u"创建者") created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
is_active = models.BooleanField(default=True, verbose_name=u"是否激活") is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
date_added = models.DateTimeField(auto_now=True, null=True, blank=True) date_added = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
comment = models.CharField(max_length=128, null=True, blank=True, verbose_name=u"备注") comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self): def __unicode__(self):
return self.ip return self.ip
@ -93,9 +94,9 @@ class Label(models.Model):
key = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'key') key = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'key')
value = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'value') value = models.CharField(max_length=64, null=True, blank=True, verbose_name=u'value')
asset = models.ForeignKey(Asset, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=u'label') asset = models.ForeignKey(Asset, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=u'label')
created_by = models.CharField(max_length=32, blank=True, verbose_name=u"创建者") created_by = models.CharField(max_length=32, blank=True, verbose_name=_("Created by"))
date_added = models.DateTimeField(auto_now=True, null=True, blank=True) date_added = models.DateTimeField(auto_now=True, null=True, blank=True)
comment = models.CharField(max_length=128, blank=True, verbose_name=u"备注") comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -145,3 +146,4 @@ class SysUser(models.Model):
class Meta: class Meta:
db_table = 'sysuser' db_table = 'sysuser'

View File

@ -4,6 +4,8 @@ from .views import *
# from .api import ( # from .api import (
# AssetGroupViewSet, AssetViewSet, IDCViewSet # AssetGroupViewSet, AssetViewSet, IDCViewSet
# ) # )
from django.conf.urls import url,include
import views
# from rest_framework import routers # from rest_framework import routers
# router = routers.DefaultRouter() # router = routers.DefaultRouter()
# router.register(r'assetgroup', AssetGroupViewSet) # router.register(r'assetgroup', AssetGroupViewSet)
@ -12,9 +14,18 @@ from .views import *
app_name = 'assets' app_name = 'assets'
urlpatterns = [ urlpatterns = [
url(r'^assets/add/$', AssetAddView.as_view(), name='asset-add'),
url(r'^$', AssetListView.as_view(), name='asset-list'), url(r'^$', AssetListView.as_view(), name='asset-list'),
url(r'^(?P<pk>[0-9]+)/delete/$', AssetDeleteView.as_view(), name='asset-delete'), url(r'^(?P<pk>[0-9]+)/delete/$', AssetDeleteView.as_view(), name='asset-delete'),
url(r'^(?P<pk>[0-9]+)/detail/$', AssetDetailView.as_view(), name='asset-detail'), url(r'^(?P<pk>[0-9]+)/detail/$', AssetDetailView.as_view(), name='asset-detail'),
url(r'^asset', views.AssetListView.as_view(), name='asset-list'),
url(r'^asset/add$', views.AssetAddView.as_view(), name='asset-add'),
url(r'^asset/(?P<pk>[0-9]+)$', views.AssetDetailView.as_view(), name='asset-detail'),
url(r'^asset/(?P<pk>[0-9]+)$/edit', views.AssetEditView.as_view(), name='asset-edit'),
url(r'^asset/(?P<pk>[0-9]+)/delete$', views.AssetDeleteView.as_view(), name='asset-delete'),
url(r'^asset-group', views.AssetGroupListView.as_view(), name='assetgroup-list'),
url(r'^asset-group/add$', views.AssetAddView.as_view(), name='asset-add'),
url(r'^asset-group/(?P<pk>[0-9]+)$', views.AssetDetailView.as_view(), name='asset-detail'),
url(r'^asset-group/(?P<pk>[0-9]+)$/edit', views.AssetEditView.as_view(), name='asset-edit'),
url(r'^asset-group/(?P<pk>[0-9]+)/delete$', views.AssetDeleteView.as_view(), name='asset-delete'),
# url(r'^api/v1.0/', include(router.urls)), # url(r'^api/v1.0/', include(router.urls)),
] ]

View File

@ -1,6 +1,11 @@
from django.views.generic import TemplateView, ListView from django.views.generic import TemplateView, ListView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from __future__ import absolute_import, unicode_literals
from django.views.generic import TemplateView, ListView
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from .models import Asset, AssetGroup, IDC, AssetExtend from .models import Asset, AssetGroup, IDC, AssetExtend
from .forms import AssetForm from .forms import AssetForm
@ -8,7 +13,7 @@ from .forms import AssetForm
from .utils import AdminUserRequiredMixin from .utils import AdminUserRequiredMixin
class AssetAddView(AdminUserRequiredMixin, CreateView): class AssetAddView(CreateView):
model = Asset model = Asset
form_class = AssetForm form_class = AssetForm
template_name = 'assets/asset_add.html' template_name = 'assets/asset_add.html'
@ -19,7 +24,7 @@ class AssetAddView(AdminUserRequiredMixin, CreateView):
return super(AssetAddView, self).form_invalid(form) return super(AssetAddView, self).form_invalid(form)
class AssetEditView(): class AssetEditView(UpdateView):
pass pass
@ -39,3 +44,22 @@ class AssetDetailView(DetailView):
context_object_name = 'asset' context_object_name = 'asset'
template_name = 'assets/asset_detail.html' template_name = 'assets/asset_detail.html'
class AssetGroupAddView(CreateView):
pass
class AssetGroupListView(ListView):
pass
class AssetGroupDetailView(DetailView):
pass
class AssetGroupEditView(UpdateView):
pass
class AssetGroupDeleteView(DeleteView):
pass

View File

@ -33,7 +33,7 @@
{% if messages %} {% if messages %}
<p> <p>
<div class="alert alert-success"> <div class="alert alert-success" id="messages">
{{ messages }} {{ messages }}
</div> </div>
</p> </p>
@ -52,9 +52,26 @@
Copyright Jumpserver.org Copyright Jumpserver.org
</div> </div>
<div class="col-md-6 text-right"> <div class="col-md-6 text-right">
<small>© 2014-2016</small> <small>2014-2016</small>
</div> </div>
</div> </div>
</div> </div>
</body> </body>
<script>
var time=5;
function redirect_page() {
if (time >= 0) {
var messages = '{{ messages }} <b>' + time +'</b> ...';
$('#messages').html(messages);
time--;
setTimeout(redirect_page, 1000);
}
else {
window.location.href = "{{ redirect_url }}";
}
}
{% if auto_redirect %}
window.onload = redirect_page;
{% endif %}
</script>
</html> </html>

View File

@ -1 +1 @@
[{"model": "users.role", "pk": 1, "fields": {"name": "Administrator", "date_added": "2016-08-20T17:03:42.631Z", "created_by": "System", "comment": "\u7ba1\u7406\u5458", "permissions": [16, 17, 18, 19, 20, 21, 10, 11, 12, 13, 14, 15, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 1, 2, 3, 4, 5, 6, 7, 8, 9]}}, {"model": "users.role", "pk": 2, "fields": {"name": "User", "date_added": "2016-08-20T17:03:42.671Z", "created_by": "System", "comment": "\u7528\u6237", "permissions": []}}, {"model": "users.role", "pk": 3, "fields": {"name": "Auditor", "date_added": "2016-08-20T17:03:42.683Z", "created_by": "System", "comment": "\u5ba1\u8ba1\u5458", "permissions": []}}, {"model": "users.usergroup", "pk": 1, "fields": {"name": "ALL", "comment": "Default usergroup for all user", "date_added": "2016-08-20T17:03:42.693Z", "created_by": "System"}}, {"model": "users.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$30000$xZUhPadgI8rs$n2rm5futcOv7Ww4b4BflN8K90Vk3u7ozfnOS7GQq0ns=", "last_login": null, "is_superuser": false, "first_name": "", "last_name": "", "is_staff": false, "is_active": true, "date_joined": "2016-08-20T17:03:42.752Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "avatar": "", "wechat": "", "phone": "", "enable_otp": false, "secret_key_otp": "", "role": 1, "private_key": "", "public_key": "", "comment": "Administrator is the super user of system", "date_expired": "2086-08-03T17:03:42.753Z", "created_by": "System", "user_permissions": [], "groups": [1]}}][{"model": "users.usergroup", "pk": 1, "fields": {"name": "Default", "comment": "Default user group for all user", "date_added": "2016-08-24T08:24:34.436Z", "created_by": "System"}}, {"model": "users.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$30000$MC3vobX7pa0C$l9qIj4UwHqODnj1hMvVy9DjLxbBumZaioQWIFrWQR7c=", "last_login": null, "first_name": "", "last_name": "", "is_active": true, "date_joined": "2016-08-24T08:24:34.438Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "role": "Admin", "avatar": "", "wechat": "", "phone": "", "enable_otp": false, "secret_key_otp": "", "private_key": "", "public_key": "", "comment": "Administrator is the super user of system", "date_expired": "2086-08-07T08:24:34.438Z", "created_by": "System", "user_permissions": [], "groups": [1]}}] [{"model": "users.usergroup", "pk": 1, "fields": {"name": "Default", "comment": "Default user group for all user", "date_added": "2016-09-02T14:32:32Z", "created_by": "System"}}, {"model": "users.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$30000$QU8p6Y6ep8VP$Zhrgn0Issfc8ozrNSdSGmyb3X7lRAbc3EEWdc2RTj/M=", "last_login": null, "first_name": "", "last_name": "", "is_active": true, "date_joined": "2016-09-02T14:32:32Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "role": "Admin", "avatar": "", "wechat": "", "phone": "", "enable_otp": false, "secret_key_otp": "", "private_key": "", "public_key": "", "comment": "Administrator is the super user of system", "is_first_login": false, "date_expired": "2086-08-16T14:32:32Z", "created_by": "System", "user_permissions": [], "groups": [1]}}]

File diff suppressed because one or more lines are too long

View File

@ -61,6 +61,7 @@ INSTALLED_APPS = [
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'bootstrapform', 'bootstrapform',
'captcha',
# 'django.contrib.admin', # 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -79,6 +80,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
] ]
ROOT_URLCONF = 'jumpserver.urls' ROOT_URLCONF = 'jumpserver.urls'
@ -213,7 +215,7 @@ LOGGING = {
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/ # https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh_CN'
TIME_ZONE = 'Asia/Shanghai' TIME_ZONE = 'Asia/Shanghai'
@ -223,6 +225,9 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
# I18N translation
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale'),]
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/ # https://docs.djangoproject.com/en/1.10/howto/static-files/
@ -244,7 +249,6 @@ BOOTSTRAP_COLUMN_COUNT = 11
# Init data or generate fake data source for development # Init data or generate fake data source for development
FIXTURE_DIRS = [os.path.join(BASE_DIR, 'fixtures'), ] FIXTURE_DIRS = [os.path.join(BASE_DIR, 'fixtures'), ]
# Email config # Email config
EMAIL_HOST = CONFIG.EMAIL_HOST EMAIL_HOST = CONFIG.EMAIL_HOST
EMAIL_PORT = CONFIG.EMAIL_PORT EMAIL_PORT = CONFIG.EMAIL_PORT
@ -302,3 +306,9 @@ BROKER_URL = 'redis://%(password)s%(host)s:%(port)s/3' % {
} }
CELERY_RESULT_BACKEND = BROKER_URL CELERY_RESULT_BACKEND = BROKER_URL
# Captcha settings, more see https://django-simple-captcha.readthedocs.io/en/latest/advanced.html
CAPTCHA_IMAGE_SIZE = (75, 33)
CAPTCHA_FOREGROUND_COLOR = '#001100'
#

View File

@ -20,22 +20,14 @@ from django.views.generic.base import TemplateView
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
# def view(request, **kwargs):
# if kwargs:
# print kwargs
# return HttpResponseRedirect('/' + kwargs["module"] + '/' + kwargs["version"] + '/' + kwargs["api"])
urlpatterns = [ urlpatterns = [
url(r'^captcha/', include('captcha.urls')),
url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'), url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'),
url(r'^(api/)?users/', include('users.urls')), url(r'^(api/)?users/', include('users.urls')),
url(r'^assets/', include('assets.urls')), url(r'^assets/', include('assets.urls')),
url(r'^terminal/', include('webterminal.urls')), url(r'^terminal/', include('webterminal.urls')),
] ]
#urlpatterns += [
# url(r'^api/users/', include('users.api_urls')),
#]
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Binary file not shown.

View File

@ -0,0 +1,515 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-09-04 17:31+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: assets/models.py:9 assets/models.py:21
msgid "Name"
msgstr "名称"
#: assets/models.py:10 assets/models.py:29 assets/models.py:71
msgid "Created by"
msgstr "创建者"
#: assets/models.py:11 assets/models.py:30 assets/models.py:74
msgid "Comment"
msgstr "备注"
#: assets/models.py:22
msgid "Bandwidth"
msgstr "带宽"
#: assets/models.py:23
msgid "Contact"
msgstr "联系人"
#: assets/models.py:24
msgid "Phone"
msgstr "手机"
#: assets/models.py:25
msgid "Address"
msgstr "地址"
#: assets/models.py:26
msgid "Network"
msgstr "网络"
#: assets/models.py:27 assets/models.py:73
msgid "Date added"
msgstr "加入日期"
#: assets/models.py:28
msgid "Operator"
msgstr "运营商"
#: assets/models.py:37 assets/models.py:54 templates/_nav.html:23
msgid "IDC"
msgstr "机房"
#: assets/models.py:46
msgid "IP"
msgstr ""
#: assets/models.py:47
msgid "Other IP"
msgstr ""
#: assets/models.py:48
msgid "Remote card IP"
msgstr ""
#: assets/models.py:49
msgid "Hostname"
msgstr "用户名"
#: assets/models.py:50
msgid "Port"
msgstr "端口"
#: assets/models.py:51
msgid "Asset groups"
msgstr "用户组"
#: assets/models.py:52
#, fuzzy
#| msgid "Edit user"
msgid "Admin user"
msgstr "编辑用户"
#: assets/models.py:53
msgid "Admin password"
msgstr "管理员密码"
#: assets/models.py:55
msgid "Mac address"
msgstr "Mac地址"
#: assets/models.py:56
msgid "Brand"
msgstr "品牌"
#: assets/models.py:57
msgid "CPU"
msgstr "CPU"
#: assets/models.py:58
msgid "Memory"
msgstr "内存"
#: assets/models.py:59
msgid "Disk"
msgstr "硬盘"
#: assets/models.py:60
msgid "OS"
msgstr "操作系统"
#: assets/models.py:61
msgid "Cabinet number"
msgstr "机柜编号"
#: assets/models.py:62
msgid "Cabinet position"
msgstr "机柜层号"
#: assets/models.py:63
msgid "Asset number"
msgstr "资产编号"
#: assets/models.py:65
msgid "Asset status"
msgstr "资产状态"
#: assets/models.py:67
msgid "Asset type"
msgstr "系统类型"
#: assets/models.py:69
msgid "Asset environment"
msgstr "资产环境"
#: assets/models.py:70
msgid "Serial number"
msgstr "序列号"
#: assets/models.py:72
msgid "Is active"
msgstr "是否激活"
#: templates/_header_bar.html:8
msgid "Search"
msgstr "搜索"
#: templates/_header_bar.html:14
msgid "Welcome use Jumpserver system"
msgstr "欢迎使用Jumpserver开源跳板机系统"
#: templates/_header_bar.html:18
msgid "Help"
msgstr "帮助"
#: templates/_header_bar.html:35 templates/_nav.html:4
msgid "Home"
msgstr "仪表盘"
#: templates/_nav.html:9
msgid "Users"
msgstr "用户管理"
#: templates/_nav.html:12
msgid "User"
msgstr "用户"
#: templates/_nav.html:13
msgid "Usergroup"
msgstr "用户组"
#: templates/_nav.html:18
msgid "Assets"
msgstr "资产管理"
#: templates/_nav.html:21
msgid "Asset"
msgstr "资产"
#: templates/_nav.html:22
msgid "Assetgroup"
msgstr "用户组"
#: templates/_nav.html:24
#, fuzzy
#| msgid "Asset admin"
msgid "Assetadmin"
msgstr "管理用户"
#: templates/_nav.html:25
#, fuzzy
#| msgid "Asset user"
msgid "Assetuser"
msgstr "系统用户"
#: templates/_nav.html:26
msgid "Label"
msgstr "标签"
#: templates/_nav.html:30
msgid "Perms"
msgstr "权限管理"
#: templates/_nav.html:33
msgid "Perm"
msgstr "权限"
#: templates/_nav.html:36
msgid "Create perm"
msgstr "创建权限"
#: templates/_nav.html:42
msgid "Audits"
msgstr "审计"
#: templates/_nav.html:47
msgid "File"
msgstr "文件"
#: templates/_nav.html:50
msgid "File upload"
msgstr "文件上传"
#: templates/_nav.html:51
msgid "File download"
msgstr "文件下载"
#: templates/_nav.html:56
msgid "Settings"
msgstr "设置"
#: templates/_nav.html:61
msgid "Visit us"
msgstr "访问官网"
#: templates/_user_profile.html:19
msgid "Profile"
msgstr "个人信息"
#: templates/_user_profile.html:21
msgid "Logout"
msgstr "注销登录"
#: templates/captcha/image.html:3
msgid "Play CAPTCHA as audio file"
msgstr ""
#~ msgid "Asset user"
#~ msgstr "系统用户"
#~ msgid "Password"
#~ msgstr "密码"
#~ msgid "Join usergroups"
#~ msgstr "添加到用户组"
#~ msgid "Administrator"
#~ msgstr "管理员"
#~ msgid "Email"
#~ msgstr "邮件"
#~ msgid "Role"
#~ msgstr "角色"
#~ msgid "Avatar"
#~ msgstr "头像"
#~ msgid "Wechat"
#~ msgstr "微信"
#~ msgid "Enable OTP"
#~ msgstr "二次验证"
#~ msgid "ssh private key"
#~ msgstr "ssh密钥"
#~ msgid "ssh public key"
#~ msgstr "ssh公钥"
#~ msgid "Date expired"
#~ msgstr "失效日期"
#~ msgid "Administrator is the super user of system"
#~ msgstr "Administrator是初始的超级管理员"
#~ msgid "System"
#~ msgstr "系统"
#~ msgid "Create user"
#~ msgstr "创建用户"
#~ msgid "Account"
#~ msgstr "账户"
#~ msgid "Security and Role"
#~ msgstr "角色安全"
#~ msgid "Reset"
#~ msgstr "重置"
#~ msgid "Commit"
#~ msgstr "提交"
#~ msgid "Forget password"
#~ msgstr "忘记密码"
#~ msgid "Input your email, that will send a mail to your"
#~ msgstr "输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中"
#~ msgid "Login"
#~ msgstr "登录"
#~ msgid "Captcha invalid"
#~ msgstr "验证码错误"
#~ msgid "Password again"
#~ msgstr "再次输入密码"
#~ msgid "Setting"
#~ msgstr "设置"
#~ msgid "Reset link will be generated and sent to the user. "
#~ msgstr "生成重置密码连接,通过邮件发送给用户"
#~ msgid "Confirm delete"
#~ msgstr "确认删除"
#~ msgid "User detail"
#~ msgstr "用户详情"
#~ msgid "User assets"
#~ msgstr "用户资产"
#~ msgid "User log"
#~ msgstr "登录日志"
#~ msgid "Last login"
#~ msgstr "最后登录"
#~ msgid "Quick modify"
#~ msgstr "快速修改"
#~ msgid "Reset ssh key"
#~ msgstr "重置密钥"
#~ msgid "Select usergroups"
#~ msgstr "选择用户组"
#~ msgid "Add"
#~ msgstr "添加"
#~ msgid "Edit"
#~ msgstr "编辑"
#~ msgid "Delete"
#~ msgstr "删除"
#~ msgid "Delete selected"
#~ msgstr "批量删除"
#~ msgid "Update selected"
#~ msgstr "批量更新"
#~ msgid "Deactive selected"
#~ msgstr "禁用所选"
#~ msgid "Export selected"
#~ msgstr "批量导出"
#~ msgid "Begin to generate ssh private key ..."
#~ msgstr "开始生成ssh密钥"
#~ msgid "Finish to generate ssh private key ..."
#~ msgstr "生成ssh密钥成功"
#~ msgid "These is error when generate ssh key."
#~ msgstr "创建密钥失败"
#~ msgid "Create account successfully"
#~ msgstr "创建账户成功"
#~ msgid ""
#~ "\n"
#~ " Hello %(name)s:\n"
#~ " </br>\n"
#~ " Your account has been created successfully\n"
#~ " </br>\n"
#~ " <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">click "
#~ "here to set your password</a>\n"
#~ " </br>\n"
#~ " This link is valid for 1 hour. After it expires, <a href="
#~ "\"%(forget_password_url)s?email=%(email)s\">request new one</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " ---\n"
#~ "\n"
#~ " </br>\n"
#~ " <a href=\"%(login_url)s\">Login direct</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " "
#~ msgstr ""
#~ "\n"
#~ " 你好 %(name)s:\n"
#~ " </br>\n"
#~ " 恭喜您,您的账号已经创建成功 </br>\n"
#~ " <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">请点"
#~ "击这里设置密码</a> </br>\n"
#~ " 这个链接有效期1小时, 超过时间您可以 <a href=\"%(forget_password_url)s?"
#~ "email=%(email)s\">重新申请</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " ---\n"
#~ "\n"
#~ " </br>\n"
#~ " <a href=\"%(login_url)s\">Login direct</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " "
#~ msgid ""
#~ "\n"
#~ " Hello %(name)s:\n"
#~ " </br>\n"
#~ " Please click the link below to reset your password, if not your "
#~ "request, concern your account security\n"
#~ " </br>\n"
#~ " <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">Click "
#~ "here reset password</a>\n"
#~ " </br>\n"
#~ " This link is valid for 1 hour. After it expires, <a href="
#~ "\"%(forget_password_url)s?email=%(email)s\">request new one<</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " ---\n"
#~ "\n"
#~ " </br>\n"
#~ " <a href=\"%(login_url)s\">Login direct</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " "
#~ msgstr ""
#~ "\n"
#~ " 您好 %(name)s:\n"
#~ " </br>\n"
#~ " 请点击下面链接重置密码, 如果不是您申请的,请关注账号安全\n"
#~ " </br>\n"
#~ " <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">请点击"
#~ "这里设置密码 /a>\n"
#~ " </br>\n"
#~ " 这个链接有效期1小时, 超过时间您可以<a href=\"%(forget_password_url)s?"
#~ "email=%(email)s\">重新申请</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " ---\n"
#~ "\n"
#~ " </br>\n"
#~ " <a href=\"%(login_url)s\">直接登录</a>\n"
#~ "\n"
#~ " </br>\n"
#~ " "
#~ msgid "Username or password invalid"
#~ msgstr "用户名或密码错误"
#~ msgid "Logout success"
#~ msgstr "退出登录成功"
#~ msgid "Logout success, return login page"
#~ msgstr "退出登录成功,返回到登录页面"
#~ msgid "User list"
#~ msgstr "用户列表"
#~ msgid "Create user<a href=\"%s\">%s</a> success."
#~ msgstr "创建用户<a href=\"%s\">%s</a> 成功"
#~ msgid "Usergroup list"
#~ msgstr "用户组列表"
#~ msgid "Create usergroup"
#~ msgstr "创建用户组"
#~ msgid "Email address invalid, input again"
#~ msgstr "邮箱地址错误,重新输入"
#~ msgid "Send reset password message"
#~ msgstr "发送重置密码邮件"
#~ msgid "Send reset password mail success, login your mail box and follow it "
#~ msgstr ""
#~ "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#~ msgid "Reset password success"
#~ msgstr "重置密码成功"
#~ msgid "Reset password success, return to login page"
#~ msgstr "重置密码成功,返回到登录页面"
#~ msgid "Token invalid or expired"
#~ msgstr "Token错误或失效"
#~ msgid "Password not same"
#~ msgstr "密码不一致"

View File

@ -1,20 +1,21 @@
{% load i18n %}
<div class="row border-bottom"> <div class="row border-bottom">
<nav class="navbar navbar-static-top white-bg" role="navigation" style="margin-bottom: 0"> <nav class="navbar navbar-static-top white-bg" role="navigation" style="margin-bottom: 0">
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-minimalize minimalize-styl-2 btn btn-primary " href="#"><i class="fa fa-bars"></i> </a> <a class="navbar-minimalize minimalize-styl-2 btn btn-primary " href="#"><i class="fa fa-bars"></i> </a>
<form role="search" class="navbar-form-custom" method="get" action=""> <form role="search" class="navbar-form-custom" method="get" action="">
<div class="form-group"> <div class="form-group">
<input type="text" placeholder="输入搜索..." class="form-control" name="search" id="top-search"> <input type="text" placeholder="{% trans 'Search' %}..." class="form-control" name="search" id="top-search">
</div> </div>
</form> </form>
</div> </div>
<ul class="nav navbar-top-links navbar-right"> <ul class="nav navbar-top-links navbar-right">
<li> <li>
<span class="m-r-sm text-muted welcome-message">欢迎使用Jumpserver开源跳板机系统</span> <span class="m-r-sm text-muted welcome-message">{% trans 'Welcome use Jumpserver system' %}</span>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a class="dropdown-toggle count-info" data-toggle="dropdown" href="#"> <a class="dropdown-toggle count-info" data-toggle="dropdown" href="#">
<span class="m-r-sm text-muted welcome-message">帮助</span> <span class="m-r-sm text-muted welcome-message">{% trans 'Help' %}</span>
</a> </a>
</li> </li>
<li> <li>
@ -31,7 +32,7 @@
<h2></h2> <h2></h2>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li> <li>
<a href="">仪表盘</a> <a href="">{% trans 'Home' %}</a>
</li> </li>
<li> <li>
{% if app %} {% if app %}

View File

@ -1,62 +1,63 @@
{% load i18n %}
<li id="index"> <li id="index">
<a href=""> <a href="">
<i class="fa fa-dashboard"></i> <span class="nav-label">仪表盘</span><span class="label label-info pull-right"></span> <i class="fa fa-dashboard"></i> <span class="nav-label">{% trans 'Home' %}</span><span class="label label-info pull-right"></span>
</a> </a>
</li> </li>
<li id="users"> <li id="users">
<a href="#"> <a href="#">
<i class="fa fa-group"></i> <span class="nav-label">用户管理</span><span class="fa arrow"></span> <i class="fa fa-group"></i> <span class="nav-label">{% trans 'Users' %}</span><span class="fa arrow"></span>
</a> </a>
<ul class="nav nav-second-level active"> <ul class="nav nav-second-level active">
<li class="users"><a href="{% url 'users:user-list' %}">用户列表</a></li> <li class="users"><a href="{% url 'users:user-list' %}">{% trans 'User' %}</a></li>
<li class="usergroups"><a href="{% url 'users:usergroup-list' %}">用户组列表</a></li> <li class="usergroups"><a href="{% url 'users:usergroup-list' %}">{% trans 'Usergroup' %}</a></li>
</ul> </ul>
</li> </li>
<li id=""> <li id="">
<a> <a>
<i class="fa fa-inbox"></i> <span class="nav-label">资产管理</span><span class="fa arrow"></span> <i class="fa fa-inbox"></i> <span class="nav-label">{% trans 'Assets' %}</span><span class="fa arrow"></span>
</a> </a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li class=""><a href="">资产列表</a></li> <li class=""><a href="">{% trans 'Asset' %}</a></li>
<li class=""><a href="">资产组列表</a></li> <li class=""><a href="">{% trans 'Assetgroup' %}</a></li>
<li class=""><a href="">机房列表</a></li> <li class=""><a href="">{% trans 'IDC' %}</a></li>
<li class=""><a href="">管理用户</a></li> <li class=""><a href="">{% trans 'Assetadmin' %}</a></li>
<li class=""><a href="">系统用户</a></li> <li class=""><a href="">{% trans 'Assetuser' %}</a></li>
<li class=""><a href="">标签列表</a></li> <li class=""><a href="">{% trans 'Label' %}</a></li>
</ul> </ul>
</li> </li>
<li id=""> <li id="">
<a href="#"><i class="fa fa-edit"></i> <span class="nav-label">授权管理</span><span class="fa arrow"></span></a> <a href="#"><i class="fa fa-edit"></i> <span class="nav-label">{% trans 'Perms' %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li class="sudo"> <li class="sudo">
<a class="sudo" href="">授权列表</a> <a class="sudo" href="">{% trans 'Perm' %}</a>
</li> </li>
<li class="role"> <li class="role">
<a href="">添加授权</a> <a href="">{% trans 'Create perm' %}</a>
</li> </li>
</ul> </ul>
</li> </li>
<li id=""> <li id="">
<a href=""> <a href="">
<i class="fa fa-files-o"></i><span class="nav-label">审计管理</span><span class="label label-info pull-right"></span> <i class="fa fa-files-o"></i><span class="nav-label">{% trans 'Audits' %}</span><span class="label label-info pull-right"></span>
</a> </a>
</li> </li>
<li id=""> <li id="">
<a href="#"> <a href="#">
<i class="fa fa-download"></i> <span class="nav-label">上传下载</span><span class="fa arrow"></span> <i class="fa fa-download"></i> <span class="nav-label">{% trans 'File' %}</span><span class="fa arrow"></span>
</a> </a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li class="upload"><a href="">文件上传</a></li> <li class="upload"><a href="">{% trans 'File upload' %}</a></li>
<li class="download"><a href="">文件下载</a></li> <li class="download"><a href="">{% trans 'File download' %}</a></li>
</ul> </ul>
</li> </li>
<li id=""> <li id="">
<a href=""> <a href="">
<i class="fa fa-gears"></i> <span class="nav-label">设置</span><span class="label label-info pull-right"></span> <i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
</a> </a>
</li> </li>
<li class="special_link"> <li class="special_link">
<a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i> <a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i>
<span class="nav-label">访问官网</span> <span class="nav-label">{% trans 'Visit us' %}</span>
</a> </a>
</li> </li>

View File

@ -34,10 +34,10 @@
</div> </div>
{% endif %} {% endif %}
<script> <script>
function sleep(n) { //n表示的毫秒数 {# function sleep(n) { //n表示的毫秒数#}
var start = new Date().getTime(); {# var start = new Date().getTime();#}
while (true) if (new Date().getTime() - start > n) break; {# while (true) if (new Date().getTime() - start > n) break;#}
} {# }#}
$(document).ready(function () { $(document).ready(function () {
$('.page').click(function () { $('.page').click(function () {

View File

@ -1,4 +1,5 @@
{% load static %} {% load static %}
{% load i18n %}
<li class="nav-header"> <li class="nav-header">
<div class="dropdown profile-element"> <div class="dropdown profile-element">
<span> <span>
@ -10,15 +11,14 @@
<strong class="font-bold"> {{ request.user.name }}<span style="color: #8095a8"></span></strong> <strong class="font-bold"> {{ request.user.name }}<span style="color: #8095a8"></span></strong>
</span> </span>
<span class="text-muted text-xs block"> <span class="text-muted text-xs block">
{{ request.user.get_role_display | default:'普通用户' }}<b class="caret"></b> {{ request.user.get_role_display | default:"{% trans 'User' %}" }}<b class="caret"></b>
</span> </span>
</span> </span>
</a> </a>
<ul class="dropdown-menu animated fadeInRight m-t-xs"> <ul class="dropdown-menu animated fadeInRight m-t-xs">
<li><a value="">个人信息</a></li> <li><a value="">{% trans 'Profile' %}</a></li>
<li><a href="">修改信息</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="">注销</a></li> <li><a href="">{% trans 'Logout' %}</a></li>
</ul> </ul>
</div> </div>
<div class="logo-element"> <div class="logo-element">

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<title>Jumpserver | 开源跳板机系统</title> <title>Jumpserver</title>
<link rel="shortcut icon" href={% static "img/facio.ico" %} type="image/x-icon"> <link rel="shortcut icon" href={% static "img/facio.ico" %} type="image/x-icon">
{% include '_head_css_js.html' %} {% include '_head_css_js.html' %}

View File

@ -0,0 +1,12 @@
{{image}}{{hidden_field}}{{text_field}}
<script>
function refresh_captcha() {
$.getJSON("{% url "captcha-refresh" %}",
function (result) {
$('.captcha').attr('src', result['image_url']);
$('#id_captcha_0').val(result['key'])
})
}
$('.captcha').click(refresh_captcha)
</script>

View File

@ -0,0 +1 @@
<input id="{{id}}_0" name="{{name}}_0" type="hidden" value="{{key}}" />

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% spaceless %}
{% if audio %}<a title="{% trans "Play CAPTCHA as audio file" %}" href="{{audio}}">{% endif %}<img src="{{image}}" alt="captcha" class="captcha" />{% if audio %}</a>{% endif %}
{% endspaceless %}

View File

@ -0,0 +1,7 @@
<div class="row">
<div class="col-sm-6">
<input autocomplete="off" id="{{id}}_1" class="form-control" name="{{name}}_1" type="text" />
<span class="red-fonts" id="captcha-error" style="display: none">验证码错误</span>
</div>
</div>
</br>

View File

@ -2,21 +2,16 @@
from django.forms import ModelForm from django.forms import ModelForm
from django import forms from django import forms
from captcha.fields import CaptchaField
from django.utils.translation import gettext_lazy as _
from .models import User, UserGroup from .models import User, UserGroup
# class UserLoginForm(ModelForm):
# class Meta:
# model = User
# fields = [
# "email", "password"
# ]
class UserLoginForm(forms.Form): class UserLoginForm(forms.Form):
username = forms.CharField(label='用户名', max_length=100) username = forms.CharField(label=_('Username'), max_length=100)
password = forms.CharField(label='密码', widget=forms.PasswordInput, max_length=100) password = forms.CharField(label=_('Password'), widget=forms.PasswordInput, max_length=100)
captcha = CaptchaField()
class UserAddForm(ModelForm): class UserAddForm(ModelForm):
@ -33,7 +28,7 @@ class UserAddForm(ModelForm):
} }
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': '请选择用户组'}), 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join usergroups')}),
} }
@ -52,7 +47,7 @@ class UserUpdateForm(ModelForm):
} }
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': '请选择用户组'}), 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join usergroups')}),
} }

Binary file not shown.

View File

@ -0,0 +1,379 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-09-04 17:31+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: users/forms.py:12 users/models.py:106 users/templates/users/login.html:57
#: users/templates/users/user_detail.html:73
#: users/templates/users/user_edit.html:5
#: users/templates/users/user_list.html:13
msgid "Username"
msgstr ""
#: users/forms.py:13 users/templates/users/login.html:60
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_add.html:8 users/templates/users/user_add.html:10
#: users/templates/users/user_edit.html:12
#: users/templates/users/user_edit.html:14
msgid "Password"
msgstr ""
#: users/forms.py:31 users/forms.py:50
msgid "Join usergroups"
msgstr ""
#: users/models.py:60 users/models.py:107
#: users/templates/users/user_detail.html:69
#: users/templates/users/user_list.html:12
msgid "Name"
msgstr ""
#: users/models.py:61 users/models.py:118
#: users/templates/users/user_detail.html:113
msgid "Comment"
msgstr ""
#: users/models.py:102 users/models.py:225
msgid "Administrator"
msgstr ""
#: users/models.py:103
msgid "User"
msgstr ""
#: users/models.py:108 users/templates/users/user_detail.html:77
msgid "Email"
msgstr ""
#: users/models.py:109 users/templates/users/user_detail.html:181
#: users/templates/users/user_list.html:15
msgid "Usergroup"
msgstr ""
#: users/models.py:110 users/templates/users/user_detail.html:93
#: users/templates/users/user_list.html:14
msgid "Role"
msgstr ""
#: users/models.py:111
msgid "Avatar"
msgstr ""
#: users/models.py:112 users/templates/users/user_detail.html:88
msgid "Wechat"
msgstr ""
#: users/models.py:113 users/templates/users/user_detail.html:82
msgid "Phone"
msgstr ""
#: users/models.py:114 users/templates/users/_user.html:56
msgid "Enable OTP"
msgstr ""
#: users/models.py:116
msgid "ssh private key"
msgstr ""
#: users/models.py:117
msgid "ssh public key"
msgstr ""
#: users/models.py:121 users/templates/users/user_detail.html:97
msgid "Date expired"
msgstr ""
#: users/models.py:122 users/templates/users/user_detail.html:101
msgid "Created by"
msgstr ""
#: users/models.py:228
msgid "Administrator is the super user of system"
msgstr ""
#: users/models.py:229
msgid "System"
msgstr ""
#: users/templates/users/_user.html:17 users/templates/users/user_list.html:5
#: users/views.py:113
msgid "Create user"
msgstr ""
#: users/templates/users/_user.html:33
msgid "Account"
msgstr ""
#: users/templates/users/_user.html:43
msgid "Security and Role"
msgstr ""
#: users/templates/users/_user.html:62
msgid "Profile"
msgstr ""
#: users/templates/users/_user.html:69
#: users/templates/users/user_detail.html:162
#: users/templates/users/user_detail.html:170
msgid "Reset"
msgstr ""
#: users/templates/users/_user.html:70
#: users/templates/users/forget_password.html:44
#: users/templates/users/user_list.html:63
msgid "Commit"
msgstr ""
#: users/templates/users/forget_password.html:26
msgid "Forget password"
msgstr ""
#: users/templates/users/forget_password.html:33
msgid "Input your email, that will send a mail to your"
msgstr ""
#: users/templates/users/login.html:45 users/templates/users/login.html:65
msgid "Login"
msgstr ""
#: users/templates/users/login.html:50
msgid "Captcha invalid"
msgstr ""
#: users/templates/users/reset_password.html:45
#: users/templates/users/user_detail.html:159 users/utils.py:98
msgid "Reset password"
msgstr ""
#: users/templates/users/reset_password.html:55
msgid "Password again"
msgstr ""
#: users/templates/users/reset_password.html:57
msgid "Setting"
msgstr ""
#: users/templates/users/user_add.html:12
msgid "Reset link will be generated and sent to the user. "
msgstr ""
#: users/templates/users/user_delete_confirm.html:6
msgid "Confirm delete"
msgstr ""
#: users/templates/users/user_detail.html:18 users/views.py:173
msgid "User detail"
msgstr ""
#: users/templates/users/user_detail.html:20
msgid "User assets"
msgstr ""
#: users/templates/users/user_detail.html:21
msgid "User log"
msgstr ""
#: users/templates/users/user_detail.html:29
msgid "Search"
msgstr ""
#: users/templates/users/user_detail.html:105
msgid "Date joined"
msgstr ""
#: users/templates/users/user_detail.html:109
msgid "Last login"
msgstr ""
#: users/templates/users/user_detail.html:124
msgid "Quick modify"
msgstr ""
#: users/templates/users/user_detail.html:167
msgid "Reset ssh key"
msgstr ""
#: users/templates/users/user_detail.html:189
msgid "Select usergroups"
msgstr ""
#: users/templates/users/user_detail.html:198
msgid "Add"
msgstr ""
#: users/templates/users/user_list.html:16
msgid "Asset num"
msgstr ""
#: users/templates/users/user_list.html:17
msgid "Active"
msgstr ""
#: users/templates/users/user_list.html:44
msgid "Edit"
msgstr ""
#: users/templates/users/user_list.html:45
msgid "Delete"
msgstr ""
#: users/templates/users/user_list.html:55
msgid "Delete selected"
msgstr ""
#: users/templates/users/user_list.html:56
msgid "Update selected"
msgstr ""
#: users/templates/users/user_list.html:57
msgid "Deactive selected"
msgstr ""
#: users/templates/users/user_list.html:58
msgid "Export selected"
msgstr ""
#: users/utils.py:47
msgid "Begin to generate ssh private key ..."
msgstr ""
#: users/utils.py:59
msgid "Finish to generate ssh private key ..."
msgstr ""
#: users/utils.py:63
msgid "These is error when generate ssh key."
msgstr ""
#: users/utils.py:67
msgid "Create account successfully"
msgstr ""
#: users/utils.py:69
#, python-format
msgid ""
"\n"
" Hello %(name)s:\n"
" </br>\n"
" Your account has been created successfully\n"
" </br>\n"
" <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">click "
"here to set your password</a>\n"
" </br>\n"
" This link is valid for 1 hour. After it expires, <a href="
"\"%(forget_password_url)s?email=%(email)s\">request new one</a>\n"
"\n"
" </br>\n"
" ---\n"
"\n"
" </br>\n"
" <a href=\"%(login_url)s\">Login direct</a>\n"
"\n"
" </br>\n"
" "
msgstr ""
#: users/utils.py:100
#, python-format
msgid ""
"\n"
" Hello %(name)s:\n"
" </br>\n"
" Please click the link below to reset your password, if not your request, "
"concern your account security\n"
" </br>\n"
" <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">Click "
"here reset password</a>\n"
" </br>\n"
" This link is valid for 1 hour. After it expires, <a href="
"\"%(forget_password_url)s?email=%(email)s\">request new one<</a>\n"
"\n"
" </br>\n"
" ---\n"
"\n"
" </br>\n"
" <a href=\"%(login_url)s\">Login direct</a>\n"
"\n"
" </br>\n"
" "
msgstr ""
#: users/views.py:53
msgid "Username or password invalid"
msgstr ""
#: users/views.py:70
msgid "Logout success"
msgstr ""
#: users/views.py:71
msgid "Logout success, return login page"
msgstr ""
#: users/views.py:100 users/views.py:113 users/views.py:155 users/views.py:173
#: users/views.py:197 users/views.py:210
msgid "Users"
msgstr ""
#: users/views.py:100
msgid "User list"
msgstr ""
#: users/views.py:109
#, python-format
msgid "Create user<a href=\"%s\">%s</a> success."
msgstr ""
#: users/views.py:155
msgid "Edit user"
msgstr ""
#: users/views.py:197
msgid "Usergroup list"
msgstr ""
#: users/views.py:210
msgid "Create usergroup"
msgstr ""
#: users/views.py:242
msgid "Email address invalid, input again"
msgstr ""
#: users/views.py:253
msgid "Send reset password message"
msgstr ""
#: users/views.py:254
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
#: users/views.py:266
msgid "Reset password success"
msgstr ""
#: users/views.py:267
msgid "Reset password success, return to login page"
msgstr ""
#: users/views.py:283 users/views.py:296
msgid "Token invalid or expired"
msgstr ""
#: users/views.py:292
msgid "Password not same"
msgstr ""

View File

@ -12,6 +12,7 @@ from django.contrib.auth.models import AbstractUser, Permission
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.db import IntegrityError from django.db import IntegrityError
from django.utils.translation import ugettext_lazy as _
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from django.core import signing from django.core import signing
@ -56,8 +57,8 @@ from django.core import signing
class UserGroup(models.Model): class UserGroup(models.Model):
name = models.CharField(max_length=100, unique=True, verbose_name='组名称') name = models.CharField(max_length=100, unique=True, verbose_name=_('Name'))
comment = models.TextField(blank=True, verbose_name='描述') comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_added = models.DateTimeField(auto_now_add=True) date_added = models.DateTimeField(auto_now_add=True)
created_by = models.CharField(max_length=100) created_by = models.CharField(max_length=100)
@ -98,26 +99,27 @@ def date_expired_default():
class User(AbstractUser): class User(AbstractUser):
ROLE_CHOICES = ( ROLE_CHOICES = (
('Admin', '管理员'), ('Admin', _('Administrator')),
('User', '用户'), ('User', _('User')),
) )
username = models.CharField(max_length=20, unique=True, verbose_name='用户名') username = models.CharField(max_length=20, unique=True, verbose_name=_('Username'))
name = models.CharField(max_length=20, blank=True, verbose_name='姓名') name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
email = models.EmailField(max_length=30, unique=True, verbose_name='邮件') email = models.EmailField(max_length=30, unique=True, verbose_name=_('Email'))
groups = models.ManyToManyField(UserGroup, related_name='users', blank=True, verbose_name='用户组') groups = models.ManyToManyField(UserGroup, related_name='users', blank=True, verbose_name=_('Usergroup'))
role = models.CharField(choices=ROLE_CHOICES, default='User', max_length=10, blank=True, verbose_name='角色') role = models.CharField(choices=ROLE_CHOICES, default='User', max_length=10, blank=True, verbose_name=_('Role'))
avatar = models.ImageField(upload_to="avatar", verbose_name='头像') avatar = models.ImageField(upload_to="avatar", verbose_name=_('Avatar'))
wechat = models.CharField(max_length=30, blank=True, verbose_name='微信') wechat = models.CharField(max_length=30, blank=True, verbose_name=_('Wechat'))
phone = models.CharField(max_length=20, blank=True, verbose_name='手机号') phone = models.CharField(max_length=20, blank=True, verbose_name=_('Phone'))
enable_otp = models.BooleanField(default=False, verbose_name='启用二次验证') enable_otp = models.BooleanField(default=False, verbose_name=_('Enable OTP'))
secret_key_otp = models.CharField(max_length=16, blank=True) secret_key_otp = models.CharField(max_length=16, blank=True)
private_key = models.CharField(max_length=5000, blank=True, verbose_name='ssh私钥') # ssh key max length 4096 bit private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('ssh private key'))
public_key = models.CharField(max_length=1000, blank=True, verbose_name='公钥') public_key = models.CharField(max_length=1000, blank=True, verbose_name=_('ssh public key'))
comment = models.TextField(max_length=200, blank=True, verbose_name='描述') comment = models.TextField(max_length=200, blank=True, verbose_name=_('Comment'))
is_first_login = models.BooleanField(default=False) is_first_login = models.BooleanField(default=False)
date_expired = models.DateTimeField(default=date_expired_default, blank=True, null=True, verbose_name='有效期') date_expired = models.DateTimeField(default=date_expired_default, blank=True, null=True,
created_by = models.CharField(max_length=30, default='') verbose_name=_('Date expired'))
created_by = models.CharField(max_length=30, default='', verbose_name=_('Created by'))
@property @property
def password_raw(self): def password_raw(self):
@ -204,7 +206,7 @@ class User(AbstractUser):
user_email = data.get('email', '') user_email = data.get('email', '')
user = cls.objects.get(id=user_id, email=user_email) user = cls.objects.get(id=user_id, email=user_email)
except signing.BadSignature, cls.DoesNotExist: except (signing.BadSignature, cls.DoesNotExist):
user = None user = None
return user return user
@ -220,11 +222,11 @@ class User(AbstractUser):
def initial(cls): def initial(cls):
user = cls(username='admin', user = cls(username='admin',
email='admin@jumpserver.org', email='admin@jumpserver.org',
name='Administrator', name=_('Administrator'),
password_raw='admin', password_raw='admin',
role='Admin', role='Admin',
comment='Administrator is the super user of system', comment=_('Administrator is the super user of system'),
created_by='System') created_by=_('System'))
user.save() user.save()
user.groups.add(UserGroup.initial()) user.groups.add(UserGroup.initial())

View File

@ -1,4 +1,5 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n %}
{% load static %} {% load static %}
{% load bootstrap %} {% load bootstrap %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
@ -13,7 +14,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-title">
<h5>填写用户信息</h5> <h5>{% trans 'Create user' %}</h5>
<div class="ibox-tools"> <div class="ibox-tools">
<a class="collapse-link"> <a class="collapse-link">
<i class="fa fa-chevron-up"></i> <i class="fa fa-chevron-up"></i>
@ -29,7 +30,7 @@
<div class="ibox-content"> <div class="ibox-content">
<form method="post" id="userForm" class="form-horizontal" action="" enctype="multipart/form-data"> <form method="post" id="userForm" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<h3>账户</h3> <h3>{% trans 'Account' %}</h3>
{% block username %} {% endblock %} {% block username %} {% endblock %}
{{ form.email|bootstrap_horizontal }} {{ form.email|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }}
@ -39,7 +40,7 @@
{% block password %} {% endblock %} {% block password %} {% endblock %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>角色安全</h3> <h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }} {{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5"> <div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label> <label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
@ -52,21 +53,21 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">二次验证</label> <label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8"> <div class="col-sm-8">
{{ form.enable_otp }} {{ form.enable_otp }}
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>信息</h3> <h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }} {{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }} {{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }} {{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">取消</button> <button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button> <button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Commit' %}</button>
</div> </div>
</div> </div>
</form> </form>

View File

@ -1,4 +1,5 @@
{% load static %} {% load static %}
{% load i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -6,8 +7,8 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="{% static "img/facio.ico" %}" type="image/x-icon">
<title>INSPINIA | Forgot password</title> <title>忘记密码</title>
{% include '_head_css_js.html' %} {% include '_head_css_js.html' %}
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet"> <link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
@ -22,14 +23,14 @@
<div class="ibox-content"> <div class="ibox-content">
<img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82"> <img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82">
<h2 class="font-bold" style="display: inline">忘记密码 ?</h2> <h2 class="font-bold" style="display: inline">{% trans 'Forget password' %} ?</h2>
<h1></h1> <h1></h1>
{% if errors %} {% if errors %}
<p class="red-fonts">{{ errors }}</p> <p class="red-fonts">{{ errors }}</p>
{% endif %} {% endif %}
<p> <p>
输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中 {% trans 'Input your email, that will send a mail to your' %}
</p> </p>
<div class="row"> <div class="row">
@ -40,7 +41,7 @@
<input type="email" name="email" class="form-control" placeholder="Email address" required=""> <input type="email" name="email" class="form-control" placeholder="Email address" required="">
</div> </div>
<button type="submit" class="btn btn-primary block full-width m-b">重置密码</button> <button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Commit' %}</button>
</form> </form>
</div> </div>

View File

@ -1,4 +1,5 @@
{% load static %} {% load static %}
{% load i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -10,28 +11,30 @@
{% include '_head_css_js.html' %} {% include '_head_css_js.html' %}
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet"> <link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
<script src="{% static "js/jumpserver.js" %}"></script> <script src="{% static "js/jumpserver.js" %}"></script>
<style>
.captcha {
float: right;
}
#id_captcha_1 {
}
</style>
</head> </head>
<body class="gray-bg"> <body class="gray-bg">
<div class="loginColumns animated fadeInDown"> <div class="loginColumns animated fadeInDown">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h2 class="font-bold">欢迎使用Jumpserver开源跳板机</h2> <h2 class="font-bold">欢迎使用Jumpserver开源跳板机</h2>
<p> <p>
Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理 Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理
</p> </p>
<p> <p>
我们自五湖四海,我们对开源精神无比敬仰和崇拜,我们对完美、整洁、优雅 无止境的追求 我们自五湖四海,我们对开源精神无比敬仰和崇拜,我们对完美、整洁、优雅 无止境的追求
</p> </p>
<p> <p>
专注自动化运维,努力打造 易用、稳定、安全、自动化 的跳板机, 这是我们的不懈的追求和动力 专注自动化运维,努力打造 易用、稳定、安全、自动化 的跳板机, 这是我们的不懈的追求和动力
</p> </p>
<p> <p>
<small>永远年轻,永远热泪盈眶 stay foolish stay hungry</small> <small>永远年轻,永远热泪盈眶 stay foolish stay hungry</small>
</p> </p>
@ -39,31 +42,36 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="ibox-content"> <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> <div><img src="{% static 'img/logo.png' %}" width="82" height="82"> <span class="font-bold text-center" style="font-size: 32px; font-family: inherit">{% trans 'Login' %}</span></div>
<form class="m-t" role="form" method="post" action="{% url 'users:login' %}"> <form class="m-t" role="form" method="post" action="{% url 'users:login' %}">
{% csrf_token %} {% csrf_token %}
{% if form.errors %} {% if form.errors %}
<p class="red-fonts">用户名/密码 不正确, 请重试</p> {% if 'captcha' in form.errors %}
<p class="red-fonts">{% trans 'Captcha invalid' %}</p>
{% endif %}
{% endif %}
{% if errors %}
<p class="red-fonts">{{ errors }}</p>
{% endif %} {% endif %}
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="username" placeholder="Username" required=""> <input type="text" class="form-control" name="{{ form.username.html_name }}" placeholder="{% trans 'Username' %}" required="">
</div> </div>
<div class="form-group"> <div class="form-group">
<input type="password" class="form-control" name="password" placeholder="Password" required=""> <input type="password" class="form-control" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
</div> </div>
<button type="submit" class="btn btn-primary block full-width m-b">Login</button> <div>
{{ form.captcha }}
</div>
<button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Login' %}</button>
<a href="{% url 'users:forget-password' %}"> <a href="{% url 'users:forget-password' %}">
<small>Forgot password?</small> <small>Forgot password?</small>
</a> </a>
<p class="text-muted text-center"> <p class="text-muted text-center">
{# <small>Do not have an account?</small>#}
</p> </p>
{# <a class="btn btn-sm btn-white btn-block" href="register.html">Create an account</a>#}
</form> </form>
<p class="m-t"> <p class="m-t">
{# <small>Inspinia we app framework base on Bootstrap 3 &copy; 2014</small>#}
</p> </p>
</div> </div>
</div> </div>
@ -78,7 +86,6 @@
</div> </div>
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,4 +1,5 @@
{% load static %} {% load static %}
{% load i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -41,31 +42,28 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="ibox-content"> <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> <div><img src="{% static 'img/logo.png' %}" width="82" height="82"> <span class="font-bold text-center" style="font-size: 32px; font-family: inherit">{% trans 'Reset password' %}</span></div>
<form class="m-t" role="form" method="post" action=""> <form class="m-t" role="form" method="post" action="">
{% csrf_token %} {% csrf_token %}
{% if errors %} {% if errors %}
<p class="red-fonts">{{ errors }}</p> <p class="red-fonts">{{ errors }}</p>
{% endif %} {% endif %}
<div class="form-group"> <div class="form-group">
<input type="password" class="form-control" name="password" placeholder="Password" required=""> <input type="password" class="form-control" name="password" placeholder="{% trans 'Password' %}" required="">
</div> </div>
<div class="form-group"> <div class="form-group">
<input type="password" class="form-control" name="password-confirm" placeholder="Password again" required=""> <input type="password" class="form-control" name="password-confirm" placeholder="{% trans 'Password again' %}" required="">
</div> </div>
<button type="submit" class="btn btn-primary block full-width m-b">Setting</button> <button type="submit" class="btn btn-primary block full-width m-b">{% trans "Setting" %}</button>
<a href="#"> <a href="#">
<small>Forgot password?</small> <small>Forgot password?</small>
</a> </a>
<p class="text-muted text-center"> <p class="text-muted text-center">
{# <small>Do not have an account?</small>#}
</p> </p>
{# <a class="btn btn-sm btn-white btn-block" href="register.html">Create an account</a>#}
</form> </form>
<p class="m-t"> <p class="m-t">
{# <small>Inspinia we app framework base on Bootstrap 3 &copy; 2014</small>#}
</p> </p>
</div> </div>
</div> </div>

View File

@ -1,14 +1,15 @@
{% extends 'users/_user.html' %} {% extends 'users/_user.html' %}
{% load i18n %}
{% load bootstrap %} {% load bootstrap %}
{% block username %} {% block username %}
{{ form.username|bootstrap_horizontal }} {{ form.username|bootstrap_horizontal }}
{% endblock %} {% endblock %}
{% block password %} {% block password %}
<h3>密码</h3> <h3>{% trans 'Password' %}</h3>
<div class="form-group"> <div class="form-group">
<label class="col-sm-2 control-label">密码</label> <label class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-8 controls" > <div class="col-sm-8 controls" >
生成重置密码连接,通过邮件发送给用户 {% trans 'Reset link will be generated and sent to the user. ' %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,9 @@
{% load i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>确认删除</title> <title>{% trans 'Confirm delete' %}</title>
</head> </head>
<body> <body>
<form action="" method="post"> <form action="" method="post">

View File

@ -2,6 +2,7 @@
{% load common_tags %} {% load common_tags %}
{% load users_tags %} {% load users_tags %}
{% load static %} {% load static %}
{% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
@ -12,13 +13,12 @@
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
{# <div class="ibox-content">#}
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> 用户信息 </a> <li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li> </li>
<li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> 用户资产</a></li> <li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'User assets' %}</a></li>
<li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> 登录记录 </a></li> <li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'User log' %}</a></li>
<div class="" style="float: right"> <div class="" style="float: right">
<form id="search_form" method="get" action="" class="pull-right mail-search"> <form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group"> <div class="input-group">
@ -26,7 +26,7 @@
value="{{ keyword }}" placeholder="Search"> value="{{ keyword }}" placeholder="Search">
<div class="input-group-btn"> <div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary"> <button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索 {% trans 'Search' %}
</button> </button>
</div> </div>
</div> </div>
@ -66,52 +66,52 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td width="20%">姓名:</td> <td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ user.name }}</b></td> <td><b>{{ user.name }}</b></td>
</tr> </tr>
<tr> <tr>
<td>用户名:</td> <td>{% trans 'Username' %}:</td>
<td><b>{{ user.username }}</b></td> <td><b>{{ user.username }}</b></td>
</tr> </tr>
<tr> <tr>
<td>邮件:</td> <td>{% trans 'Email' %}:</td>
<td><b>{{ user.email }}</b></td> <td><b>{{ user.email }}</b></td>
</tr> </tr>
{% if user.phone %} {% if user.phone %}
<tr> <tr>
<td>手机:</td> <td>{% trans 'Phone' %}:</td>
<td><b>{{ user.phone }}</b></td> <td><b>{{ user.phone }}</b></td>
</tr> </tr>
{% endif %} {% endif %}
{% if user.wechat %} {% if user.wechat %}
<tr> <tr>
<td>微信:</td> <td>{% trans 'Wechat' %}:</td>
<td><b>{{ user.wechat }}</b></td> <td><b>{{ user.wechat }}</b></td>
</tr> </tr>
{% endif %} {% endif %}
<tr> <tr>
<td>角色:</td> <td>{% trans 'Role' %}:</td>
<td><b>{{ user.get_role_display }}</b></td> <td><b>{{ user.get_role_display }}</b></td>
</tr> </tr>
<tr> <tr>
<td>有效期:</td> <td>{% trans 'Date expired' %}:</td>
<td><b>{{ user.date_expired|date:"Y-m-j H:i:s" }}</b></td> <td><b>{{ user.date_expired|date:"Y-m-j H:i:s" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>创建者:</td> <td>{% trans 'Created by' %}:</td>
<td><b>{{ user.created_by }}</b></td> <td><b>{{ user.created_by }}</b></td>
</tr> </tr>
<tr> <tr>
<td>创建日期:</td> <td>{% trans 'Date joined' %}:</td>
<td><b>{{ user.date_joined|date:"Y-m-j H:i:s" }}</b></td> <td><b>{{ user.date_joined|date:"Y-m-j H:i:s" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>最后登录:</td> <td>{% trans 'Last login' %}:</td>
<td><b>{{ user.last_login|date:"Y-m-j H:i:s" }}</b></td> <td><b>{{ user.last_login|date:"Y-m-j H:i:s" }}</b></td>
</tr> </tr>
<tr> <tr>
<td class="no-borders">描述:</td> <td>{% trans 'Comment' %}:</td>
<td class="no-borders"><b>{{ user.comment }}</b></td> <td><b>{{ user.comment }}</b></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -121,7 +121,7 @@
<div class="col-sm-5" style="padding-left: 0px;"> <div class="col-sm-5" style="padding-left: 0px;">
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-info-circle"></i> 快速修改 <i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="table"> <table class="table">
@ -156,18 +156,18 @@
</span></td> </span></td>
</tr> </tr>
<tr> <tr>
<td>重置密码:</td> <td>{% trans 'Reset password' %}:</td>
<td> <td>
<span style="float: right"> <span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">重置</button> <button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
</span> </span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>重置密钥:</td> <td>{% trans 'Reset ssh key' %}:</td>
<td> <td>
<span style="float: right"> <span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px;">重置</button> <button type="button" class="btn btn-primary btn-xs" style="width: 54px;">{% trans 'Reset' %}</button>
</span> </span>
</td> </td>
</tr> </tr>
@ -178,7 +178,7 @@
<div class="panel panel-info"> <div class="panel panel-info">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-info-circle"></i> 用户组 <i class="fa fa-info-circle"></i> {% trans 'Usergroup' %}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="table"> <table class="table">
@ -186,7 +186,7 @@
<form> <form>
<tr> <tr>
<td colspan="2" class="no-borders"> <td colspan="2" class="no-borders">
<select data-placeholder="选择用户组" class="select2" style="width: 100%" multiple="" tabindex="4"> <select data-placeholder="{% trans 'Select usergroups' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %} {% for group in groups %}
<option value="{{ group.id }}">{{ group.name }}</option> <option value="{{ group.id }}">{{ group.name }}</option>
{% endfor %} {% endfor %}
@ -195,7 +195,7 @@
</tr> </tr>
<tr> <tr>
<td colspan="2" class="no-borders"> <td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small">添加到用户组</button> <button type="button" class="btn btn-info btn-small">{% trans 'Add' %}</button>
</td> </td>
</tr> </tr>
</form> </form>

View File

@ -1,16 +1,17 @@
{% extends 'users/_user.html' %} {% extends 'users/_user.html' %}
{% load i18n %}
{% block username %} {% block username %}
<div class="form-group"> <div class="form-group">
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">用户名</label> <label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
<div class="col-sm-9 controls" > <div class="col-sm-9 controls" >
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user.username }}" readonly class="form-control"> <input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user.username }}" readonly class="form-control">
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block password %} {% block password %}
<h3>密码</h3> <h3>{% trans 'Password' %}</h3>
<div class="form-group"> <div class="form-group">
<label for="password" class="col-sm-2 control-label">密码</label> <label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-9 controls" > <div class="col-sm-9 controls" >
<input id="password" name="password" type="password" class="form-control"> <input id="password" name="password" type="password" class="form-control">
</div> </div>

View File

@ -1,92 +0,0 @@
{% extends '_list_base.html' %}
{% load common_tags %}
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 查看用户 </h5>
<div class="ibox-tools">
<a class="collapise-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="">
<a href="{% url 'users:user-add' %}" class="btn btn-sm btn-primary "> 添加用户 </a>
<a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="用户名或姓名" value="{{ keyword }}">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
</div>
<table class="table table-striped table-bordered table-hover " id="editable" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=name">姓名</a></th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=username">用户名</a></th>
<th class="text-center">角色</th>
<th class="text-center">用户组</th>
<th class="text-center">资产数量</th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">有效</a></th>
<th class="text-center"></th>
</tr>
</thead>
<tbody>
{% for user in user_list %}
<tr class="gradeX">
<td class="text-center">
<input type="checkbox" name="checked" value="{{ user.id }}">
</td>
<td class="text-center">
<a href="{% url 'users:user-detail' pk=user.id %}">
{{ user.name }}
</a>
</td>
<td class="text-center">{{ user.username }}</td>
<td class="text-center">{{ user.role.name }}</td>
<td class="text-center" title="{% for user_group in user.group.all %} {{ user_group.name }} {% endfor %}"> {{ user.groups.all|join_queryset_attr:"name" }} </td>
<th class="text-center">{{ user.name }}</th>
<td class="text-center">
{% if user.is_expired %}
<i class="fa fa-times text-danger"></i>
{% else %}
<i class="fa fa-check text-navy"></i>
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'users:user-edit' pk=user.id %}" class="btn btn-xs btn-info">编辑</a>
<a href="{% url 'users:user-delete' pk=user.id %}" class="btn btn-xs btn-danger del {% if user.username == 'admin' %} disabled {% endif %}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-sm-6">
</div>
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,20 +1,20 @@
{% extends '_list_base.html' %} {% extends '_list_base.html' %}
{% load i18n %}
{% load common_tags %} {% load common_tags %}
{% block content_left_head %} {% block content_left_head %}
<a href="{% url 'users:user-add' %}" class="btn btn-sm btn-primary "> 添加用户 </a> <a href="{% url 'users:user-add' %}" class="btn btn-sm btn-primary "> {% trans "Create user" %} </a>
{# <a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>#}
{% endblock %} {% endblock %}
{% block table_head %} {% block table_head %}
<th class="text-center"> <th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')"> <input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th> </th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=name">姓名</a></th> <th class="text-center"><a href="{% url 'users:user-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=username">用户名</a></th> <th class="text-center"><a href="{% url 'users:user-list' %}?sort=username">{% trans 'Username' %}</a></th>
<th class="text-center">角色</th> <th class="text-center">{% trans 'Role' %}</th>
<th class="text-center">用户组</th> <th class="text-center">{% trans 'Usergroup' %}</th>
<th class="text-center">资产数量</th> <th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">有效</a></th> <th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">{% trans 'Active' %}</a></th>
<th class="text-center"></th> <th class="text-center"></th>
{% endblock %} {% endblock %}
@ -41,8 +41,8 @@
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">
<a href="{% url 'users:user-edit' pk=user.id %}" class="btn btn-xs btn-info">编辑</a> <a href="{% url 'users:user-edit' pk=user.id %}" class="btn btn-xs btn-info">{% trans 'Edit' %}</a>
<a href="{% url 'users:user-delete' pk=user.id %}" class="btn btn-xs btn-danger del {% if user.id == request.user.id or user.username == 'admin' %} disabled {% endif %}">删除</a> <a href="{% url 'users:user-delete' pk=user.id %}" class="btn btn-xs btn-danger del {% if user.id == request.user.id or user.username == 'admin' %} disabled {% endif %}">{% trans 'Delete' %}</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -52,15 +52,15 @@
<form id="" method="get" action="" class=" mail-search"> <form id="" method="get" action="" class=" mail-search">
<div class="input-group"> <div class="input-group">
<select class="form-control m-b" style="width: auto"> <select class="form-control m-b" style="width: auto">
<option>批量删除</option> <option>{% trans 'Delete selected' %}</option>
<option>批量更新</option> <option>{% trans 'Update selected' %}</option>
<option>批量禁用</option> <option>{% trans 'Deactive selected' %}</option>
<option>批量导出</option> <option>{% trans 'Export selected' %}</option>
</select> </select>
<div class="input-group-btn pull-left" style="padding-left: 5px;"> <div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='search_btn' type="submit" style="height: 32px;" class="btn btn-sm btn-primary"> <button id='search_btn' type="submit" style="height: 32px;" class="btn btn-sm btn-primary">
确认 {% trans 'Commit' %}
</button> </button>
</div> </div>

View File

@ -1,5 +1,6 @@
from django.conf.urls import url from django.conf.urls import url, include
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.urls import reverse_lazy
import views import views
import api import api
@ -7,23 +8,23 @@ import api
app_name = 'users' app_name = 'users'
urlpatterns = [ urlpatterns = [
url(r'^login$', auth_views.login, {'template_name': 'users/login.html'}, name='login'), url(r'^login$', views.UserLoginView.as_view(), name='login'),
url(r'^logout$', auth_views.logout, {'template_name': 'users/login.html'}, name='logout'), url(r'^logout$', views.UserLogoutView.as_view(), name='logout'),
url(r'^password/forget$', views.UserForgetPasswordView.as_view(), name='forget-password'), url(r'^password/forget$', views.UserForgetPasswordView.as_view(), name='forget-password'),
url(r'^password/forget/sendmail-success$', url(r'^password/forget/sendmail-success$',
views.UserForgetPasswordSendmailSuccessView.as_view(), name='forget-password-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$', views.UserResetPasswordView.as_view(), name='reset-password'),
url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(), name='reset-password-success'), 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'^user$', views.UserListView.as_view(), name='user-list'),
url(r'^users/(?P<pk>[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'), url(r'^user/(?P<pk>[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'),
url(r'^users/add$', views.UserAddView.as_view(), name='user-add'), url(r'^user/add$', views.UserAddView.as_view(), name='user-add'),
url(r'^users/(?P<pk>[0-9]+)/edit$', views.UserUpdateView.as_view(), name='user-edit'), url(r'^user/(?P<pk>[0-9]+)/edit$', views.UserUpdateView.as_view(), name='user-edit'),
url(r'^users/(?P<pk>[0-9]+)/delete$', views.UserDeleteView.as_view(), name='user-delete'), url(r'^user/(?P<pk>[0-9]+)/delete$', views.UserDeleteView.as_view(), name='user-delete'),
url(r'^usergroups$', views.UserGroupListView.as_view(), name='usergroup-list'), url(r'^usergroup$', views.UserGroupListView.as_view(), name='usergroup-list'),
url(r'^usergroups/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(), name='usergroup-detail'), url(r'^usergroup/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(), name='usergroup-detail'),
url(r'^usergroups/add/$', views.UserGroupAddView.as_view(), name='usergroup-add'), url(r'^usergroup/add/$', views.UserGroupAddView.as_view(), name='usergroup-add'),
url(r'^usergroups/(?P<pk>[0-9]+)/edit$', views.UserGroupUpdateView.as_view(), name='usergroup-edit'), url(r'^usergroup/(?P<pk>[0-9]+)/edit$', views.UserGroupUpdateView.as_view(), name='usergroup-edit'),
url(r'^usergroups/(?P<pk>[0-9]+)/delete$', views.UserGroupDeleteView.as_view(), name='usergroup-delete'), url(r'^usergroup/(?P<pk>[0-9]+)/delete$', views.UserGroupDeleteView.as_view(), name='usergroup-delete'),
] ]

View File

@ -8,6 +8,7 @@ import logging
from paramiko.rsakey import RSAKey from paramiko.rsakey import RSAKey
from django.contrib.auth.mixins import UserPassesTestMixin from django.contrib.auth.mixins import UserPassesTestMixin
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from common.tasks import send_mail_async from common.tasks import send_mail_async
from common.utils import reverse from common.utils import reverse
@ -43,7 +44,7 @@ def ssh_key_gen(length=2048, password=None, username='root', hostname=None):
f = StringIO.StringIO() f = StringIO.StringIO()
try: try:
logger.debug('Begin to generate ssh private key ...') logger.debug(_('Begin to generate ssh private key ...'))
private_key_obj = RSAKey.generate(length) private_key_obj = RSAKey.generate(length)
private_key_obj.write_private_key(f, password=password) private_key_obj.write_private_key(f, password=password)
private_key = f.getvalue() private_key = f.getvalue()
@ -55,33 +56,33 @@ def ssh_key_gen(length=2048, password=None, username='root', hostname=None):
'hostname': hostname, 'hostname': hostname,
} }
logger.debug('Finish to generate ssh private key ...') logger.debug(_('Finish to generate ssh private key ...'))
return private_key, public_key return private_key, public_key
except IOError: except IOError:
raise IOError('These is error when generate ssh key.') raise IOError(_('These is error when generate ssh key.'))
def user_add_success_next(user): def user_add_success_next(user):
subject = '您的用户创建成功' subject = _('Create account successfully')
recipient_list = [user.email] recipient_list = [user.email]
message = """ message = _("""
您好 %(name)s: Hello %(name)s:
</br> </br>
恭喜您您的账号已经创建成功. Your account has been created successfully
</br> </br>
<a href="%(rest_password_url)s?token=%(rest_password_token)s">请点击这里设置密码</a> <a href="%(rest_password_url)s?token=%(rest_password_token)s">click here to set your password</a>
</br> </br>
这个链接有效期1小时, 超过时间您可以 <a href="%(forget_password_url)s?email=%(email)s">重新申请</a> This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one</a>
</br> </br>
--- ---
</br> </br>
<a href="%(login_url)s">直接登录</a> <a href="%(login_url)s">Login direct</a>
</br> </br>
""" % { """) % {
'name': user.name, 'name': user.name,
'rest_password_url': reverse('users:reset-password', external=True), 'rest_password_url': reverse('users:reset-password', external=True),
'rest_password_token': user.generate_reset_token(), 'rest_password_token': user.generate_reset_token(),
@ -94,25 +95,25 @@ def user_add_success_next(user):
def send_reset_password_mail(user): def send_reset_password_mail(user):
subject = '重设密码' subject = _('Reset password')
recipient_list = [user.email] recipient_list = [user.email]
message = """ message = _("""
您好 %(name)s: Hello %(name)s:
</br> </br>
您好请点击下面链接重置密码, 如果不是您申请的, 请关注账号安全 Please click the link below to reset your password, if not your request, concern your account security
</br> </br>
<a href="%(rest_password_url)s?token=%(rest_password_token)s">请点击这里设置密码</a> <a href="%(rest_password_url)s?token=%(rest_password_token)s">Click here reset password</a>
</br> </br>
这个链接有效期1小时, 超过时间您可以 <a href="%(forget_password_url)s?email=%(email)s">重新申请</a> This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one<</a>
</br> </br>
--- ---
</br> </br>
<a href="%(login_url)s">直接登录</a> <a href="%(login_url)s">Login direct</a>
</br> </br>
""" % { """) % {
'name': user.name, 'name': user.name,
'rest_password_url': reverse('users:reset-password', external=True), 'rest_password_url': reverse('users:reset-password', external=True),
'rest_password_token': user.generate_reset_token(), 'rest_password_token': user.generate_reset_token(),

View File

@ -4,9 +4,9 @@ from __future__ import unicode_literals
import logging import logging
from django.shortcuts import get_object_or_404, reverse, render, Http404 from django.shortcuts import get_object_or_404, reverse, render, Http404, redirect
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.db.models import Q from django.db.models import Q
from django.views.generic.base import View, TemplateView from django.views.generic.base import View, TemplateView
from django.views.generic.list import ListView from django.views.generic.list import ListView
@ -15,6 +15,7 @@ from django.views.generic.detail import DetailView
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.conf import settings from django.conf import settings
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib.auth import views as auth_view, authenticate, login, logout
from common.utils import get_object_or_none from common.utils import get_object_or_none
@ -26,6 +27,55 @@ from .utils import AdminUserRequiredMixin, ssh_key_gen, user_add_success_next, s
logger = logging.getLogger('jumpserver.users.views') logger = logging.getLogger('jumpserver.users.views')
class UserLoginView(FormView):
template_name = 'users/login.html'
form_class = UserLoginForm
redirect_field_name = 'next'
def get(self, request, *args, **kwargs):
if self.request.user.is_staff:
return redirect(request.POST.get(self.redirect_field_name, reverse('index')))
# Todo: Django have bug, lose context issue: https://github.com/django/django/pull/7202
# so we jump it and use origin method render_to_response
# return super(UserLoginView, self).get(request, *args, **kwargs)
return self.render_to_response(self.get_context_data(**kwargs))
def post(self, request, *args, **kwargs):
form = self.get_form()
if not form.is_valid():
return self.form_invalid(form)
username = form['username'].value()
password = form['password'].value()
user = authenticate(username=username, password=password)
if user is None:
kwargs.update({'errors': _('Username or password invalid')})
return self.get(request, *args, **kwargs)
login(request, user)
return redirect(request.GET.get(self.redirect_field_name, reverse('index')))
class UserLogoutView(TemplateView):
template_name = 'common/flash_message_standalone.html'
def get(self, request, *args, **kwargs):
logout(request)
return super(UserLogoutView, self).get(request)
def get_context_data(self, **kwargs):
context = {
'title': _('Logout success'),
'messages': _('Logout success, return login page'),
'redirect_url': reverse('users:login'),
'auto_redirect': True,
}
kwargs.update(context)
return super(UserLogoutView, self).get_context_data(**kwargs)
class UserListView(AdminUserRequiredMixin, ListView): class UserListView(AdminUserRequiredMixin, ListView):
model = User model = User
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
@ -47,7 +97,7 @@ class UserListView(AdminUserRequiredMixin, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserListView, self).get_context_data(**kwargs) context = super(UserListView, self).get_context_data(**kwargs)
context.update({'app': '用户管理', 'action': '用户列表', 'keyword': self.keyword}) context.update({'app': _('Users'), 'action': _('User list'), 'keyword': self.keyword})
return context return context
@ -56,11 +106,11 @@ class UserAddView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
form_class = UserAddForm form_class = UserAddForm
template_name = 'users/user_add.html' template_name = 'users/user_add.html'
success_url = reverse_lazy('users:user-list') success_url = reverse_lazy('users:user-list')
success_message = '添加用户 <a href="%s">%s</a> 成功 .' success_message = _('Create user<a href="%s">%s</a> success.')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserAddView, self).get_context_data(**kwargs) context = super(UserAddView, self).get_context_data(**kwargs)
context.update({'app': '用户管理', 'action': '用户添加'}) context.update({'app': _('Users'), 'action': _('Create user')})
return context return context
def form_valid(self, form): def form_valid(self, form):
@ -102,7 +152,7 @@ class UserUpdateView(AdminUserRequiredMixin, UpdateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserUpdateView, self).get_context_data(**kwargs) context = super(UserUpdateView, self).get_context_data(**kwargs)
context.update({'app': '用户管理', 'action': '用户编辑'}) context.update({'app': _('Users'), 'action': _('Edit user')})
return context return context
@ -120,7 +170,7 @@ class UserDetailView(AdminUserRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserDetailView, self).get_context_data(**kwargs) context = super(UserDetailView, self).get_context_data(**kwargs)
groups = [group for group in UserGroup.objects.iterator() if group not in self.object.groups.iterator()] groups = [group for group in UserGroup.objects.iterator() if group not in self.object.groups.iterator()]
context.update({'app': '用户管理', 'action': '用户详情', 'groups': groups}) context.update({'app': _('Users'), 'action': _('User detail'), 'groups': groups})
return context return context
@ -144,7 +194,7 @@ class UserGroupListView(AdminUserRequiredMixin, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserGroupListView, self).get_context_data(**kwargs) context = super(UserGroupListView, self).get_context_data(**kwargs)
context.update({'app': '用户管理', 'action': '用户组列表', 'keyword': self.keyword}) context.update({'app': _('Users'), 'action': _('Usergroup list'), 'keyword': self.keyword})
return context return context
@ -157,7 +207,7 @@ class UserGroupAddView(AdminUserRequiredMixin, CreateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserGroupAddView, self).get_context_data(**kwargs) context = super(UserGroupAddView, self).get_context_data(**kwargs)
users = User.objects.all() users = User.objects.all()
context.update({'app': '用户管理', 'action': '用户组添加', 'users': users}) context.update({'app': _('Users'), 'action': _('Create usergroup'), 'users': users})
return context return context
def form_valid(self, form): def form_valid(self, form):
@ -189,7 +239,7 @@ class UserForgetPasswordView(TemplateView):
email = request.POST.get('email') email = request.POST.get('email')
user = get_object_or_none(User, email=email) user = get_object_or_none(User, email=email)
if not user: if not user:
return self.get(request, errors='邮件地址错误,请重新输入') return self.get(request, errors=_('Email address invalid, input again'))
else: else:
send_reset_password_mail(user) send_reset_password_mail(user)
return HttpResponseRedirect(reverse('users:forget-password-sendmail-success')) return HttpResponseRedirect(reverse('users:forget-password-sendmail-success'))
@ -200,8 +250,8 @@ class UserForgetPasswordSendmailSuccessView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'title': '发送重置邮件', 'title': _('Send reset password message'),
'messages': '发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)', 'messages': _('Send reset password mail success, login your mail box and follow it '),
'redirect_url': reverse('users:login'), 'redirect_url': reverse('users:login'),
} }
kwargs.update(context) kwargs.update(context)
@ -213,9 +263,10 @@ class UserResetPasswordSuccessView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'title': '重设密码成功', 'title': _('Reset password success'),
'messages': '密码重置成功, 请返回登录页面登录系统', 'messages': _('Reset password success, return to login page'),
'redirect_url': reverse('users:login'), 'redirect_url': reverse('users:login'),
'auto_redirect': True,
} }
kwargs.update(context) kwargs.update(context)
return super(UserResetPasswordSuccessView, self).get_context_data(**kwargs) return super(UserResetPasswordSuccessView, self).get_context_data(**kwargs)
@ -229,7 +280,7 @@ class UserResetPasswordView(TemplateView):
user = User.validate_reset_token(token) user = User.validate_reset_token(token)
if not user: if not user:
kwargs.update({'errors': 'Token不正确或已过期'}) kwargs.update({'errors': _('Token invalid or expired')})
return super(UserResetPasswordView, self).get(request, *args, **kwargs) return super(UserResetPasswordView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@ -238,11 +289,11 @@ class UserResetPasswordView(TemplateView):
token = request.GET.get('token') token = request.GET.get('token')
if password != password_confirm: if password != password_confirm:
return self.get(request, errors='两次密码不匹配') return self.get(request, errors=_('Password not same'))
user = User.validate_reset_token(token) user = User.validate_reset_token(token)
if not user: if not user:
return self.get(request, errors='Token不正确或已过期') return self.get(request, errors=_('Token invalid or expired'))
user.reset_password(password) user.reset_password(password)
return HttpResponseRedirect(reverse('users:reset-password-success')) return HttpResponseRedirect(reverse('users:reset-password-success'))

View File

@ -23,6 +23,7 @@ class Config:
# It's used to identify your site, When we send a create mail to user, we only know login url is /login/ # It's used to identify your site, When we send a create mail to user, we only know login url is /login/
# But we should know the absolute url like: http://jms.jumpserver.org/login/, so SITE_URL is # But we should know the absolute url like: http://jms.jumpserver.org/login/, so SITE_URL is
# HTTP_PROTOCOL://HOST[:PORT] # HTTP_PROTOCOL://HOST[:PORT]
# Todo: May be use :method: get_current_site more grace, bug restful api unknown ok or not
SITE_URL = 'http://localhost' SITE_URL = 'http://localhost'
# Django security setting, if your disable debug model, you should setting that # Django security setting, if your disable debug model, you should setting that
@ -52,7 +53,7 @@ class Config:
# When Django start it will bind this host and port # When Django start it will bind this host and port
# ./manage.py runserver 127.0.0.1:8080 # ./manage.py runserver 127.0.0.1:8080
# Todo: Gunicorn or uwsgi run may be use it # Todo: Gunicorn or uwsgi run may be use it
HTTP_LISTEN_HOST = '127.0.0.1' HTTP_LISTEN_HOST = '0.0.0.0'
HTTP_LISTEN_PORT = 8080 HTTP_LISTEN_PORT = 8080
# Use Redis as broker for celery and web socket # Use Redis as broker for celery and web socket

View File

@ -21,14 +21,15 @@ https://example.org/api/
### 版本 ### 版本
将API的版本号放入URL 将API的版本号放入URL中, 由于一个项目多个app所以Jumpserver使用以下风格,
将版本号放到app后面
``` ```
https://example.com/api/v1/ https://example.com/api/:app:/:version:/:resource:
https://example.com/api/assets/v1.0/assets [GET, POST]
https://example.com/api/assets/v1.0/assets/1 [GET, PUT, DELETE]
``` ```
另一种做法是将版本号放在HTTP头信息中但不如放入URL方便和直观。Github采用这种做法。
### 路径 ### 路径
@ -37,11 +38,11 @@ https://example.com/api/v1/
举例来说 cmdb中的assets列表, idc列表 举例来说 cmdb中的assets列表, idc列表
``` ```
https://example.com/api/v1/assets https://example.com/api/:app:/:version:/:resource:
https://example.com/api/v1/assetgroups
https://example.com/api/v1/assetgroups/:id/assets https://example.com/api/assets/v1.0/assets [GET, POST]
https://example.com/api/v1/assets/:id https://example.com/api/assets/v1.0/assets/1 [GET, PUT, DELETE]
https://example.com/api/v1/idcs https://example.com/api/assets/v1.0/idcs [GET, POST]
``` ```
一般性的增删查改(CRUD)API完全使用HTTP method加上url提供的语义url中的可变部分比如上面提到的<role_id> 一般性的增删查改(CRUD)API完全使用HTTP method加上url提供的语义url中的可变部分比如上面提到的<role_id>

View File

@ -45,5 +45,6 @@
│ │ └── wsgi.py │ │ └── wsgi.py
│ ├── manage.py │ ├── manage.py
│ ├── static // 项目静态资源目录 │ ├── static // 项目静态资源目录
│ ├── static // 项目多语言目录
│ └── templates // 项目模板目录 │ └── templates // 项目模板目录
``` ```

View File

@ -16,3 +16,4 @@ ForgeryPy==0.1
paramiko==2.0.2 paramiko==2.0.2
celery==3.1.23 celery==3.1.23
ansible==2.1.1.0 ansible==2.1.1.0
django-simple-captcha==0.5.2