@@ -45,11 +70,20 @@
{% endblock %}
{% block custom_foot_js %}
-
+
{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/asset_detail.html b/apps/assets/templates/assets/asset_detail.html
index fc2320420..80916340f 100644
--- a/apps/assets/templates/assets/asset_detail.html
+++ b/apps/assets/templates/assets/asset_detail.html
@@ -71,27 +71,7 @@
#}
{# {% trans 'Change auth period' %}: | #}
{# #}
@@ -144,33 +161,33 @@
- {% trans 'Clusters' %}
+ {% trans 'Nodes' %}
-
+
- {% for cluster in system_user.cluster.all %}
+ {% for node in system_user.nodes.all %}
- {{ cluster.name }} |
+ {{ node.name }} |
-
+
|
{% endfor %}
@@ -187,27 +204,27 @@
{% endblock %}
{% block custom_foot_js %}
{% endblock %}
diff --git a/apps/assets/templates/assets/system_user_list.html b/apps/assets/templates/assets/system_user_list.html
index 5ad20a57f..b1dd401bc 100644
--- a/apps/assets/templates/assets/system_user_list.html
+++ b/apps/assets/templates/assets/system_user_list.html
@@ -4,7 +4,7 @@
{% block help_message %}
系统用户是 用户登录资产(服务器)时使用的用户,如 web, sa, dba等具有特殊功能的用户。系统用户创建时,如果选择了自动推送
- Jumpserver会使用ansible自动推送到系统用户所在集群的资产中,如果资产(交换机)不支持ansible, 请手动填写账号密码。
+ Jumpserver会使用ansible自动推送系统用户到资产中,如果资产(交换机、windows)不支持ansible, 请手动填写账号密码。
{% endblock %}
@@ -75,7 +75,8 @@ function initTable() {
if (val === 100) {
innerHtml = "" + val + "% ";
} else {
- innerHtml = "" + val + "% ";
+ var num = new Number(val);
+ innerHtml = "" + num.toFixed(1) + "% ";
}
$(td).html('' + innerHtml + '');
diff --git a/apps/assets/templates/assets/system_user_update.html b/apps/assets/templates/assets/system_user_update.html
index 0d2029ee8..46ef8d6a3 100644
--- a/apps/assets/templates/assets/system_user_update.html
+++ b/apps/assets/templates/assets/system_user_update.html
@@ -5,8 +5,8 @@
{% block auth %}
{% trans 'Auth' %}
- {% bootstrap_field form.private_key_file layout="horizontal" %}
{% bootstrap_field form.password layout="horizontal" %}
+ {% bootstrap_field form.private_key_file layout="horizontal" %}
diff --git a/apps/assets/templates/assets/user_asset_list.html b/apps/assets/templates/assets/user_asset_list.html
index 4a91d4f65..5c9fa2633 100644
--- a/apps/assets/templates/assets/user_asset_list.html
+++ b/apps/assets/templates/assets/user_asset_list.html
@@ -19,8 +19,6 @@
{% trans 'Hostname' %} |
{% trans 'IP' %} |
{% trans 'Port' %} |
- {% trans 'Type' %} |
- {% trans 'Env' %} |
{% trans 'Hardware' %} |
{% trans 'Active' %} |
{% trans 'Connective' %} |
@@ -44,14 +42,14 @@ function initTable() {
var detail_btn = ' ' + cellData + '';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
- {targets: 7, createdCell: function (td, cellData) {
+ {targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html(' ')
} else {
$(td).html(' ')
}
}},
- {targets: 8, createdCell: function (td, cellData) {
+ {targets: 6, createdCell: function (td, cellData) {
if (cellData == 'Unknown'){
$(td).html(' ')
} else if (!cellData) {
@@ -68,8 +66,7 @@ function initTable() {
ajax_url: '{% url "api-assets:user-asset-list" %}',
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
- {data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"},
- {data: "is_active" }, {data: "is_connective"}
+ {data: "hardware_info"}, {data: "is_active" }, {data: "is_connective"}
],
op_html: $('#actions').html()
};
diff --git a/apps/assets/templatetags/__init__.py b/apps/assets/templatetags/__init__.py
new file mode 100644
index 000000000..ec51c5a2b
--- /dev/null
+++ b/apps/assets/templatetags/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+#
diff --git a/apps/assets/templatetags/asset_tags.py b/apps/assets/templatetags/asset_tags.py
index 829ab2a80..15605f835 100644
--- a/apps/assets/templatetags/asset_tags.py
+++ b/apps/assets/templatetags/asset_tags.py
@@ -1,6 +1,12 @@
+from collections import defaultdict
from django import template
-from django.utils import timezone
-from django.conf import settings
register = template.Library()
+
+@register.filter
+def group_labels(queryset):
+ grouped = defaultdict(list)
+ for label in queryset:
+ grouped[label.name].append(label)
+ return [(name, labels) for name, labels in grouped.items()]
diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py
index 503464b31..38494539c 100644
--- a/apps/assets/urls/api_urls.py
+++ b/apps/assets/urls/api_urls.py
@@ -7,11 +7,13 @@ app_name = 'assets'
router = BulkRouter()
-router.register(r'v1/groups', api.AssetGroupViewSet, 'asset-group')
+# router.register(r'v1/groups', api.AssetGroupViewSet, 'asset-group')
router.register(r'v1/assets', api.AssetViewSet, 'asset')
-router.register(r'v1/clusters', api.ClusterViewSet, 'cluster')
+# router.register(r'v1/clusters', api.ClusterViewSet, 'cluster')
router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user')
router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
+router.register(r'v1/labels', api.LabelViewSet, 'label')
+router.register(r'v1/nodes', api.NodeViewSet, 'node')
urlpatterns = [
url(r'^v1/assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
@@ -24,23 +26,27 @@ urlpatterns = [
url(r'^v1/assets/user-assets/$',
api.UserAssetListView.as_view(), name='user-asset-list'),
# update the asset group, which add or delete the asset to the group
- url(r'^v1/groups/(?P [0-9a-zA-Z\-]{36})/assets/$',
- api.GroupUpdateAssetsApi.as_view(), name='group-update-assets'),
- url(r'^v1/groups/(?P[0-9a-zA-Z\-]{36})/assets/add/$',
- api.GroupAddAssetsApi.as_view(), name='group-add-assets'),
+ #url(r'^v1/groups/(?P[0-9a-zA-Z\-]{36})/assets/$',
+ # api.GroupUpdateAssetsApi.as_view(), name='group-update-assets'),
+ #url(r'^v1/groups/(?P[0-9a-zA-Z\-]{36})/assets/add/$',
+ # api.GroupAddAssetsApi.as_view(), name='group-add-assets'),
# update the Cluster, and add or delete the assets to the Cluster
- url(r'^v1/cluster/(?P[0-9a-zA-Z\-]{36})/assets/$',
- api.ClusterAddAssetsApi.as_view(), name='cluster-add-assets'),
- url(r'^v1/cluster/(?P[0-9a-zA-Z\-]{36})/assets/connective/$',
- api.ClusterTestAssetsAliveApi.as_view(), name='cluster-test-connective'),
- url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/clusters/$',
- api.AdminUserAddClustersApi.as_view(), name='admin-user-add-clusters'),
+ #url(r'^v1/cluster/(?P[0-9a-zA-Z\-]{36})/assets/$',
+ # api.ClusterAddAssetsApi.as_view(), name='cluster-add-assets'),
+ #url(r'^v1/cluster/(?P[0-9a-zA-Z\-]{36})/assets/connective/$',
+ # api.ClusterTestAssetsAliveApi.as_view(), name='cluster-test-connective'),
+ url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/nodes/$',
+ api.ReplaceNodesAdminUserApi.as_view(), name='replace-nodes-admin-user'),
url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/connective/$',
api.AdminUserTestConnectiveApi.as_view(), name='admin-user-connective'),
url(r'^v1/system-user/(?P[0-9a-zA-Z\-]{36})/push/$',
api.SystemUserPushApi.as_view(), name='system-user-push'),
url(r'^v1/system-user/(?P[0-9a-zA-Z\-]{36})/connective/$',
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
+ url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/$', api.NodeChildrenApi.as_view(), name='node-children'),
+ url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'),
+ url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'),
+ url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
]
urlpatterns += router.urls
diff --git a/apps/assets/urls/views_urls.py b/apps/assets/urls/views_urls.py
index e24721c09..545e7b062 100644
--- a/apps/assets/urls/views_urls.py
+++ b/apps/assets/urls/views_urls.py
@@ -14,27 +14,11 @@ urlpatterns = [
url(r'^asset/(?P[0-9a-zA-Z\-]{36})/$', views.AssetDetailView.as_view(), name='asset-detail'),
url(r'^asset/(?P[0-9a-zA-Z\-]{36})/update/$', views.AssetUpdateView.as_view(), name='asset-update'),
url(r'^asset/(?P[0-9a-zA-Z\-]{36})/delete/$', views.AssetDeleteView.as_view(), name='asset-delete'),
- url(r'^asset-modal$', views.AssetModalListView.as_view(), name='asset-modal-list'),
url(r'^asset/update/$', views.AssetBulkUpdateView.as_view(), name='asset-bulk-update'),
# User asset view
url(r'^user-asset/$', views.UserAssetListView.as_view(), name='user-asset-list'),
- # Resource asset group url
- url(r'^asset-group/$', views.AssetGroupListView.as_view(), name='asset-group-list'),
- url(r'^asset-group/create/$', views.AssetGroupCreateView.as_view(), name='asset-group-create'),
- url(r'^asset-group/(?P[0-9a-zA-Z\-]{36})/$', views.AssetGroupDetailView.as_view(), name='asset-group-detail'),
- url(r'^asset-group/(?P[0-9a-zA-Z\-]{36})/update/$', views.AssetGroupUpdateView.as_view(), name='asset-group-update'),
- url(r'^asset-group/(?P[0-9a-zA-Z\-]{36})/delete/$', views.AssetGroupDeleteView.as_view(), name='asset-group-delete'),
-
- # Resource cluster url
- url(r'^cluster/$', views.ClusterListView.as_view(), name='cluster-list'),
- url(r'^cluster/create/$', views.ClusterCreateView.as_view(), name='cluster-create'),
- url(r'^cluster/(?P[0-9a-zA-Z\-]{36})/$', views.ClusterDetailView.as_view(), name='cluster-detail'),
- url(r'^cluster/(?P[0-9a-zA-Z\-]{36})/update/', views.ClusterUpdateView.as_view(), name='cluster-update'),
- url(r'^cluster/(?P[0-9a-zA-Z\-]{36})/delete/$', views.ClusterDeleteView.as_view(), name='cluster-delete'),
- url(r'^cluster/(?P[0-9a-zA-Z\-]{36})/assets/$', views.ClusterAssetsView.as_view(), name='cluster-assets'),
-
# Resource admin user url
url(r'^admin-user/$', views.AdminUserListView.as_view(), name='admin-user-list'),
url(r'^admin-user/create/$', views.AdminUserCreateView.as_view(), name='admin-user-create'),
@@ -50,8 +34,10 @@ urlpatterns = [
url(r'^system-user/(?P[0-9a-zA-Z\-]{36})/update/$', views.SystemUserUpdateView.as_view(), name='system-user-update'),
url(r'^system-user/(?P[0-9a-zA-Z\-]{36})/delete/$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
url(r'^system-user/(?P[0-9a-zA-Z\-]{36})/asset/$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
- # url(r'^system-user/(?P[0-9a-zA-Z\-]{36})/asset-group$', views.SystemUserAssetGroupView.as_view(),
- # name='system-user-asset-group'),
+ url(r'^label/$', views.LabelListView.as_view(), name='label-list'),
+ url(r'^label/create/$', views.LabelCreateView.as_view(), name='label-create'),
+ url(r'^label/(?P[0-9a-zA-Z\-]{36})/update/$', views.LabelUpdateView.as_view(), name='label-update'),
+ url(r'^label/(?P[0-9a-zA-Z\-]{36})/delete/$', views.LabelDeleteView.as_view(), name='label-delete'),
]
diff --git a/apps/assets/utils.py b/apps/assets/utils.py
index ab63bbafc..e3899bd8a 100644
--- a/apps/assets/utils.py
+++ b/apps/assets/utils.py
@@ -1,8 +1,13 @@
# ~*~ coding: utf-8 ~*~
#
from collections import defaultdict
+from functools import reduce
+import operator
+
+from django.db.models import Q
+
from common.utils import get_object_or_none
-from .models import Asset, SystemUser
+from .models import Asset, SystemUser, Label
def get_assets_by_id_list(id_list):
@@ -18,12 +23,26 @@ def get_system_user_by_name(name):
return system_user
-def check_assets_have_system_user(assets, system_users):
- errors = defaultdict(list)
+class LabelFilter:
+ def filter_queryset(self, queryset):
+ queryset = super().filter_queryset(queryset)
+ query_keys = self.request.query_params.keys()
+ all_label_keys = Label.objects.values_list('name', flat=True)
+ valid_keys = set(all_label_keys) & set(query_keys)
+ labels_query = {}
+ for key in valid_keys:
+ labels_query[key] = self.request.query_params.get(key)
+
+ conditions = []
+ for k, v in labels_query.items():
+ query = {'labels__name': k, 'labels__value': v}
+ conditions.append(query)
+
+ if conditions:
+ for kwargs in conditions:
+ queryset = queryset.filter(**kwargs)
+ return queryset
+
+
+
- for system_user in system_users:
- clusters = system_user.cluster.all()
- for asset in assets:
- if asset.cluster not in clusters:
- errors[asset].append(system_user)
- return errors
diff --git a/apps/assets/views/__init__.py b/apps/assets/views/__init__.py
index 883120b36..097dec3ae 100644
--- a/apps/assets/views/__init__.py
+++ b/apps/assets/views/__init__.py
@@ -1,7 +1,5 @@
# coding:utf-8
from .asset import *
-from .group import *
-from .cluster import *
from .system_user import *
from .admin_user import *
-
+from .label import *
diff --git a/apps/assets/views/admin_user.py b/apps/assets/views/admin_user.py
index ce7b8bfb4..7d7878e88 100644
--- a/apps/assets/views/admin_user.py
+++ b/apps/assets/views/admin_user.py
@@ -10,7 +10,7 @@ from django.views.generic.detail import DetailView, SingleObjectMixin
from common.const import create_success_msg, update_success_msg
from .. import forms
-from ..models import AdminUser, Cluster
+from ..models import AdminUser, Node
from ..hands import AdminUserRequiredMixin
__all__ = [
@@ -74,11 +74,10 @@ class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
object = None
def get_context_data(self, **kwargs):
- cluster_remain = Cluster.objects.exclude(admin_user=self.object)
context = {
'app': _('Assets'),
'action': _('Admin user detail'),
- 'cluster_remain': cluster_remain,
+ 'nodes': Node.objects.all()
}
kwargs.update(context)
return super().get_context_data(**kwargs)
@@ -95,11 +94,8 @@ class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
return super().get(request, *args, **kwargs)
def get_queryset(self):
- queryset = []
- for cluster in self.object.cluster_set.all():
- queryset.extend([asset for asset in cluster.assets.all() if not asset.admin_user])
- self.queryset = queryset
- return queryset
+ self.queryset = self.object.asset_set.all()
+ return self.queryset
def get_context_data(self, **kwargs):
context = {
diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py
index b2f57e323..466bf7f4c 100644
--- a/apps/assets/views/asset.py
+++ b/apps/assets/views/asset.py
@@ -27,15 +27,14 @@ from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger, is_uuid
from common.const import create_success_msg, update_success_msg
from .. import forms
-from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
+from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser, Label, Node
from ..hands import AdminUserRequiredMixin
__all__ = [
'AssetListView', 'AssetCreateView', 'AssetUpdateView',
'UserAssetListView', 'AssetBulkUpdateView', 'AssetDetailView',
- 'AssetModalListView', 'AssetDeleteView', 'AssetExportView',
- 'BulkImportAssetView',
+ 'AssetDeleteView', 'AssetExportView', 'BulkImportAssetView',
]
logger = get_logger(__file__)
@@ -44,10 +43,11 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
template_name = 'assets/asset_list.html'
def get_context_data(self, **kwargs):
+ Node.root()
context = {
'app': _('Assets'),
'action': _('Asset list'),
- 'system_users': SystemUser.objects.all(),
+ 'labels': Label.objects.all().order_by('name'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
@@ -58,8 +58,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs):
context = {
- 'app': _('Assets'),
- 'action': _('Asset list'),
+ 'action': _('My assets'),
'system_users': SystemUser.objects.all(),
}
kwargs.update(context)
@@ -72,12 +71,23 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
template_name = 'assets/asset_create.html'
success_url = reverse_lazy('assets:asset-list')
- def form_valid(self, form):
- asset = form.save()
- asset.created_by = self.request.user.username or 'Admin'
- asset.date_created = timezone.now()
- asset.save()
- return super().form_valid(form)
+ # def form_valid(self, form):
+ # print("form valid")
+ # asset = form.save()
+ # asset.created_by = self.request.user.username or 'Admin'
+ # asset.date_created = timezone.now()
+ # asset.save()
+ # return super().form_valid(form)
+
+ def get_form(self, form_class=None):
+ form = super().get_form(form_class=form_class)
+ node_id = self.request.GET.get("node_id")
+ if node_id:
+ node = get_object_or_none(Node, id=node_id)
+ else:
+ node = Node.root()
+ form["nodes"].initial = node
+ return form
def get_context_data(self, **kwargs):
context = {
@@ -91,22 +101,22 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
return create_success_msg % ({"name": cleaned_data["hostname"]})
-class AssetModalListView(AdminUserRequiredMixin, ListView):
- paginate_by = settings.DISPLAY_PER_PAGE
- model = Asset
- context_object_name = 'asset_modal_list'
- template_name = 'assets/asset_modal_list.html'
-
- def get_context_data(self, **kwargs):
- assets = Asset.objects.all()
- assets_id = self.request.GET.get('assets_id', '')
- assets_id_list = [i for i in assets_id.split(',') if i.isdigit()]
- context = {
- 'all_assets': assets_id_list,
- 'assets': assets
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
+# class AssetModalListView(AdminUserRequiredMixin, ListView):
+# paginate_by = settings.DISPLAY_PER_PAGE
+# model = Asset
+# context_object_name = 'asset_modal_list'
+# template_name = 'assets/_asset_list_modal.html'
+#
+# def get_context_data(self, **kwargs):
+# assets = Asset.objects.all()
+# assets_id = self.request.GET.get('assets_id', '')
+# assets_id_list = [i for i in assets_id.split(',') if i.isdigit()]
+# context = {
+# 'all_assets': assets_id_list,
+# 'assets': assets
+# }
+# kwargs.update(context)
+# return super().get_context_data(**kwargs)
class AssetBulkUpdateView(AdminUserRequiredMixin, ListView):
@@ -180,14 +190,11 @@ class AssetDetailView(DetailView):
template_name = 'assets/asset_detail.html'
def get_context_data(self, **kwargs):
- asset_groups = self.object.groups.all()
+ nodes_remain = Node.objects.exclude(assets=self.object)
context = {
'app': _('Assets'),
'action': _('Asset detail'),
- 'asset_groups_remain': [asset_group for asset_group in AssetGroup.objects.all()
- if asset_group not in asset_groups],
- 'asset_groups': asset_groups,
- 'system_users_all': SystemUser.objects.all(),
+ 'nodes_remain': nodes_remain,
}
kwargs.update(context)
return super().get_context_data(**kwargs)
@@ -206,22 +213,19 @@ class AssetExportView(View):
]
]
filename = 'assets-{}.csv'.format(
- timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
+ timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S')
+ )
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response.write(codecs.BOM_UTF8)
assets = Asset.objects.filter(id__in=assets_id)
- writer = csv.writer(response, dialect='excel',
- quoting=csv.QUOTE_MINIMAL)
+ writer = csv.writer(response, dialect='excel', quoting=csv.QUOTE_MINIMAL)
header = [field.verbose_name for field in fields]
- header.append(_('Asset groups'))
writer.writerow(header)
for asset in assets:
- groups = ','.join([group.name for group in asset.groups.all()])
data = [getattr(asset, field.name) for field in fields]
- data.append(groups)
writer.writerow(data)
return response
@@ -243,6 +247,7 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
f = form.cleaned_data['file']
det_result = chardet.detect(f.read())
f.seek(0) # reset file seek index
+
file_data = f.read().decode(det_result['encoding']).strip(codecs.BOM_UTF8.decode())
csv_file = StringIO(file_data)
reader = csv.reader(csv_file)
@@ -255,7 +260,6 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
]
header_ = csv_data[0]
mapping_reverse = {field.verbose_name: field.name for field in fields}
- mapping_reverse[_('Asset groups')] = 'groups'
attr = [mapping_reverse.get(n, None) for n in header_]
if None in attr:
data = {'valid': False,
@@ -272,20 +276,15 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
asset_dict = dict(zip(attr, row))
id_ = asset_dict.pop('id', 0)
for k, v in asset_dict.items():
- if k == 'cluster':
- v = get_object_or_none(Cluster, name=v)
- elif k == 'is_active':
- v = bool(v)
+ if k == 'is_active':
+ v = True if v in ['TRUE', 1, 'true'] else False
elif k == 'admin_user':
v = get_object_or_none(AdminUser, name=v)
- elif k in ['port', 'cabinet_pos', 'cpu_count', 'cpu_cores']:
+ elif k in ['port', 'cpu_count', 'cpu_cores']:
try:
v = int(v)
except ValueError:
v = 0
- elif k == 'groups':
- groups_name = v.split(',')
- v = AssetGroup.objects.filter(name__in=groups_name)
else:
continue
asset_dict[k] = v
@@ -293,20 +292,15 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
asset = get_object_or_none(Asset, id=id_) if is_uuid(id_) else None
if not asset:
try:
- groups = asset_dict.pop('groups')
if len(Asset.objects.filter(hostname=asset_dict.get('hostname'))):
raise Exception(_('already exists'))
asset = Asset.objects.create(**asset_dict)
- asset.groups.set(groups)
created.append(asset_dict['hostname'])
assets.append(asset)
except Exception as e:
failed.append('%s: %s' % (asset_dict['hostname'], str(e)))
else:
for k, v in asset_dict.items():
- if k == 'groups':
- asset.groups.set(v)
- continue
if v:
setattr(asset, k, v)
try:
diff --git a/apps/assets/views/cluster.py b/apps/assets/views/cluster.py
deleted file mode 100644
index 835229fc1..000000000
--- a/apps/assets/views/cluster.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# coding:utf-8
-from django.utils.translation import ugettext as _
-from django.views.generic import TemplateView, ListView, View
-from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
-from django.urls import reverse_lazy
-from django.views.generic.detail import DetailView, SingleObjectMixin
-from django.contrib.messages.views import SuccessMessageMixin
-
-from common.const import create_success_msg, update_success_msg
-from .. import forms
-from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
-from ..hands import AdminUserRequiredMixin
-
-
-__all__ = [
- 'ClusterListView', 'ClusterCreateView', 'ClusterUpdateView',
- 'ClusterDetailView', 'ClusterDeleteView', 'ClusterAssetsView',
-]
-
-
-class ClusterListView(AdminUserRequiredMixin, TemplateView):
- template_name = 'assets/cluster_list.html'
-
- def get_context_data(self, **kwargs):
- context = {
- 'app': _('Assets'),
- 'action': _('Cluster list'),
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
-
-class ClusterCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
- model = Cluster
- form_class = forms.ClusterForm
- template_name = 'assets/cluster_create_update.html'
- success_url = reverse_lazy('assets:cluster-list')
- success_message = create_success_msg
-
- def get_context_data(self, **kwargs):
- context = {
- 'app': _('assets'),
- 'action': _('Create cluster'),
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
- def form_valid(self, form):
- cluster = form.save()
- cluster.created_by = self.request.user.username
- cluster.save()
- return super().form_valid(form)
-
-
-class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
- model = Cluster
- form_class = forms.ClusterForm
- template_name = 'assets/cluster_create_update.html'
- context_object_name = 'cluster'
- success_url = reverse_lazy('assets:cluster-list')
- success_message = update_success_msg
-
- def form_valid(self, form):
- cluster = form.save(commit=False)
- cluster.save()
- return super().form_valid(form)
-
- def get_context_data(self, **kwargs):
- context = {
- 'app': _('assets'),
- 'action': _('Update Cluster'),
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
-
-class ClusterDetailView(AdminUserRequiredMixin, DetailView):
- model = Cluster
- template_name = 'assets/cluster_detail.html'
- context_object_name = 'cluster'
-
- def get_context_data(self, **kwargs):
- admin_user_list = AdminUser.objects.exclude()
- context = {
- 'app': _('Assets'),
- 'action': _('Cluster detail'),
- 'admin_user_list': admin_user_list,
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
-
-class ClusterAssetsView(AdminUserRequiredMixin, DetailView):
- model = Cluster
- template_name = 'assets/cluster_assets.html'
- context_object_name = 'cluster'
-
- def get_context_data(self, **kwargs):
- assets_remain = Asset.objects.exclude(id__in=self.object.assets.all())
-
- context = {
- 'app': _('Assets'),
- 'action': _('Asset detail'),
- 'groups': AssetGroup.objects.all(),
- 'system_users': SystemUser.objects.all(),
- 'assets_remain': assets_remain,
- 'assets': [asset for asset in Asset.objects.all() if asset not in assets_remain],
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
-
-class ClusterDeleteView(AdminUserRequiredMixin, DeleteView):
- model = Cluster
- template_name = 'delete_confirm.html'
- success_url = reverse_lazy('assets:cluster-list')
diff --git a/apps/assets/views/group.py b/apps/assets/views/group.py
deleted file mode 100644
index 0ae65eaa0..000000000
--- a/apps/assets/views/group.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# coding:utf-8
-from __future__ import absolute_import, unicode_literals
-
-from django.utils.translation import ugettext as _
-from django.views.generic import TemplateView, ListView, View
-from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
-from django.urls import reverse_lazy
-from django.views.generic.detail import DetailView, SingleObjectMixin
-from django.shortcuts import get_object_or_404, reverse, redirect
-from django.contrib.messages.views import SuccessMessageMixin
-
-from common.const import create_success_msg, update_success_msg
-from .. import forms
-from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
-from ..hands import AdminUserRequiredMixin
-
-
-__all__ = [
- 'AssetGroupCreateView', 'AssetGroupDetailView',
- 'AssetGroupUpdateView', 'AssetGroupListView',
- 'AssetGroupDeleteView',
-]
-
-
-class AssetGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
- model = AssetGroup
- form_class = forms.AssetGroupForm
- template_name = 'assets/asset_group_create.html'
- success_url = reverse_lazy('assets:asset-group-list')
- success_message = create_success_msg
-
- def get_context_data(self, **kwargs):
- context = {
- 'app': _('Assets'),
- 'action': _('Create asset group'),
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
- def form_valid(self, form):
- group = form.save()
- group.created_by = self.request.user.username
- group.save()
- return super().form_valid(form)
-
-
-class AssetGroupListView(AdminUserRequiredMixin, TemplateView):
- template_name = 'assets/asset_group_list.html'
-
- def get_context_data(self, **kwargs):
- context = {
- 'app': _('Assets'),
- 'action': _('Asset group list'),
- 'assets': Asset.objects.all(),
- 'system_users': SystemUser.objects.all(),
- }
- kwargs.update(context)
- return super(AssetGroupListView, self).get_context_data(**kwargs)
-
-
-class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
- model = AssetGroup
- template_name = 'assets/asset_group_detail.html'
- context_object_name = 'asset_group'
-
- def get_context_data(self, **kwargs):
- assets_remain = Asset.objects.exclude(groups__in=[self.object])
- context = {
- 'app': _('Assets'),
- 'action': _('Asset group detail'),
- 'assets_remain': assets_remain,
- 'assets': self.object.assets.all(),
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
-
-class AssetGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
- model = AssetGroup
- form_class = forms.AssetGroupForm
- template_name = 'assets/asset_group_create.html'
- success_url = reverse_lazy('assets:asset-group-list')
- success_message = update_success_msg
-
- def get_context_data(self, **kwargs):
- context = {
- 'app': _('Assets'),
- 'action': _('Create asset group'),
- }
- kwargs.update(context)
- return super().get_context_data(**kwargs)
-
-
-class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
- template_name = 'delete_confirm.html'
- model = AssetGroup
- success_url = reverse_lazy('assets:asset-group-list')
diff --git a/apps/assets/views/label.py b/apps/assets/views/label.py
new file mode 100644
index 000000000..0b2d0d6ad
--- /dev/null
+++ b/apps/assets/views/label.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+#
+
+from django.views.generic import TemplateView, CreateView, \
+ UpdateView, DeleteView, DetailView
+from django.utils.translation import ugettext_lazy as _
+from django.urls import reverse_lazy
+
+from common.mixins import AdminUserRequiredMixin
+from common.const import create_success_msg, update_success_msg
+from ..models import Label
+from ..forms import LabelForm
+
+
+__all__ = (
+ "LabelListView", "LabelCreateView", "LabelUpdateView",
+ "LabelDetailView", "LabelDeleteView",
+)
+
+
+class LabelListView(AdminUserRequiredMixin, TemplateView):
+ template_name = 'assets/label_list.html'
+
+ def get_context_data(self, **kwargs):
+ context = {
+ 'app': _('Assets'),
+ 'action': _('Label list'),
+ }
+ kwargs.update(context)
+ return super().get_context_data(**kwargs)
+
+
+class LabelCreateView(AdminUserRequiredMixin, CreateView):
+ model = Label
+ template_name = 'assets/label_create_update.html'
+ form_class = LabelForm
+ success_url = reverse_lazy('assets:label-list')
+ success_message = create_success_msg
+
+ def get_context_data(self, **kwargs):
+ context = {
+ 'app': _('Assets'),
+ 'action': _('Create label'),
+ }
+ kwargs.update(context)
+ return super().get_context_data(**kwargs)
+
+
+class LabelUpdateView(AdminUserRequiredMixin, UpdateView):
+ model = Label
+ template_name = 'assets/label_create_update.html'
+ form_class = LabelForm
+ success_url = reverse_lazy('assets:label-list')
+ success_message = update_success_msg
+
+ def get_context_data(self, **kwargs):
+ context = {
+ 'app': _('Assets'),
+ 'action': _('Update label'),
+ }
+ kwargs.update(context)
+ return super().get_context_data(**kwargs)
+
+
+class LabelDetailView(AdminUserRequiredMixin, DetailView):
+ pass
+
+
+class LabelDeleteView(AdminUserRequiredMixin, DeleteView):
+ model = Label
+ template_name = 'delete_confirm.html'
+ success_url = reverse_lazy('assets:label-list')
diff --git a/apps/assets/views/system_user.py b/apps/assets/views/system_user.py
index f7e7eaa35..2e4eacf56 100644
--- a/apps/assets/views/system_user.py
+++ b/apps/assets/views/system_user.py
@@ -8,8 +8,8 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView
from common.const import create_success_msg, update_success_msg
-from ..forms import SystemUserForm, SystemUserUpdateForm
-from ..models import SystemUser, Cluster
+from ..forms import SystemUserForm
+from ..models import SystemUser, Node
from ..hands import AdminUserRequiredMixin
@@ -50,7 +50,7 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
class SystemUserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = SystemUser
- form_class = SystemUserUpdateForm
+ form_class = SystemUserForm
template_name = 'assets/system_user_update.html'
success_url = reverse_lazy('assets:system-user-list')
success_message = update_success_msg
@@ -70,11 +70,10 @@ class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
model = SystemUser
def get_context_data(self, **kwargs):
- cluster_remain = Cluster.objects.exclude(systemuser=self.object)
context = {
'app': _('Assets'),
'action': _('System user detail'),
- 'cluster_remain': cluster_remain,
+ 'nodes_remain': Node.objects.exclude(systemuser=self.object)
}
kwargs.update(context)
return super().get_context_data(**kwargs)
diff --git a/apps/common/api.py b/apps/common/api.py
index e8680e85e..58a5822c1 100644
--- a/apps/common/api.py
+++ b/apps/common/api.py
@@ -9,7 +9,7 @@ from django.core.mail import get_connection, send_mail
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
-from .permissions import IsSuperUser
+from .permissions import IsSuperUser, IsAppUser
from .serializers import MailTestSerializer, LDAPTestSerializer
@@ -63,8 +63,6 @@ class LDAPTestingAPI(APIView):
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
- print(serializer.validated_data)
-
try:
attr_map = json.loads(attr_map)
except json.JSONDecodeError:
@@ -77,9 +75,6 @@ class LDAPTestingAPI(APIView):
except Exception as e:
return Response({"error": str(e)}, status=401)
- print(search_ou)
- print(search_filter % ({"user": "*"}))
- print(attr_map.values())
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
attributes=list(attr_map.values()))
if not ok:
@@ -93,7 +88,7 @@ class LDAPTestingAPI(APIView):
user[attr] = getattr(entry, mapping)
users.append(user)
if len(users) > 0:
- return Response({"msg": "Match {} s users".format(len(users))})
+ return Response({"msg": _("Match {} s users").format(len(users))})
else:
return Response({"error": "Have user but attr mapping error"}, status=401)
else:
@@ -102,9 +97,11 @@ class LDAPTestingAPI(APIView):
class DjangoSettingsAPI(APIView):
def get(self, request):
+ if not settings.DEBUG:
+ return Response('Only debug mode support')
+
configs = {}
for i in dir(settings):
if i.isupper():
configs[i] = str(getattr(settings, i))
return Response(configs)
-
diff --git a/apps/common/fields.py b/apps/common/fields.py
index 36a8bdf9a..e4645b683 100644
--- a/apps/common/fields.py
+++ b/apps/common/fields.py
@@ -5,6 +5,8 @@ import json
from django import forms
from django.utils import six
from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext as _
+from rest_framework import serializers
class DictField(forms.Field):
@@ -18,16 +20,16 @@ class DictField(forms.Field):
# we don't need to handle that explicitly.
if isinstance(value, six.string_types):
try:
- print(value)
value = json.loads(value)
return value
except json.JSONDecodeError:
- pass
- value = {}
- return value
+ return ValidationError(_("Not a valid json"))
+ else:
+ return ValidationError(_("Not a string type"))
def validate(self, value):
- print(value)
+ if isinstance(value, ValidationError):
+ raise value
if not value and self.required:
raise ValidationError(self.error_messages['required'], code='required')
@@ -35,3 +37,9 @@ class DictField(forms.Field):
# Sometimes data or initial may be a string equivalent of a boolean
# so we should run it through to_python first to get a boolean value
return self.to_python(initial) != self.to_python(data)
+
+
+class StringIDField(serializers.Field):
+ def to_representation(self, value):
+ return {"pk": value.pk, "name": value.__str__()}
+
diff --git a/apps/common/forms.py b/apps/common/forms.py
index 6b83c54cc..8a6c87fe4 100644
--- a/apps/common/forms.py
+++ b/apps/common/forms.py
@@ -4,7 +4,9 @@ import json
from django import forms
from django.utils.translation import ugettext_lazy as _
+from django.utils.html import escape
from django.db import transaction
+from django.conf import settings
from .models import Setting
from .fields import DictField
@@ -24,34 +26,38 @@ def to_form_value(value):
data = value
return data
except json.JSONDecodeError:
- return ''
+ return ""
class BaseForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- settings = Setting.objects.all()
+ db_settings = Setting.objects.all()
for name, field in self.fields.items():
- db_value = getattr(settings, name).value
- if db_value:
+ db_value = getattr(db_settings, name).value
+ django_value = getattr(settings, name) if hasattr(settings, name) else None
+ if db_value is False or db_value:
field.initial = to_form_value(db_value)
+ elif django_value is False or django_value:
+ field.initial = to_form_value(to_model_value(django_value))
- def save(self):
+ def save(self, category="default"):
if not self.is_bound:
raise ValueError("Form is not bound")
- settings = Setting.objects.all()
+ db_settings = Setting.objects.all()
if self.is_valid():
with transaction.atomic():
for name, value in self.cleaned_data.items():
field = self.fields[name]
if isinstance(field.widget, forms.PasswordInput) and not value:
continue
- if value == to_form_value(getattr(settings, name).value):
+ if value == to_form_value(getattr(db_settings, name).value):
continue
defaults = {
'name': name,
+ 'category': category,
'value': to_model_value(value)
}
Setting.objects.update_or_create(defaults=defaults, name=name)
@@ -62,10 +68,10 @@ class BaseForm(forms.Form):
class BasicSettingForm(BaseForm):
SITE_URL = forms.URLField(
label=_("Current SITE URL"),
- help_text="http://jumpserver.abc.com:8080"
+ help_text="eg: http://jumpserver.abc.com:8080"
)
USER_GUIDE_URL = forms.URLField(
- label=_("User Guide URL"),
+ label=_("User Guide URL"), required=False,
help_text=_("User first login update profile done redirect to it")
)
EMAIL_SUBJECT_PREFIX = forms.CharField(
@@ -111,7 +117,8 @@ class LDAPSettingForm(BaseForm):
label=_("User OU"), initial='ou=tech,dc=jumpserver,dc=org'
)
AUTH_LDAP_SEARCH_FILTER = forms.CharField(
- label=_("User search filter"), initial='(cn=%(user)s)'
+ label=_("User search filter"), initial='(cn=%(user)s)',
+ help_text=_("User search filter must contain ([cn,uid,sAMAccountName,...]=%(user)s)")
)
AUTH_LDAP_USER_ATTR_MAP = DictField(
label=_("User attr map"),
@@ -119,13 +126,45 @@ class LDAPSettingForm(BaseForm):
"username": "cn",
"name": "sn",
"email": "mail"
- })
+ }),
+ help_text=_(
+ "User attr map present how to map LDAP user attr to jumpserver, username,name,email is jumpserver attr")
)
# AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU
# AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER
AUTH_LDAP_START_TLS = forms.BooleanField(
label=_("Use SSL"), initial=False, required=False
)
- AUTH_LDAP = forms.BooleanField(
- label=_("Enable LDAP Auth"), initial=False, required=False
+ AUTH_LDAP = forms.BooleanField(label=_("Enable LDAP auth"), initial=False, required=False)
+
+
+class TerminalSettingForm(BaseForm):
+ SORT_BY_CHOICES = (
+ ('hostname', _('Hostname')),
+ ('ip', _('IP')),
)
+ TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField(
+ choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by")
+ )
+ TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField(
+ initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds")
+ )
+ TERMINAL_PASSWORD_AUTH = forms.BooleanField(
+ initial=True, required=False, label=_("Password auth")
+ )
+ TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField(
+ initial=True, required=False, label=_("Public key auth")
+ )
+ TERMINAL_COMMAND_STORAGE = DictField(
+ label=_("Command storage"), help_text=_(
+ "Set terminal storage setting, `default` is the using as default,"
+ "You can set other storage and some terminal using"
+ )
+ )
+ TERMINAL_REPLAY_STORAGE = DictField(
+ label=_("Replay storage"), help_text=_(
+ "Set replay storage setting, `default` is the using as default,"
+ "You can set other storage and some terminal using"
+ )
+ )
+
diff --git a/apps/common/mixins.py b/apps/common/mixins.py
index d1afb081b..243ee93c6 100644
--- a/apps/common/mixins.py
+++ b/apps/common/mixins.py
@@ -47,9 +47,9 @@ class JSONResponseMixin(object):
return JsonResponse(context)
-class CustomFilterMixin(object):
+class IDInFilterMixin(object):
def filter_queryset(self, queryset):
- queryset = super(CustomFilterMixin, self).filter_queryset(queryset)
+ queryset = super(IDInFilterMixin, self).filter_queryset(queryset)
id_list = self.request.query_params.get('id__in')
if id_list:
import json
@@ -99,9 +99,8 @@ class DatetimeSearchMixin:
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, self.date_format)
- self.date_from = date_from.replace(
- tzinfo=timezone.get_current_timezone()
- )
+ tz = timezone.get_current_timezone()
+ self.date_from = tz.localize(date_from)
else:
self.date_from = timezone.now() - timezone.timedelta(7)
diff --git a/apps/common/models.py b/apps/common/models.py
index 091b8b83a..1f634bce2 100644
--- a/apps/common/models.py
+++ b/apps/common/models.py
@@ -2,6 +2,7 @@ import json
import ldap
from django.db import models
+from django.db.utils import ProgrammingError, OperationalError
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django_auth_ldap.config import LDAPSearch
@@ -24,6 +25,7 @@ class SettingManager(models.Manager):
class Setting(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
value = models.TextField(verbose_name=_("Value"))
+ category = models.CharField(max_length=128, default="default")
enabled = models.BooleanField(verbose_name=_("Enabled"), default=True)
comment = models.TextField(verbose_name=_("Comment"))
@@ -33,17 +35,28 @@ class Setting(models.Model):
return self.name
@property
- def value_(self):
+ def cleaned_value(self):
try:
return json.loads(self.value)
except json.JSONDecodeError:
return None
+ @cleaned_value.setter
+ def cleaned_value(self, item):
+ try:
+ v = json.dumps(item)
+ self.value = v
+ except json.JSONDecodeError as e:
+ raise ValueError("Json dump error: {}".format(str(e)))
+
@classmethod
def refresh_all_settings(cls):
- settings_list = cls.objects.all()
- for setting in settings_list:
- setting.refresh_setting()
+ try:
+ settings_list = cls.objects.all()
+ for setting in settings_list:
+ setting.refresh_setting()
+ except (ProgrammingError, OperationalError):
+ pass
def refresh_setting(self):
try:
@@ -53,9 +66,9 @@ class Setting(models.Model):
setattr(settings, self.name, value)
if self.name == "AUTH_LDAP":
- if self.value_ and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
+ if self.cleaned_value and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND)
- elif not self.value_ and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
+ elif not self.cleaned_value and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
if self.name == "AUTH_LDAP_SEARCH_FILTER":
diff --git a/apps/common/templates/common/basic_setting.html b/apps/common/templates/common/basic_setting.html
index fb5039795..496eca977 100644
--- a/apps/common/templates/common/basic_setting.html
+++ b/apps/common/templates/common/basic_setting.html
@@ -20,6 +20,9 @@
{% trans 'LDAP setting' %}
+
+ {% trans 'Terminal setting' %}
+
diff --git a/apps/common/templates/common/email_setting.html b/apps/common/templates/common/email_setting.html
index 7561849bd..1fd772db1 100644
--- a/apps/common/templates/common/email_setting.html
+++ b/apps/common/templates/common/email_setting.html
@@ -20,6 +20,9 @@
{% trans 'LDAP setting' %}
+
+ {% trans 'Terminal setting' %}
+
diff --git a/apps/common/templates/common/ldap_setting.html b/apps/common/templates/common/ldap_setting.html
index 26f021569..f0569f873 100644
--- a/apps/common/templates/common/ldap_setting.html
+++ b/apps/common/templates/common/ldap_setting.html
@@ -20,6 +20,9 @@
{% trans 'LDAP setting' %}
+
+ {% trans 'Terminal setting' %}
+
diff --git a/apps/common/templates/common/terminal_setting.html b/apps/common/templates/common/terminal_setting.html
new file mode 100644
index 000000000..16927c05a
--- /dev/null
+++ b/apps/common/templates/common/terminal_setting.html
@@ -0,0 +1,150 @@
+{% extends 'base.html' %}
+{% load static %}
+{% load bootstrap3 %}
+{% load i18n %}
+{% load common_tags %}
+
+{% block content %}
+
+
+{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
diff --git a/apps/common/templatetags/common_tags.py b/apps/common/templatetags/common_tags.py
index c10c228c8..9123ecef8 100644
--- a/apps/common/templatetags/common_tags.py
+++ b/apps/common/templatetags/common_tags.py
@@ -73,17 +73,20 @@ def to_html(s):
@register.filter
def time_util_with_seconds(date_from, date_to):
- if date_from and date_to:
- delta = date_to - date_from
- seconds = delta.seconds
- if seconds < 60:
- return '{} s'.format(seconds)
- elif seconds < 60*60:
- return '{} m'.format(seconds//60)
- else:
- return '{} h'.format(seconds//3600)
- else:
+ if not date_from:
return ''
+ if not date_to:
+ return ''
+ date_to = timezone.now()
+
+ delta = date_to - date_from
+ seconds = delta.seconds
+ if seconds < 60:
+ return '{} s'.format(seconds)
+ elif seconds < 60*60:
+ return '{} m'.format(seconds//60)
+ else:
+ return '{} h'.format(seconds//3600)
@register.filter
@@ -92,3 +95,8 @@ def is_bool_field(field):
return True
else:
return False
+
+
+@register.filter
+def to_dict(data):
+ return dict(data)
diff --git a/apps/common/urls/view_urls.py b/apps/common/urls/view_urls.py
index ff8086bde..466f7c49c 100644
--- a/apps/common/urls/view_urls.py
+++ b/apps/common/urls/view_urls.py
@@ -10,4 +10,5 @@ urlpatterns = [
url(r'^$', views.BasicSettingView.as_view(), name='basic-setting'),
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
+ url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
]
diff --git a/apps/common/utils.py b/apps/common/utils.py
index f366e6786..0915564ae 100644
--- a/apps/common/utils.py
+++ b/apps/common/utils.py
@@ -238,9 +238,10 @@ def content_md5(data):
"""
if isinstance(data, str):
data = hashlib.md5(data.encode('utf-8'))
- value = base64.b64encode(data.digest())
+ value = base64.b64encode(data.hexdigest().encode('utf-8'))
return value.decode('utf-8')
+
_STRPTIME_LOCK = threading.Lock()
_GMT_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
diff --git a/apps/common/views.py b/apps/common/views.py
index 43b249cee..99b924423 100644
--- a/apps/common/views.py
+++ b/apps/common/views.py
@@ -1,9 +1,12 @@
-from django.views.generic import View, TemplateView
+from django.views.generic import TemplateView
from django.shortcuts import render, redirect
from django.contrib import messages
from django.utils.translation import ugettext as _
+from django.conf import settings
-from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm
+from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
+ TerminalSettingForm
+from .models import Setting
from .mixins import AdminUserRequiredMixin
from .signals import ldap_auth_enable
@@ -86,3 +89,34 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
context = self.get_context_data()
context.update({"form": form})
return render(request, self.template_name, context)
+
+
+class TerminalSettingView(AdminUserRequiredMixin, TemplateView):
+ form_class = TerminalSettingForm
+ template_name = "common/terminal_setting.html"
+
+ def get_context_data(self, **kwargs):
+ command_storage = settings.TERMINAL_COMMAND_STORAGE
+ replay_storage = settings.TERMINAL_REPLAY_STORAGE
+ context = {
+ 'app': _('Settings'),
+ 'action': _('Terminal setting'),
+ 'form': self.form_class(),
+ 'replay_storage': replay_storage,
+ 'command_storage': command_storage,
+ }
+ kwargs.update(context)
+ return super().get_context_data(**kwargs)
+
+ def post(self, request):
+ form = self.form_class(request.POST)
+ if form.is_valid():
+ form.save()
+ msg = _("Update setting successfully, please restart program")
+ messages.success(request, msg)
+ return redirect('settings:terminal-setting')
+ else:
+ context = self.get_context_data()
+ context.update({"form": form})
+ return render(request, self.template_name, context)
+
diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo
new file mode 100644
index 000000000..2861b0128
Binary files /dev/null and b/apps/i18n/zh/LC_MESSAGES/django.mo differ
diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po
similarity index 63%
rename from apps/locale/zh/LC_MESSAGES/django.po
rename to apps/i18n/zh/LC_MESSAGES/django.po
index ebcc5ac66..2a98c8c0c 100644
--- a/apps/locale/zh/LC_MESSAGES/django.po
+++ b/apps/i18n/zh/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-01-17 17:26+0800\n"
+"POT-Creation-Date: 2018-03-07 11:54+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler \n"
"Language-Team: Jumpserver team\n"
@@ -17,120 +17,77 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: assets/forms.py:23 assets/forms.py:53 assets/forms.py:99 perms/forms.py:37
-#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:246
-msgid "Select asset groups"
-msgstr "选择资产组"
+#: assets/api/node.py:55
+msgid "New node {}"
+msgstr "新节点 {}"
-#: assets/forms.py:24 assets/templates/assets/admin_user_detail.html:92
-msgid "Select cluster"
-msgstr "选择集群"
+#: assets/forms/asset.py:23 assets/forms/asset.py:52 assets/forms/user.py:125
+#: assets/models/asset.py:53 assets/models/user.py:218
+#: assets/templates/assets/asset_detail.html:181
+#: assets/templates/assets/asset_detail.html:189
+#: assets/templates/assets/system_user_detail.html:164
+msgid "Nodes"
+msgstr "节点管理"
-#: assets/forms.py:25
-msgid "Select admin user"
-msgstr "选择管理用户"
+#: assets/forms/asset.py:26 assets/forms/asset.py:55 assets/forms/asset.py:90
+#: assets/forms/asset.py:94 assets/models/asset.py:57
+#: assets/models/cluster.py:19 assets/models/user.py:187
+#: assets/templates/assets/asset_detail.html:73 templates/_nav.html:24
+msgid "Admin user"
+msgstr "管理用户"
-#: assets/forms.py:33 assets/forms.py:61
-msgid "Host level admin user, If not set using cluster admin user default"
-msgstr "主机级别管理用户,如果没有设置则默认使用集群级别管理用户"
+#: assets/forms/asset.py:29 assets/forms/asset.py:58 assets/models/asset.py:81
+#: assets/templates/assets/asset_create.html:32
+#: assets/templates/assets/asset_detail.html:218
+#: assets/templates/assets/asset_update.html:37 templates/_nav.html:26
+msgid "Labels"
+msgstr "标签管理"
-#: assets/forms.py:40 assets/forms.py:68
-msgid "You need set a admin user if cluster not have"
-msgstr "集群没有管理用户,你需要为集群设置管理用户或设置一个主机级别的管理用户"
+#: assets/forms/asset.py:37 assets/forms/asset.py:68
+msgid ""
+"Admin user is a privilege user exist on this asset,Example: root or other "
+"NOPASSWD sudo privilege user"
+msgstr "管理用户是资产上已经存在的特权用户,如 root或者其它有NOPASSWD的用户"
-#: assets/forms.py:54
-msgid "Default using cluster admin user"
-msgstr "默认使用管理用户"
-
-#: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127
-#: assets/templates/assets/asset_group_detail.html:75 perms/forms.py:34
-#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:243
+#: assets/forms/asset.py:77 assets/forms/asset.py:81 assets/forms/label.py:15
+#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:242
msgid "Select assets"
msgstr "选择资产"
-#: assets/forms.py:86 assets/models/asset.py:55
-#: assets/templates/assets/admin_user_assets.html:61
+#: assets/forms/asset.py:86 assets/models/asset.py:52
+#: assets/templates/assets/admin_user_assets.html:53
#: assets/templates/assets/asset_detail.html:69
-#: assets/templates/assets/asset_group_detail.html:52
-#: assets/templates/assets/asset_list.html:32
-#: assets/templates/assets/cluster_assets.html:53
-#: assets/templates/assets/system_user_asset.html:54
+#: assets/templates/assets/system_user_asset.html:51
#: assets/templates/assets/user_asset_list.html:21
-#: users/templates/users/user_group_granted_asset.html:51
msgid "Port"
msgstr "端口"
-#: assets/forms.py:124 assets/models/asset.py:171
+#: assets/forms/asset.py:106 assets/templates/assets/asset_create.html:36
+msgid "Select labels"
+msgstr "选择标签"
+
+#: assets/forms/asset.py:109 assets/templates/assets/admin_user_detail.html:91
+msgid "Select nodes"
+msgstr "选择节点"
+
+#: assets/forms/label.py:13 assets/models/asset.py:153
#: assets/templates/assets/admin_user_list.html:24
-#: assets/templates/assets/asset_group_list.html:16
+#: assets/templates/assets/label_list.html:16
#: assets/templates/assets/system_user_list.html:26 perms/models.py:17
-#: perms/templates/perms/asset_permission_create_update.html:40
-#: perms/templates/perms/asset_permission_list.html:28 templates/_nav.html:22
-#: terminal/backends/command/models.py:11 terminal/models.py:93
+#: terminal/backends/command/models.py:11 terminal/models.py:123
#: terminal/templates/terminal/command_list.html:40
#: terminal/templates/terminal/command_list.html:73
#: terminal/templates/terminal/session_list.html:41
#: terminal/templates/terminal/session_list.html:72
-#: users/templates/users/user_granted_asset.html:82
-#: users/templates/users/user_group_granted_asset.html:86
msgid "Asset"
msgstr "资产"
-#: assets/forms.py:161 perms/forms.py:40
-#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:249
-msgid "Select system users"
-msgstr "选择系统用户"
+#: assets/forms/user.py:24
+msgid "Password or private key passphrase"
+msgstr "密码或密钥密码"
-#: assets/forms.py:163
-#: assets/templates/assets/_asset_group_bulk_update_modal.html:22
-#: assets/templates/assets/cluster_list.html:22
-msgid "System users"
-msgstr "系统用户"
-
-#: assets/forms.py:165
-msgid "Selected system users will be create at cluster assets"
-msgstr "选择的系统用户将会在该集群资产上创建"
-
-#: assets/forms.py:173 assets/forms.py:248 assets/forms.py:308
-#: assets/models/cluster.py:18 assets/models/group.py:20
-#: assets/models/user.py:28 assets/templates/assets/admin_user_detail.html:56
-#: assets/templates/assets/admin_user_list.html:22
-#: assets/templates/assets/asset_group_list.html:15
-#: assets/templates/assets/cluster_detail.html:57
-#: assets/templates/assets/cluster_list.html:19
-#: assets/templates/assets/system_user_detail.html:58
-#: assets/templates/assets/system_user_list.html:24 common/models.py:25
-#: ops/models.py:31 ops/templates/ops/task_detail.html:56
-#: ops/templates/ops/task_list.html:34 perms/models.py:14
-#: perms/templates/perms/asset_permission_create_update.html:33
-#: perms/templates/perms/asset_permission_detail.html:62
-#: perms/templates/perms/asset_permission_list.html:25
-#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:14
-#: terminal/models.py:118 terminal/templates/terminal/terminal_detail.html:43
-#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
-#: users/models/user.py:35 users/templates/users/_select_user_modal.html:13
-#: users/templates/users/user_detail.html:62
-#: users/templates/users/user_granted_asset.html:81
-#: users/templates/users/user_group_detail.html:55
-#: users/templates/users/user_group_granted_asset.html:85
-#: users/templates/users/user_group_list.html:12
-#: users/templates/users/user_list.html:23
-#: users/templates/users/user_profile.html:51
-#: users/templates/users/user_pubkey_update.html:53
-msgid "Name"
-msgstr "名称"
-
-#: assets/forms.py:179
-msgid "Cluster level admin user"
-msgstr "集群级别管理用户"
-
-#: assets/forms.py:200
-msgid "Password or private key password"
-msgstr "密码或秘钥不合法"
-
-#: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30
-#: common/forms.py:107 users/forms.py:16 users/forms.py:24
-#: users/templates/users/login.html:56
+#: assets/forms/user.py:25 assets/models/user.py:30 common/forms.py:113
+#: users/forms.py:16 users/forms.py:24 users/templates/users/login.html:59
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11
#: users/templates/users/user_password_update.html:40
@@ -139,304 +96,233 @@ msgstr "密码或秘钥不合法"
msgid "Password"
msgstr "密码"
-#: assets/forms.py:204 assets/forms.py:264 users/models/user.py:45
+#: assets/forms/user.py:28 users/models/user.py:45
msgid "Private key"
msgstr "ssh私钥"
-#: assets/forms.py:229 assets/forms.py:290 assets/forms.py:354
+#: assets/forms/user.py:38
msgid "Invalid private key"
msgstr "ssh密钥不合法"
-#: assets/forms.py:240
+#: assets/forms/user.py:47
msgid "Password and private key file must be input one"
msgstr "密码和私钥, 必须输入一个"
-#: assets/forms.py:249 assets/forms.py:309 assets/models/user.py:29
+#: assets/forms/user.py:79 assets/forms/user.py:120 assets/models/cluster.py:18
+#: assets/models/group.py:20 assets/models/label.py:17 assets/models/user.py:28
+#: assets/templates/assets/admin_user_detail.html:56
+#: assets/templates/assets/admin_user_list.html:22
+#: assets/templates/assets/label_list.html:14
+#: assets/templates/assets/system_user_detail.html:58
+#: assets/templates/assets/system_user_list.html:24 common/models.py:26
+#: common/templates/common/terminal_setting.html:67
+#: common/templates/common/terminal_setting.html:85 ops/models.py:31
+#: ops/templates/ops/task_detail.html:56 ops/templates/ops/task_list.html:34
+#: perms/models.py:14 perms/templates/perms/asset_permission_detail.html:62
+#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:16
+#: terminal/models.py:149 terminal/templates/terminal/terminal_detail.html:43
+#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
+#: users/models/user.py:35 users/templates/users/_select_user_modal.html:13
+#: users/templates/users/user_detail.html:63
+#: users/templates/users/user_group_detail.html:55
+#: users/templates/users/user_group_list.html:12
+#: users/templates/users/user_list.html:23
+#: users/templates/users/user_profile.html:51
+#: users/templates/users/user_pubkey_update.html:53
+msgid "Name"
+msgstr "名称"
+
+#: assets/forms/user.py:80 assets/forms/user.py:121 assets/models/user.py:29
#: assets/templates/assets/admin_user_detail.html:60
#: assets/templates/assets/admin_user_list.html:23
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:25
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:14
-#: users/models/authentication.py:44 users/models/user.py:34
+#: users/models/authentication.py:45 users/models/user.py:34
#: users/templates/users/_select_user_modal.html:14
-#: users/templates/users/login.html:53
+#: users/templates/users/login.html:56
#: users/templates/users/login_log_list.html:49
-#: users/templates/users/user_detail.html:66
+#: users/templates/users/user_detail.html:67
#: users/templates/users/user_list.html:24
#: users/templates/users/user_profile.html:47
msgid "Username"
msgstr "用户名"
-#: assets/forms.py:297 assets/forms.py:360
-msgid "Auth info required, private_key or password"
-msgstr "密钥和密码必须填写一个"
+#: assets/forms/user.py:132
+msgid "If auto push checked, system user will be create at node assets"
+msgstr "如果选择了自动推送,系统用户将会创建在节点资产上"
-#: assets/forms.py:313
-msgid " Select clusters"
-msgstr "选择集群"
-
-#: assets/forms.py:320
-msgid "If auto push checked, system user will be create at cluster assets"
-msgstr "如果选择了自动推送,系统用户将会创建在集群资产上"
-
-#: assets/forms.py:321
+#: assets/forms/user.py:133
msgid "Auto push system user to asset"
msgstr "自动推送系统用户到资产"
-#: assets/forms.py:322
+#: assets/forms/user.py:134
msgid ""
"High level will be using login asset as default, if user was granted more "
"than 2 system user"
msgstr "高优先级的系统用户将会作为默认登录用户"
-#: assets/models/asset.py:34
-msgid "In use"
-msgstr "使用中"
-
-#: assets/models/asset.py:35
-msgid "Out of use"
-msgstr "未使用"
-
-#: assets/models/asset.py:38
-msgid "Server"
-msgstr "物理机"
-
-#: assets/models/asset.py:39
-msgid "VM"
-msgstr "虚拟机"
-
-#: assets/models/asset.py:40
-msgid "Switch"
-msgstr "交换机"
-
-#: assets/models/asset.py:41
-msgid "Router"
-msgstr "路由器"
-
-#: assets/models/asset.py:42
-msgid "Firewall"
-msgstr "防火墙"
-
-#: assets/models/asset.py:43
-msgid "Storage"
-msgstr "存储"
-
-#: assets/models/asset.py:46
-msgid "Production"
-msgstr "生产环境"
-
-#: assets/models/asset.py:47
-msgid "Development"
-msgstr "开发环境"
-
-#: assets/models/asset.py:48
-msgid "Testing"
-msgstr "测试环境"
-
-#: assets/models/asset.py:53 assets/templates/assets/admin_user_assets.html:60
+#: assets/models/asset.py:50 assets/templates/assets/_asset_list_modal.html:21
+#: assets/templates/assets/admin_user_assets.html:52
#: assets/templates/assets/asset_detail.html:61
-#: assets/templates/assets/asset_group_detail.html:51
-#: assets/templates/assets/asset_list.html:31
-#: assets/templates/assets/cluster_assets.html:52
-#: assets/templates/assets/system_user_asset.html:53
-#: assets/templates/assets/user_asset_list.html:20
+#: assets/templates/assets/asset_list.html:87
+#: assets/templates/assets/system_user_asset.html:50
+#: assets/templates/assets/user_asset_list.html:20 common/forms.py:144
#: perms/templates/perms/asset_permission_asset.html:55
#: users/templates/users/login_log_list.html:52
-#: users/templates/users/user_granted_asset.html:49
-#: users/templates/users/user_group_granted_asset.html:50
+#: users/templates/users/user_granted_asset.html:45
+#: users/templates/users/user_group_granted_asset.html:45
msgid "IP"
msgstr "IP"
-#: assets/models/asset.py:54 assets/templates/assets/admin_user_assets.html:59
+#: assets/models/asset.py:51 assets/templates/assets/_asset_list_modal.html:20
+#: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/asset_detail.html:57
-#: assets/templates/assets/asset_group_detail.html:50
-#: assets/templates/assets/asset_list.html:30
-#: assets/templates/assets/cluster_assets.html:51
-#: assets/templates/assets/system_user_asset.html:52
-#: assets/templates/assets/user_asset_list.html:19
+#: assets/templates/assets/asset_list.html:86
+#: assets/templates/assets/system_user_asset.html:49
+#: assets/templates/assets/user_asset_list.html:19 common/forms.py:143
#: perms/templates/perms/asset_permission_asset.html:54
-#: users/templates/users/user_granted_asset.html:48
-#: users/templates/users/user_group_granted_asset.html:49
+#: users/templates/users/user_granted_asset.html:44
+#: users/templates/users/user_group_granted_asset.html:44
msgid "Hostname"
msgstr "主机名"
-#: assets/models/asset.py:56 assets/templates/assets/asset_detail.html:213
-#: assets/views/asset.py:218 assets/views/asset.py:258
-msgid "Asset groups"
-msgstr "资产组"
-
-#: assets/models/asset.py:57 assets/models/cluster.py:40
-#: assets/models/user.py:219 assets/templates/assets/asset_detail.html:85
-#: assets/templates/assets/asset_list.html:33 templates/_nav.html:24
-msgid "Cluster"
-msgstr "集群"
-
-#: assets/models/asset.py:58 assets/templates/assets/asset_detail.html:129
+#: assets/models/asset.py:54 assets/models/label.py:20
+#: assets/templates/assets/asset_detail.html:105
+#: perms/templates/perms/asset_permission_list.html:70
msgid "Is active"
msgstr "激活"
-#: assets/models/asset.py:59 assets/templates/assets/asset_detail.html:133
-msgid "Asset type"
-msgstr "系统类型"
-
-#: assets/models/asset.py:60 assets/templates/assets/asset_detail.html:137
-msgid "Asset environment"
-msgstr "资产环境"
-
-#: assets/models/asset.py:61 assets/templates/assets/asset_detail.html:125
-msgid "Asset status"
-msgstr "资产状态"
-
-#: assets/models/asset.py:64 assets/models/cluster.py:19
-#: assets/models/user.py:190 assets/templates/assets/asset_detail.html:73
-#: assets/templates/assets/cluster_list.html:20 templates/_nav.html:25
-msgid "Admin user"
-msgstr "管理用户"
-
-#: assets/models/asset.py:67 assets/templates/assets/asset_detail.html:65
+#: assets/models/asset.py:60 assets/templates/assets/asset_detail.html:65
msgid "Public IP"
msgstr "公网IP"
-#: assets/models/asset.py:68
-msgid "Remote control card IP"
-msgstr "远控卡IP"
-
-#: assets/models/asset.py:69 assets/templates/assets/asset_detail.html:89
-msgid "Cabinet number"
-msgstr "机柜编号"
-
-#: assets/models/asset.py:70 assets/templates/assets/asset_detail.html:93
-msgid "Cabinet position"
-msgstr "机柜层号"
-
-#: assets/models/asset.py:71 assets/templates/assets/asset_detail.html:145
+#: assets/models/asset.py:61 assets/templates/assets/asset_detail.html:113
msgid "Asset number"
msgstr "资产编号"
-#: assets/models/asset.py:74 assets/templates/assets/asset_detail.html:97
+#: assets/models/asset.py:64 assets/templates/assets/asset_detail.html:77
msgid "Vendor"
msgstr "制造商"
-#: assets/models/asset.py:75 assets/templates/assets/asset_detail.html:101
+#: assets/models/asset.py:65 assets/templates/assets/asset_detail.html:81
msgid "Model"
msgstr "型号"
-#: assets/models/asset.py:76 assets/templates/assets/asset_detail.html:141
+#: assets/models/asset.py:66 assets/templates/assets/asset_detail.html:109
msgid "Serial number"
msgstr "序列号"
-#: assets/models/asset.py:78
+#: assets/models/asset.py:68
msgid "CPU model"
msgstr "CPU型号"
-#: assets/models/asset.py:79
+#: assets/models/asset.py:69
msgid "CPU count"
msgstr "CPU数量"
-#: assets/models/asset.py:80
+#: assets/models/asset.py:70
msgid "CPU cores"
msgstr "CPU核数"
-#: assets/models/asset.py:81 assets/templates/assets/asset_detail.html:109
+#: assets/models/asset.py:71 assets/templates/assets/asset_detail.html:89
msgid "Memory"
msgstr "内存"
-#: assets/models/asset.py:82
+#: assets/models/asset.py:72
msgid "Disk total"
msgstr "硬盘大小"
-#: assets/models/asset.py:83
+#: assets/models/asset.py:73
msgid "Disk info"
msgstr "硬盘信息"
-#: assets/models/asset.py:85 assets/templates/assets/asset_detail.html:117
+#: assets/models/asset.py:75 assets/templates/assets/asset_detail.html:97
msgid "Platform"
msgstr "系统平台"
-#: assets/models/asset.py:86 assets/templates/assets/asset_detail.html:121
+#: assets/models/asset.py:76 assets/templates/assets/asset_detail.html:101
msgid "OS"
msgstr "操作系统"
-#: assets/models/asset.py:87
+#: assets/models/asset.py:77
msgid "OS version"
msgstr "系统版本"
-#: assets/models/asset.py:88
+#: assets/models/asset.py:78
msgid "OS arch"
msgstr "系统架构"
-#: assets/models/asset.py:89
+#: assets/models/asset.py:79
msgid "Hostname raw"
msgstr "主机名原始"
-#: assets/models/asset.py:91 assets/models/cluster.py:28
+#: assets/models/asset.py:82 assets/models/cluster.py:28
#: assets/models/group.py:21 assets/models/user.py:36
#: assets/templates/assets/admin_user_detail.html:68
-#: assets/templates/assets/asset_detail.html:149
-#: assets/templates/assets/cluster_detail.html:93
+#: assets/templates/assets/asset_detail.html:117
#: assets/templates/assets/system_user_detail.html:96
-#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:22
+#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:22 perms/models.py:79
#: perms/templates/perms/asset_permission_detail.html:94
-#: users/models/user.py:50 users/templates/users/user_detail.html:98
+#: users/models/user.py:50 users/templates/users/user_detail.html:99
msgid "Created by"
msgstr "创建者"
-#: assets/models/asset.py:92 assets/models/cluster.py:26
-#: assets/models/group.py:22 assets/templates/assets/admin_user_detail.html:64
-#: assets/templates/assets/cluster_detail.html:89
+#: assets/models/asset.py:83 assets/models/cluster.py:26
+#: assets/models/group.py:22 assets/models/label.py:23
+#: assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/system_user_detail.html:92
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:60
-#: perms/models.py:23 perms/templates/perms/asset_permission_detail.html:90
+#: perms/models.py:23 perms/models.py:80
+#: perms/templates/perms/asset_permission_detail.html:90
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17
#: users/templates/users/user_group_detail.html:63
msgid "Date created"
msgstr "创建日期"
-#: assets/models/asset.py:93 assets/models/cluster.py:29
-#: assets/models/group.py:23 assets/models/user.py:33
+#: assets/models/asset.py:84 assets/models/cluster.py:29
+#: assets/models/group.py:23 assets/models/label.py:21 assets/models/user.py:33
#: assets/templates/assets/admin_user_detail.html:72
#: assets/templates/assets/admin_user_list.html:28
-#: assets/templates/assets/asset_detail.html:157
-#: assets/templates/assets/asset_group_list.html:17
-#: assets/templates/assets/cluster_detail.html:97
+#: assets/templates/assets/asset_detail.html:125
#: assets/templates/assets/system_user_detail.html:100
-#: assets/templates/assets/system_user_list.html:30 common/models.py:28
-#: ops/models.py:37 perms/models.py:24
-#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:22
+#: assets/templates/assets/system_user_list.html:30 common/models.py:30
+#: ops/models.py:37 perms/models.py:24 perms/models.py:81
+#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:26
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
-#: users/models/user.py:47 users/templates/users/user_detail.html:110
+#: users/models/user.py:47 users/templates/users/user_detail.html:111
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:14
-#: users/templates/users/user_profile.html:118
+#: users/templates/users/user_profile.html:114
msgid "Comment"
msgstr "备注"
-#: assets/models/cluster.py:20 assets/templates/assets/cluster_detail.html:61
+#: assets/models/cluster.py:20
msgid "Bandwidth"
msgstr "带宽"
-#: assets/models/cluster.py:21 assets/templates/assets/cluster_detail.html:65
+#: assets/models/cluster.py:21
msgid "Contact"
msgstr "联系人"
-#: assets/models/cluster.py:22 assets/templates/assets/cluster_detail.html:69
-#: users/models/user.py:41 users/templates/users/user_detail.html:75
+#: assets/models/cluster.py:22 users/models/user.py:41
+#: users/templates/users/user_detail.html:76
msgid "Phone"
msgstr "手机"
-#: assets/models/cluster.py:23 assets/templates/assets/cluster_detail.html:73
+#: assets/models/cluster.py:23
msgid "Address"
msgstr "地址"
-#: assets/models/cluster.py:24 assets/templates/assets/cluster_detail.html:77
+#: assets/models/cluster.py:24
msgid "Intranet"
msgstr "内网"
-#: assets/models/cluster.py:25 assets/templates/assets/cluster_detail.html:81
+#: assets/models/cluster.py:25
msgid "Extranet"
msgstr "外网"
-#: assets/models/cluster.py:27 assets/templates/assets/cluster_detail.html:85
+#: assets/models/cluster.py:27
msgid "Operator"
msgstr "运营商"
@@ -444,7 +330,8 @@ msgstr "运营商"
msgid "Default"
msgstr "默认"
-#: assets/models/cluster.py:36 users/models/user.py:258
+#: assets/models/cluster.py:36 assets/models/label.py:13
+#: users/models/user.py:266
msgid "System"
msgstr "系统"
@@ -452,8 +339,11 @@ msgstr "系统"
msgid "Default Cluster"
msgstr "默认Cluster"
+#: assets/models/cluster.py:40
+msgid "Cluster"
+msgstr "集群"
+
#: assets/models/group.py:30 perms/models.py:18
-#: perms/templates/perms/asset_permission_list.html:29 templates/_nav.html:23
msgid "Asset group"
msgstr "资产组"
@@ -461,6 +351,31 @@ msgstr "资产组"
msgid "Default asset group"
msgstr "默认资产组"
+#: assets/models/label.py:14 perms/models.py:15
+#: terminal/backends/command/models.py:10 terminal/models.py:122
+#: terminal/templates/terminal/command_list.html:32
+#: terminal/templates/terminal/command_list.html:72
+#: terminal/templates/terminal/session_list.html:33
+#: terminal/templates/terminal/session_list.html:71 users/forms.py:190
+#: users/models/user.py:30 users/models/user.py:254
+#: users/templates/users/user_group_detail.html:78
+#: users/templates/users/user_group_list.html:13 users/views/user.py:333
+msgid "User"
+msgstr "用户"
+
+#: assets/models/label.py:18 assets/models/node.py:15
+#: assets/templates/assets/label_list.html:15 common/models.py:27
+msgid "Value"
+msgstr "值"
+
+#: assets/models/label.py:19
+msgid "Category"
+msgstr "分类"
+
+#: assets/models/node.py:14
+msgid "Key"
+msgstr ""
+
#: assets/models/user.py:31
msgid "SSH private key"
msgstr "ssh密钥"
@@ -469,38 +384,36 @@ msgstr "ssh密钥"
msgid "SSH public key"
msgstr "ssh公钥"
-#: assets/models/user.py:220
+#: assets/models/user.py:219
msgid "Priority"
msgstr "优先级"
-#: assets/models/user.py:221 assets/templates/assets/system_user_detail.html:66
+#: assets/models/user.py:220 assets/templates/assets/system_user_detail.html:66
msgid "Protocol"
msgstr "协议"
-#: assets/models/user.py:222 assets/templates/assets/_system_user.html:59
+#: assets/models/user.py:221 assets/templates/assets/_system_user.html:58
#: assets/templates/assets/system_user_detail.html:118
#: assets/templates/assets/system_user_update.html:11
msgid "Auto push"
msgstr "自动推送"
-#: assets/models/user.py:223 assets/templates/assets/system_user_detail.html:70
+#: assets/models/user.py:222 assets/templates/assets/system_user_detail.html:70
msgid "Sudo"
msgstr "Sudo"
-#: assets/models/user.py:224 assets/templates/assets/system_user_detail.html:75
+#: assets/models/user.py:223 assets/templates/assets/system_user_detail.html:75
msgid "Shell"
msgstr "Shell"
-#: assets/models/user.py:269 perms/models.py:19
-#: perms/templates/perms/asset_permission_detail.html:136
-#: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26
-#: terminal/backends/command/models.py:12 terminal/models.py:94
+#: assets/models/user.py:266 perms/forms.py:25 perms/models.py:19
+#: perms/models.py:76 perms/templates/perms/asset_permission_detail.html:136
+#: perms/templates/perms/asset_permission_list.html:69 templates/_nav.html:25
+#: terminal/backends/command/models.py:12 terminal/models.py:124
#: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74
#: terminal/templates/terminal/session_list.html:49
#: terminal/templates/terminal/session_list.html:73
-#: users/templates/users/user_granted_asset.html:50
-#: users/templates/users/user_group_granted_asset.html:52
msgid "System user"
msgstr "系统用户"
@@ -509,91 +422,80 @@ msgstr "系统用户"
msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number"
-#: assets/signals_handler.py:31
-msgid "Push cluster system users to asset"
-msgstr "推送集群系统用户到资产"
-
-#: assets/signals_handler.py:63 assets/signals_handler.py:125
-msgid "Push system user to cluster assets: {}->{}"
-msgstr "推送系统用户到: {}->{}"
-
-#: assets/signals_handler.py:102
-msgid "Push system user to assets"
-msgstr "推送系统用户到资产"
-
-#: assets/tasks.py:91
+#: assets/tasks.py:92
msgid "Update some assets hardware info"
msgstr "更新一些资产硬件信息"
-#: assets/tasks.py:107
+#: assets/tasks.py:108
msgid "Update asset hardware info"
msgstr "更新资产硬件信息"
-#: assets/tasks.py:121
+#: assets/tasks.py:122
msgid "Update assets hardware info period"
msgstr "定期更新资产硬件信息"
-#: assets/tasks.py:189
+#: assets/tasks.py:195
msgid "Test admin user connectability period: {}"
msgstr "定期测试管理用户可连接性: {}"
-#: assets/tasks.py:203
+#: assets/tasks.py:201
msgid "Test admin user connectability: {}"
msgstr "测试管理用户可连接性: {}"
-#: assets/tasks.py:212
+#: assets/tasks.py:210
msgid "Test asset connectability"
msgstr "测试资产可连接性"
-#: assets/tasks.py:283
+#: assets/tasks.py:281
msgid "Test system user connectability: {}"
msgstr "测试系统用户可连接性: {}"
-#: assets/tasks.py:295
-msgid "Test system user connectability period: {}"
-msgstr "定期测试系统用户可连接性: {}"
+#: assets/tasks.py:292
+msgid "test system user connectability period: {}"
+msgstr "测试系统用户可连接性: {}"
-#: assets/tasks.py:376
-msgid "Push system user to cluster assets: {}"
-msgstr "推送系统用户到资产: {}"
+#: assets/tasks.py:365
+msgid "Push system user to node: {} => {}"
+msgstr "推送系统用户到节点: {}->{}"
#: assets/tasks.py:397
-msgid "Push cluster system users to assets period: {}"
-msgstr "定期推送集群系统用户到资产: {}"
+msgid "Push system users to node: {}"
+msgstr "推送系统用户到节点: {}"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:5
msgid "Update asset group"
-msgstr "编辑用户组"
+msgstr "更新用户组"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:8
msgid "Hint: only change the field you want to update."
msgstr "仅修改你需要更新的字段"
-#: assets/templates/assets/_asset_group_bulk_update_modal.html:12
+#: assets/templates/assets/_asset_group_bulk_update_modal.html:11
#: assets/templates/assets/system_user_asset.html:21
#: assets/views/admin_user.py:29 assets/views/admin_user.py:47
-#: assets/views/admin_user.py:63 assets/views/admin_user.py:79
-#: assets/views/admin_user.py:106 assets/views/asset.py:48
-#: assets/views/asset.py:61 assets/views/asset.py:84 assets/views/asset.py:144
-#: assets/views/asset.py:161 assets/views/asset.py:185
-#: assets/views/cluster.py:26 assets/views/cluster.py:85
-#: assets/views/cluster.py:102 assets/views/group.py:34
-#: assets/views/group.py:52 assets/views/group.py:69 assets/views/group.py:87
-#: assets/views/system_user.py:28 assets/views/system_user.py:44
-#: assets/views/system_user.py:60 assets/views/system_user.py:75
-#: templates/_nav.html:19
+#: assets/views/admin_user.py:63 assets/views/admin_user.py:78
+#: assets/views/admin_user.py:102 assets/views/asset.py:48
+#: assets/views/asset.py:94 assets/views/asset.py:154 assets/views/asset.py:171
+#: assets/views/asset.py:195 assets/views/label.py:26 assets/views/label.py:42
+#: assets/views/label.py:58 assets/views/system_user.py:28
+#: assets/views/system_user.py:44 assets/views/system_user.py:60
+#: assets/views/system_user.py:74 templates/_nav.html:20
msgid "Assets"
msgstr "资产管理"
-#: assets/templates/assets/_asset_group_bulk_update_modal.html:14
+#: assets/templates/assets/_asset_group_bulk_update_modal.html:13
msgid "Select Asset"
msgstr "选择资产"
-#: assets/templates/assets/_asset_group_bulk_update_modal.html:24
+#: assets/templates/assets/_asset_group_bulk_update_modal.html:21
+msgid "System users"
+msgstr "系统用户"
+
+#: assets/templates/assets/_asset_group_bulk_update_modal.html:23
msgid "Select System Users"
msgstr "选择系统用户"
-#: assets/templates/assets/_asset_group_bulk_update_modal.html:35
+#: assets/templates/assets/_asset_group_bulk_update_modal.html:34
msgid "Enable-OTP"
msgstr "二次验证"
@@ -619,77 +521,96 @@ msgstr "资产csv文件"
msgid "If set id, will use this id update asset existed"
msgstr "如果设置了id,则会使用该行信息更新该id的资产"
-#: assets/templates/assets/_system_user.html:16
-#: assets/templates/assets/system_user_list.html:16
-#: assets/views/system_user.py:45
-msgid "Create system user"
-msgstr "创建系统用户"
+#: assets/templates/assets/_asset_list_modal.html:22
+#: assets/templates/assets/asset_list.html:88
+#: assets/templates/assets/user_asset_list.html:22
+msgid "Hardware"
+msgstr "硬件"
-#: assets/templates/assets/_system_user.html:37
-#: assets/templates/assets/asset_create.html:14
-#: assets/templates/assets/asset_update.html:19
-#: assets/templates/assets/cluster_create_update.html:35
-msgid "Basic"
-msgstr "基本"
+#: assets/templates/assets/_asset_list_modal.html:23
+#: assets/templates/assets/asset_detail.html:143
+#: assets/templates/assets/asset_list.html:89
+#: assets/templates/assets/user_asset_list.html:23 perms/models.py:20
+#: perms/models.py:77
+#: perms/templates/perms/asset_permission_create_update.html:51
+#: perms/templates/perms/asset_permission_detail.html:116
+#: terminal/templates/terminal/terminal_list.html:34
+#: users/templates/users/_select_user_modal.html:18
+#: users/templates/users/user_detail.html:128
+#: users/templates/users/user_granted_asset.html:46
+#: users/templates/users/user_group_granted_asset.html:46
+#: users/templates/users/user_list.html:27
+#: users/templates/users/user_profile.html:63
+msgid "Active"
+msgstr "激活中"
-#: assets/templates/assets/_system_user.html:45
-#: assets/templates/assets/asset_create.html:24
-#: assets/templates/assets/asset_update.html:29
-#: assets/templates/assets/system_user_update.html:7
-#: users/templates/users/user_create.html:9
-#: users/templates/users/user_update.html:6
-msgid "Auth"
-msgstr "认证"
+#: assets/templates/assets/_asset_list_modal.html:24
+#: assets/templates/assets/admin_user_assets.html:54
+#: assets/templates/assets/admin_user_list.html:25
+#: assets/templates/assets/asset_detail.html:357
+#: assets/templates/assets/asset_list.html:90
+#: assets/templates/assets/system_user_asset.html:52
+#: assets/templates/assets/system_user_list.html:27
+#: users/templates/users/user_granted_asset.html:47
+#: users/templates/users/user_group_granted_asset.html:47
+msgid "Reachable"
+msgstr "可连接"
-#: assets/templates/assets/_system_user.html:48
-msgid "Auto generate key"
-msgstr "自动生成秘钥"
+#: assets/templates/assets/_asset_list_modal.html:25
+#: assets/templates/assets/admin_user_list.html:29
+#: assets/templates/assets/asset_list.html:91
+#: assets/templates/assets/label_list.html:17
+#: assets/templates/assets/system_user_list.html:31
+#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:61
+#: ops/templates/ops/task_history.html:62 ops/templates/ops/task_list.html:41
+#: perms/templates/perms/asset_permission_list.html:72
+#: terminal/templates/terminal/session_list.html:80
+#: terminal/templates/terminal/terminal_list.html:36
+#: users/templates/users/user_group_list.html:15
+#: users/templates/users/user_list.html:28
+msgid "Action"
+msgstr "动作"
-#: assets/templates/assets/_system_user.html:65
-#: assets/templates/assets/asset_create.html:32
-#: assets/templates/assets/asset_update.html:47
-#: assets/templates/assets/cluster_create_update.html:46
-#: perms/templates/perms/asset_permission_create_update.html:45
-#: terminal/templates/terminal/terminal_update.html:40
-msgid "Other"
-msgstr "其它"
+#: assets/templates/assets/_asset_list_modal.html:34
+#: assets/templates/assets/asset_list.html:100
+#: users/templates/users/user_list.html:37
+msgid "Delete selected"
+msgstr "批量删除"
+#: assets/templates/assets/_asset_list_modal.html:35
+#: assets/templates/assets/asset_list.html:101
+#: users/templates/users/user_list.html:38
+msgid "Update selected"
+msgstr "批量更新"
+
+#: assets/templates/assets/_asset_list_modal.html:36
+#: assets/templates/assets/asset_list.html:103
+#: users/templates/users/user_list.html:39
+msgid "Deactive selected"
+msgstr "禁用所选"
+
+#: assets/templates/assets/_asset_list_modal.html:37
+#: assets/templates/assets/asset_list.html:104
+#: users/templates/users/user_list.html:40
+msgid "Active selected"
+msgstr "激活所选"
+
+#: assets/templates/assets/_asset_list_modal.html:41
#: assets/templates/assets/_system_user.html:71
-#: assets/templates/assets/admin_user_create_update.html:45
-#: assets/templates/assets/asset_bulk_update.html:23
-#: assets/templates/assets/asset_create.html:40
-#: assets/templates/assets/asset_group_create.html:16
-#: assets/templates/assets/asset_update.html:55
-#: assets/templates/assets/cluster_create_update.html:54
-#: common/templates/common/basic_setting.html:55
-#: common/templates/common/email_setting.html:56
-#: common/templates/common/ldap_setting.html:56
-#: perms/templates/perms/asset_permission_create_update.html:67
-#: terminal/templates/terminal/terminal_update.html:45
-#: users/templates/users/_user.html:49
-#: users/templates/users/user_bulk_update.html:23
-#: users/templates/users/user_password_update.html:58
-#: users/templates/users/user_profile.html:139
-#: users/templates/users/user_profile.html:147
-#: users/templates/users/user_profile_update.html:63
-#: users/templates/users/user_pubkey_update.html:70
-msgid "Reset"
-msgstr "重置"
-
-#: assets/templates/assets/_system_user.html:72
#: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24
-#: assets/templates/assets/asset_create.html:41
-#: assets/templates/assets/asset_group_create.html:17
-#: assets/templates/assets/asset_list.html:53
-#: assets/templates/assets/asset_update.html:56
-#: assets/templates/assets/cluster_create_update.html:55
-#: common/templates/common/basic_setting.html:56
-#: common/templates/common/email_setting.html:57
-#: common/templates/common/ldap_setting.html:57
-#: perms/templates/perms/asset_permission_create_update.html:68
-#: terminal/templates/terminal/terminal_update.html:46
-#: users/templates/users/_user.html:50
+#: assets/templates/assets/asset_create.html:66
+#: assets/templates/assets/asset_list.html:108
+#: assets/templates/assets/asset_update.html:70
+#: assets/templates/assets/label_create_update.html:17
+#: common/templates/common/basic_setting.html:59
+#: common/templates/common/email_setting.html:60
+#: common/templates/common/ldap_setting.html:60
+#: common/templates/common/terminal_setting.html:103
+#: perms/templates/perms/asset_permission_create_update.html:72
+#: terminal/templates/terminal/session_list.html:120
+#: terminal/templates/terminal/terminal_update.html:48
+#: users/templates/users/_user.html:44
#: users/templates/users/first_login.html:62
#: users/templates/users/forgot_password.html:44
#: users/templates/users/user_bulk_update.html:24
@@ -700,10 +621,97 @@ msgstr "重置"
msgid "Submit"
msgstr "提交"
+#: assets/templates/assets/_asset_list_modal.html:79
+#: assets/templates/assets/admin_user_detail.html:24
+#: assets/templates/assets/admin_user_list.html:84
+#: assets/templates/assets/asset_detail.html:24
+#: assets/templates/assets/asset_list.html:166
+#: assets/templates/assets/label_list.html:38
+#: assets/templates/assets/system_user_detail.html:26
+#: assets/templates/assets/system_user_list.html:85
+#: perms/templates/perms/asset_permission_detail.html:30
+#: perms/templates/perms/asset_permission_list.html:121
+#: terminal/templates/terminal/terminal_detail.html:16
+#: terminal/templates/terminal/terminal_list.html:71
+#: users/templates/users/user_detail.html:25
+#: users/templates/users/user_group_detail.html:28
+#: users/templates/users/user_group_list.html:43
+#: users/templates/users/user_list.html:76
+msgid "Update"
+msgstr "更新"
+
+#: assets/templates/assets/_asset_list_modal.html:80
+#: assets/templates/assets/admin_user_detail.html:28
+#: assets/templates/assets/admin_user_list.html:85
+#: assets/templates/assets/asset_detail.html:28
+#: assets/templates/assets/asset_list.html:167
+#: assets/templates/assets/label_list.html:39
+#: assets/templates/assets/system_user_detail.html:30
+#: assets/templates/assets/system_user_list.html:86
+#: ops/templates/ops/task_list.html:71
+#: perms/templates/perms/asset_permission_detail.html:34
+#: perms/templates/perms/asset_permission_list.html:122
+#: terminal/templates/terminal/terminal_list.html:73
+#: users/templates/users/user_detail.html:30
+#: users/templates/users/user_group_detail.html:32
+#: users/templates/users/user_group_list.html:45
+#: users/templates/users/user_list.html:80
+#: users/templates/users/user_list.html:84
+msgid "Delete"
+msgstr "删除"
+
+#: assets/templates/assets/_system_user.html:37
+#: assets/templates/assets/asset_create.html:16
+#: assets/templates/assets/asset_update.html:21
+#: perms/templates/perms/asset_permission_create_update.html:38
+msgid "Basic"
+msgstr "基本"
+
+#: assets/templates/assets/_system_user.html:44
+#: assets/templates/assets/asset_create.html:24
+#: assets/templates/assets/asset_update.html:29
+#: assets/templates/assets/system_user_update.html:7
+#: users/templates/users/user_create.html:9
+#: users/templates/users/user_update.html:6
+msgid "Auth"
+msgstr "认证"
+
+#: assets/templates/assets/_system_user.html:47
+msgid "Auto generate key"
+msgstr "自动生成密钥"
+
+#: assets/templates/assets/_system_user.html:64
+#: assets/templates/assets/asset_create.html:58
+#: assets/templates/assets/asset_update.html:62
+#: perms/templates/perms/asset_permission_create_update.html:49
+#: terminal/templates/terminal/terminal_update.html:42
+msgid "Other"
+msgstr "其它"
+
+#: assets/templates/assets/_system_user.html:70
+#: assets/templates/assets/admin_user_create_update.html:45
+#: assets/templates/assets/asset_bulk_update.html:23
+#: assets/templates/assets/asset_create.html:65
+#: assets/templates/assets/asset_update.html:69
+#: assets/templates/assets/label_create_update.html:16
+#: common/templates/common/basic_setting.html:58
+#: common/templates/common/email_setting.html:59
+#: common/templates/common/ldap_setting.html:59
+#: common/templates/common/terminal_setting.html:101
+#: perms/templates/perms/asset_permission_create_update.html:71
+#: terminal/templates/terminal/terminal_update.html:47
+#: users/templates/users/_user.html:43
+#: users/templates/users/user_bulk_update.html:23
+#: users/templates/users/user_password_update.html:58
+#: users/templates/users/user_profile.html:135
+#: users/templates/users/user_profile.html:143
+#: users/templates/users/user_profile_update.html:63
+#: users/templates/users/user_pubkey_update.html:70
+msgid "Reset"
+msgstr "重置"
+
#: assets/templates/assets/admin_user_assets.html:18
#: assets/templates/assets/admin_user_detail.html:18
-#: assets/templates/assets/cluster_assets.html:20
-#: assets/templates/assets/cluster_detail.html:17
#: assets/templates/assets/system_user_asset.html:17
#: assets/templates/assets/system_user_detail.html:18
#: ops/templates/ops/adhoc_history.html:129
@@ -720,129 +728,59 @@ msgstr "详情"
msgid "Assets list"
msgstr "资产列表"
-#: assets/templates/assets/admin_user_assets.html:24
-#: assets/templates/assets/admin_user_detail.html:24
-#: assets/templates/assets/admin_user_list.html:83
-#: assets/templates/assets/asset_detail.html:24
-#: assets/templates/assets/asset_group_detail.html:18
-#: assets/templates/assets/asset_group_detail.html:177
-#: assets/templates/assets/asset_group_list.html:38
-#: assets/templates/assets/asset_list.html:98
-#: assets/templates/assets/cluster_assets.html:170
-#: assets/templates/assets/cluster_detail.html:25
-#: assets/templates/assets/cluster_list.html:43
-#: assets/templates/assets/system_user_asset.html:25
-#: assets/templates/assets/system_user_detail.html:26
-#: assets/templates/assets/system_user_list.html:84
-#: perms/templates/perms/asset_permission_detail.html:30
-#: perms/templates/perms/asset_permission_list.html:73
-#: terminal/templates/terminal/terminal_detail.html:16
-#: terminal/templates/terminal/terminal_list.html:71
-#: users/templates/users/user_detail.html:25
-#: users/templates/users/user_group_detail.html:28
-#: users/templates/users/user_group_list.html:39
-#: users/templates/users/user_list.html:76
-msgid "Update"
-msgstr "更新"
-
-#: assets/templates/assets/admin_user_assets.html:28
-#: assets/templates/assets/admin_user_detail.html:28
-#: assets/templates/assets/admin_user_list.html:84
-#: assets/templates/assets/asset_detail.html:28
-#: assets/templates/assets/asset_group_detail.html:22
-#: assets/templates/assets/asset_group_list.html:39
-#: assets/templates/assets/asset_list.html:99
-#: assets/templates/assets/cluster_detail.html:29
-#: assets/templates/assets/cluster_list.html:44
-#: assets/templates/assets/system_user_detail.html:30
-#: assets/templates/assets/system_user_list.html:85
-#: ops/templates/ops/task_list.html:71
-#: perms/templates/perms/asset_permission_detail.html:34
-#: perms/templates/perms/asset_permission_list.html:74
-#: terminal/templates/terminal/terminal_list.html:73
-#: users/templates/users/user_detail.html:29
-#: users/templates/users/user_group_detail.html:32
-#: users/templates/users/user_group_list.html:41
-#: users/templates/users/user_list.html:80
-#: users/templates/users/user_list.html:84
-msgid "Delete"
-msgstr "删除"
-
-#: assets/templates/assets/admin_user_assets.html:37
-#: assets/templates/assets/asset_group_detail.html:31
+#: assets/templates/assets/admin_user_assets.html:29
#: perms/templates/perms/asset_permission_asset.html:35
msgid "Asset list of "
msgstr "资产列表"
-#: assets/templates/assets/admin_user_assets.html:62
-#: assets/templates/assets/asset_group_detail.html:53
-#: assets/templates/assets/cluster_assets.html:54
-#: assets/templates/assets/user_asset_list.html:22
-#: users/templates/users/login_log_list.html:50
-msgid "Type"
-msgstr "类型"
-
-#: assets/templates/assets/admin_user_assets.html:63
-#: assets/templates/assets/admin_user_list.html:25
-#: assets/templates/assets/asset_detail.html:376
-#: assets/templates/assets/asset_list.html:36
-#: assets/templates/assets/system_user_asset.html:55
-#: assets/templates/assets/system_user_list.html:27
-msgid "Reachable"
-msgstr "可连接"
-
-#: assets/templates/assets/admin_user_assets.html:75
-#: assets/templates/assets/cluster_assets.html:68
-#: assets/templates/assets/system_user_asset.html:67
+#: assets/templates/assets/admin_user_assets.html:66
+#: assets/templates/assets/system_user_asset.html:64
#: assets/templates/assets/system_user_detail.html:112
#: perms/templates/perms/asset_permission_detail.html:110
msgid "Quick update"
msgstr "快速更新"
-#: assets/templates/assets/admin_user_assets.html:81
-#: assets/templates/assets/asset_detail.html:199
+#: assets/templates/assets/admin_user_assets.html:72
+#: assets/templates/assets/asset_detail.html:167
msgid "Test connective"
msgstr "测试可连接性"
-#: assets/templates/assets/admin_user_assets.html:84
-#: assets/templates/assets/asset_detail.html:202
-#: assets/templates/assets/system_user_asset.html:84
+#: assets/templates/assets/admin_user_assets.html:75
+#: assets/templates/assets/asset_detail.html:170
+#: assets/templates/assets/system_user_asset.html:81
+#: assets/templates/assets/system_user_detail.html:145
msgid "Test"
msgstr "测试"
-#: assets/templates/assets/admin_user_assets.html:147
+#: assets/templates/assets/admin_user_assets.html:131
msgid "Task has been send, seen left asset status"
msgstr "任务已下发,查看左侧资产状态"
-#: assets/templates/assets/admin_user_create_update.html:16
+#: assets/templates/assets/admin_user_detail.html:83
+msgid "Replace node assets admin user with this"
+msgstr "替换资产的管理员"
+
+#: assets/templates/assets/admin_user_detail.html:100
+#: assets/templates/assets/asset_detail.html:198
+#: assets/templates/assets/asset_list.html:541
+#: assets/templates/assets/system_user_detail.html:181
+#: assets/templates/assets/system_user_list.html:135 templates/_modal.html:16
+#: terminal/templates/terminal/session_detail.html:108
+#: users/templates/users/user_detail.html:339
+#: users/templates/users/user_detail.html:364
+#: users/templates/users/user_detail.html:387
+#: users/templates/users/user_group_create_update.html:32
+#: users/templates/users/user_group_list.html:86
+#: users/templates/users/user_list.html:196
+#: users/templates/users/user_profile.html:177
+msgid "Confirm"
+msgstr "确认"
+
#: assets/templates/assets/admin_user_list.html:14
#: assets/views/admin_user.py:48
msgid "Create admin user"
msgstr "创建管理用户"
-#: assets/templates/assets/admin_user_detail.html:84
-msgid "Using this as cluster admin user"
-msgstr "使用集群管理用户"
-
-#: assets/templates/assets/admin_user_detail.html:101
-#: assets/templates/assets/asset_detail.html:230
-#: assets/templates/assets/asset_group_list.html:81
-#: assets/templates/assets/asset_list.html:220
-#: assets/templates/assets/cluster_assets.html:104
-#: assets/templates/assets/cluster_list.html:89
-#: assets/templates/assets/system_user_detail.html:164
-#: assets/templates/assets/system_user_list.html:134 templates/_modal.html:16
-#: terminal/templates/terminal/session_detail.html:108
-#: users/templates/users/user_detail.html:338
-#: users/templates/users/user_detail.html:363
-#: users/templates/users/user_detail.html:386
-#: users/templates/users/user_group_create_update.html:32
-#: users/templates/users/user_group_list.html:82
-#: users/templates/users/user_list.html:196
-#: users/templates/users/user_profile.html:181
-msgid "Confirm"
-msgstr "确认"
-
#: assets/templates/assets/admin_user_list.html:26
#: assets/templates/assets/system_user_list.html:28
msgid "Unreachable"
@@ -855,284 +793,130 @@ msgstr "不可达"
msgid "Ratio"
msgstr "比例"
-#: assets/templates/assets/admin_user_list.html:29
-#: assets/templates/assets/asset_group_detail.html:55
-#: assets/templates/assets/asset_group_list.html:18
-#: assets/templates/assets/asset_list.html:37
-#: assets/templates/assets/cluster_assets.html:56
-#: assets/templates/assets/cluster_list.html:23
-#: assets/templates/assets/system_user_list.html:31
-#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:61
-#: ops/templates/ops/task_history.html:62 ops/templates/ops/task_list.html:41
-#: perms/templates/perms/asset_permission_list.html:32
-#: terminal/templates/terminal/session_list.html:79
-#: terminal/templates/terminal/terminal_list.html:36
-#: users/templates/users/user_group_list.html:15
-#: users/templates/users/user_list.html:28
-msgid "Action"
-msgstr "动作"
-
#: assets/templates/assets/asset_create.html:28
-#: assets/templates/assets/asset_update.html:33
-msgid "Group"
-msgstr "组"
+#: assets/templates/assets/asset_update.html:33 perms/models.py:74
+#: perms/templates/perms/asset_permission_create_update.html:40
+#: perms/templates/perms/asset_permission_list.html:67
+msgid "Node"
+msgstr "节点"
-#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:186
-#: assets/views/cluster.py:103
+#: assets/templates/assets/asset_create.html:34
+#: assets/templates/assets/asset_list.html:75
+#: assets/templates/assets/asset_update.html:39
+msgid "Label"
+msgstr "标签"
+
+#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:196
msgid "Asset detail"
msgstr "资产详情"
-#: assets/templates/assets/asset_detail.html:81
-msgid "Remote card IP"
-msgstr "远控卡IP"
-
-#: assets/templates/assets/asset_detail.html:105
+#: assets/templates/assets/asset_detail.html:85
msgid "CPU"
msgstr "CPU"
-#: assets/templates/assets/asset_detail.html:113
+#: assets/templates/assets/asset_detail.html:93
msgid "Disk"
msgstr "硬盘"
-#: assets/templates/assets/asset_detail.html:153
-#: users/templates/users/user_detail.html:102
+#: assets/templates/assets/asset_detail.html:121
+#: users/templates/users/user_detail.html:103
#: users/templates/users/user_profile.html:88
msgid "Date joined"
msgstr "创建日期"
-#: assets/templates/assets/asset_detail.html:169
+#: assets/templates/assets/asset_detail.html:137
#: terminal/templates/terminal/session_detail.html:81
-#: users/templates/users/user_detail.html:121
-#: users/templates/users/user_profile.html:130
+#: users/templates/users/user_detail.html:122
+#: users/templates/users/user_profile.html:126
msgid "Quick modify"
msgstr "快速修改"
-#: assets/templates/assets/asset_detail.html:175
-#: assets/templates/assets/asset_list.html:35
-#: assets/templates/assets/user_asset_list.html:25 perms/models.py:20
-#: perms/templates/perms/asset_permission_create_update.html:47
-#: perms/templates/perms/asset_permission_detail.html:116
-#: terminal/templates/terminal/terminal_list.html:34
-#: users/templates/users/_select_user_modal.html:18
-#: users/templates/users/user_detail.html:127
-#: users/templates/users/user_list.html:27
-#: users/templates/users/user_profile.html:63
-msgid "Active"
-msgstr "激活中"
-
-#: assets/templates/assets/asset_detail.html:191
+#: assets/templates/assets/asset_detail.html:159
msgid "Refresh hardware"
msgstr "更新硬件信息"
-#: assets/templates/assets/asset_detail.html:194
+#: assets/templates/assets/asset_detail.html:162
msgid "Refresh"
msgstr "刷新"
-#: assets/templates/assets/asset_detail.html:221
-msgid "Join asset groups"
-msgstr "添加到资产组"
-
-#: assets/templates/assets/asset_detail.html:318
-#: users/templates/users/user_detail.html:272
+#: assets/templates/assets/asset_detail.html:299
+#: users/templates/users/user_detail.html:273
msgid "Update successfully!"
msgstr "更新成功"
-#: assets/templates/assets/asset_group_detail.html:16
-msgid "Group assets"
-msgstr "组下资产"
+#: assets/templates/assets/asset_list.html:63
+#: assets/templates/assets/asset_list.html:120 assets/views/asset.py:95
+msgid "Create asset"
+msgstr "创建资产"
-#: assets/templates/assets/asset_group_detail.html:54
-#: assets/templates/assets/cluster_assets.html:55
-#: terminal/templates/terminal/terminal_list.html:35
-msgid "Alive"
-msgstr "在线"
-
-#: assets/templates/assets/asset_group_detail.html:67
-msgid "Add assets to this group"
-msgstr "添加资产到该组"
-
-#: assets/templates/assets/asset_group_detail.html:84
-#: perms/templates/perms/asset_permission_asset.html:97
-#: perms/templates/perms/asset_permission_detail.html:153
-#: perms/templates/perms/asset_permission_user.html:97
-#: perms/templates/perms/asset_permission_user.html:125
-#: users/templates/users/user_group_detail.html:95
-msgid "Add"
-msgstr "添加"
-
-#: assets/templates/assets/asset_group_detail.html:178
-msgid "Remove"
-msgstr "移除"
-
-#: assets/templates/assets/asset_group_list.html:7 assets/views/group.py:35
-#: assets/views/group.py:88
-msgid "Create asset group"
-msgstr "创建资产组"
-
-#: assets/templates/assets/asset_group_list.html:76
-#: assets/templates/assets/asset_list.html:215
-#: assets/templates/assets/cluster_list.html:84
-#: assets/templates/assets/system_user_list.html:129
-#: users/templates/users/user_detail.html:333
-#: users/templates/users/user_detail.html:358
-#: users/templates/users/user_group_list.html:77
-#: users/templates/users/user_list.html:191
-msgid "Are you sure?"
-msgstr "你确认吗?"
-
-#: assets/templates/assets/asset_group_list.html:77
-#: users/templates/users/user_group_list.html:78
-msgid "This will delete the selected groups !!!"
-msgstr "删除选择组"
-
-#: assets/templates/assets/asset_group_list.html:85
-msgid "Group deleted"
-msgstr "组已被删除"
-
-#: assets/templates/assets/asset_group_list.html:86
-#: assets/templates/assets/asset_group_list.html:91
-msgid "Group Delete"
-msgstr "删除"
-
-#: assets/templates/assets/asset_group_list.html:90
-msgid "Group deleting failed."
-msgstr "删除失败"
-
-#: assets/templates/assets/asset_group_list.html:153
-msgid "The selected asset groups has been updated successfully."
-msgstr "更新成功"
-
-#: assets/templates/assets/asset_group_list.html:154
-msgid "AssetGroup Updated"
-msgstr "资产组更新"
-
-#: assets/templates/assets/asset_list.html:15
+#: assets/templates/assets/asset_list.html:67
#: users/templates/users/user_list.html:7
msgid "Import"
msgstr "导入"
-#: assets/templates/assets/asset_list.html:18
+#: assets/templates/assets/asset_list.html:70
#: users/templates/users/user_list.html:10
msgid "Export"
msgstr "导出"
-#: assets/templates/assets/asset_list.html:25 assets/views/asset.py:85
-msgid "Create asset"
-msgstr "创建资产"
+#: assets/templates/assets/asset_list.html:102
+msgid "Remove from this node"
+msgstr "从节点移除"
-#: assets/templates/assets/asset_list.html:34
-#: assets/templates/assets/user_asset_list.html:24
-msgid "Hardware"
-msgstr "硬件"
+#: assets/templates/assets/asset_list.html:121
+msgid "Add asset"
+msgstr "添加资产到节点"
-#: assets/templates/assets/asset_list.html:46
-#: users/templates/users/user_list.html:37
-msgid "Delete selected"
-msgstr "批量删除"
+#: assets/templates/assets/asset_list.html:123
+msgid "Add node"
+msgstr "新建节点"
-#: assets/templates/assets/asset_list.html:47
-#: users/templates/users/user_list.html:38
-msgid "Update selected"
-msgstr "批量更新"
+#: assets/templates/assets/asset_list.html:124
+msgid "Rename node"
+msgstr "重命名节点"
-#: assets/templates/assets/asset_list.html:48
-#: users/templates/users/user_list.html:39
-msgid "Deactive selected"
-msgstr "禁用所选"
+#: assets/templates/assets/asset_list.html:126
+msgid "Delete node"
+msgstr "删除节点"
-#: assets/templates/assets/asset_list.html:49
-#: users/templates/users/user_list.html:40
-msgid "Active selected"
-msgstr "激活所选"
+#: assets/templates/assets/asset_list.html:201
+msgid "Create node failed"
+msgstr "创建节点失败"
-#: assets/templates/assets/asset_list.html:216
+#: assets/templates/assets/asset_list.html:214
+msgid "Have child node, cancel"
+msgstr "存在子节点,不能删除"
+
+#: assets/templates/assets/asset_list.html:536
+#: assets/templates/assets/system_user_list.html:130
+#: users/templates/users/user_detail.html:334
+#: users/templates/users/user_detail.html:359
+#: users/templates/users/user_group_list.html:81
+#: users/templates/users/user_list.html:191
+msgid "Are you sure?"
+msgstr "你确认吗?"
+
+#: assets/templates/assets/asset_list.html:537
msgid "This will delete the selected assets !!!"
msgstr "删除选择资产"
-# msgid "Deleted!"
-# msgstr "删除"
-#: assets/templates/assets/asset_list.html:224
+#: assets/templates/assets/asset_list.html:545
msgid "Asset Deleted."
msgstr "已被删除"
-#: assets/templates/assets/asset_list.html:225
-#: assets/templates/assets/asset_list.html:230
+#: assets/templates/assets/asset_list.html:546
+#: assets/templates/assets/asset_list.html:551
msgid "Asset Delete"
msgstr "删除"
-#: assets/templates/assets/asset_list.html:229
+#: assets/templates/assets/asset_list.html:550
msgid "Asset Deleting failed."
msgstr "删除失败"
-#: assets/templates/assets/asset_update.html:37
+#: assets/templates/assets/asset_update.html:58
msgid "Configuration"
msgstr "配置"
-#: assets/templates/assets/asset_update.html:42
-msgid "Location"
-msgstr "位置"
-
-#: assets/templates/assets/cluster_assets.html:23
-#: assets/templates/assets/cluster_assets.html:31
-#: assets/templates/assets/cluster_detail.html:21
-msgid "Cluster assets"
-msgstr "集群中资产"
-
-#: assets/templates/assets/cluster_assets.html:74
-#: assets/templates/assets/system_user_asset.html:81
-msgid "Test assets connective"
-msgstr "测试资产可连接性"
-
-#: assets/templates/assets/cluster_assets.html:77
-#: ops/templates/ops/task_list.html:70
-msgid "Run"
-msgstr "执行"
-
-#: assets/templates/assets/cluster_assets.html:87
-msgid "Add assets to"
-msgstr "添加资产到"
-
-#: assets/templates/assets/cluster_assets.html:95
-msgid "Select asset"
-msgstr "选择资产"
-
-#: assets/templates/assets/cluster_assets.html:211
-#: assets/templates/assets/system_user_asset.html:162
-msgid "Task has been send, seen left assets status"
-msgstr "任务已下发,查看左侧资产状态"
-
-#: assets/templates/assets/cluster_create_update.html:41
-#: users/templates/users/reset_password.html:57
-#: users/templates/users/user_profile.html:20
-msgid "Setting"
-msgstr "设置"
-
-#: assets/templates/assets/cluster_list.html:11 assets/views/cluster.py:43
-msgid "Create cluster"
-msgstr "创建集群"
-
-#: assets/templates/assets/cluster_list.html:21
-#: users/templates/users/_select_user_modal.html:17
-msgid "Asset num"
-msgstr "资产数量"
-
-#: assets/templates/assets/cluster_list.html:85
-msgid "This will delete the selected cluster"
-msgstr "删除选择Cluster"
-
-#: assets/templates/assets/cluster_list.html:93
-msgid "Cluster Deleted."
-msgstr "集群已被删除"
-
-#: assets/templates/assets/cluster_list.html:94
-#: assets/templates/assets/cluster_list.html:99
-msgid "Cluster delete"
-msgstr "删除集群"
-
-#: assets/templates/assets/cluster_list.html:98
-msgid "Cluster deleting failed."
-msgstr "Cluster删除失败"
-
#: assets/templates/assets/delete_confirm.html:6
#: perms/templates/perms/delete_confirm.html:6 templates/delete_confirm.html:6
msgid "Confirm delete"
@@ -1143,25 +927,38 @@ msgstr "确认删除"
msgid "Are you sure delete"
msgstr "您确定删除吗?"
-#: assets/templates/assets/system_user_asset.html:33
+#: assets/templates/assets/label_list.html:6 assets/views/label.py:43
+msgid "Create label"
+msgstr "创建标签"
+
+#: assets/templates/assets/system_user_asset.html:30
msgid "Assets of "
msgstr "资产"
-#: assets/templates/assets/system_user_asset.html:73
+#: assets/templates/assets/system_user_asset.html:70
+#: assets/templates/assets/system_user_detail.html:134
msgid "Push system user manually"
msgstr "手动推送系统"
-#: assets/templates/assets/system_user_asset.html:76
+#: assets/templates/assets/system_user_asset.html:73
+#: assets/templates/assets/system_user_detail.html:137
msgid "Push"
msgstr "推送"
-#: assets/templates/assets/system_user_asset.html:150
+#: assets/templates/assets/system_user_asset.html:78
+#: assets/templates/assets/system_user_detail.html:142
+msgid "Test assets connective"
+msgstr "测试资产可连接性"
+
+#: assets/templates/assets/system_user_asset.html:147
+#: assets/templates/assets/system_user_detail.html:301
msgid "Task has been send, Go to ops task list seen result"
msgstr "任务已下发,查看ops任务列表"
-#: assets/templates/assets/system_user_detail.html:22
-msgid "Attached assets"
-msgstr "关联的资产"
+#: assets/templates/assets/system_user_asset.html:159
+#: assets/templates/assets/system_user_detail.html:313
+msgid "Task has been send, seen left assets status"
+msgstr "任务已下发,查看左侧资产状态"
#: assets/templates/assets/system_user_detail.html:81
msgid "Home"
@@ -1171,36 +968,33 @@ msgstr "家目录"
msgid "Uid"
msgstr "Uid"
-#: assets/templates/assets/system_user_detail.html:147
-msgid "Clusters"
-msgstr "集群"
+#: assets/templates/assets/system_user_detail.html:172
+msgid "Add to node"
+msgstr "添加到节点"
-#: assets/templates/assets/system_user_detail.html:155
-msgid "Add to cluster"
-msgstr "添加到集群"
+#: assets/templates/assets/system_user_list.html:16
+#: assets/views/system_user.py:45
+msgid "Create system user"
+msgstr "创建系统用户"
-#: assets/templates/assets/system_user_list.html:130
+#: assets/templates/assets/system_user_list.html:131
msgid "This will delete the selected System Users !!!"
msgstr "删除选择系统用户"
-#: assets/templates/assets/system_user_list.html:138
+#: assets/templates/assets/system_user_list.html:139
msgid "System Users Deleted."
msgstr "已被删除"
-#: assets/templates/assets/system_user_list.html:139
-#: assets/templates/assets/system_user_list.html:144
+#: assets/templates/assets/system_user_list.html:140
+#: assets/templates/assets/system_user_list.html:145
msgid "System Users Delete"
msgstr "删除系统用户"
-#: assets/templates/assets/system_user_list.html:143
+#: assets/templates/assets/system_user_list.html:144
msgid "System Users Deleting failed."
msgstr "系统用户删除失败"
-#: assets/templates/assets/user_asset_list.html:23
-msgid "Env"
-msgstr "环境"
-
-#: assets/templates/assets/user_asset_list.html:26
+#: assets/templates/assets/user_asset_list.html:24
msgid "Connective"
msgstr "连接性"
@@ -1212,50 +1006,37 @@ msgstr "管理用户列表"
msgid "Update admin user"
msgstr "更新管理用户"
-#: assets/views/admin_user.py:80 assets/views/admin_user.py:107
+#: assets/views/admin_user.py:79 assets/views/admin_user.py:103
msgid "Admin user detail"
msgstr "管理用户详情"
-#: assets/views/asset.py:49 assets/views/asset.py:62
+#: assets/views/asset.py:49 templates/_nav.html:23
msgid "Asset list"
msgstr "资产列表"
-#: assets/views/asset.py:145
+#: assets/views/asset.py:61 templates/_nav_user.html:4
+msgid "My assets"
+msgstr "我的资产"
+
+#: assets/views/asset.py:155
msgid "Bulk update asset"
msgstr "批量更新资产"
-#: assets/views/asset.py:162
+#: assets/views/asset.py:172
msgid "Update asset"
-msgstr "编辑资产"
+msgstr "更新资产"
-#: assets/views/asset.py:298
+#: assets/views/asset.py:296
msgid "already exists"
msgstr "已经存在"
-#: assets/views/cluster.py:27
-msgid "Cluster list"
-msgstr "集群列表"
+#: assets/views/label.py:27
+msgid "Label list"
+msgstr "标签列表"
-#: assets/views/cluster.py:42 assets/views/cluster.py:70
-#: assets/views/system_user.py:96
-msgid "assets"
-msgstr "资产管理"
-
-#: assets/views/cluster.py:71
-msgid "Update Cluster"
-msgstr "更新Cluster"
-
-#: assets/views/cluster.py:86
-msgid "Cluster detail"
-msgstr "集群详情"
-
-#: assets/views/group.py:53
-msgid "Asset group list"
-msgstr "资产组列表"
-
-#: assets/views/group.py:70
-msgid "Asset group detail"
-msgstr "资产组详情"
+#: assets/views/label.py:59
+msgid "Update label"
+msgstr "更新标签"
#: assets/views/system_user.py:29
msgid "System user list"
@@ -1265,11 +1046,15 @@ msgstr "系统用户列表"
msgid "Update system user"
msgstr "更新系统用户"
-#: assets/views/system_user.py:76
+#: assets/views/system_user.py:75
msgid "System user detail"
msgstr "系统用户详情"
-#: assets/views/system_user.py:97
+#: assets/views/system_user.py:95
+msgid "assets"
+msgstr "资产管理"
+
+#: assets/views/system_user.py:96
msgid "System user asset"
msgstr "系统用户集群资产"
@@ -1281,6 +1066,10 @@ msgstr "邮件已经发送{}, 请检查"
msgid "Test ldap success"
msgstr "连接LDAP成功"
+#: common/api.py:91
+msgid "Match {} s users"
+msgstr "匹配 {} 个用户"
+
#: common/const.py:6
#, python-format
msgid "%(name)s was created successfully"
@@ -1291,81 +1080,144 @@ msgstr "%(name)s 创建成功"
msgid "%(name)s was updated successfully"
msgstr "%(name)s 更新成功"
-#: common/forms.py:64
+#: common/fields.py:26
+msgid "Not a valid json"
+msgstr "不是合法json"
+
+#: common/fields.py:28
+msgid "Not a string type"
+msgstr "不是字符类型"
+
+#: common/forms.py:70
msgid "Current SITE URL"
msgstr "当前站点URL"
-#: common/forms.py:68
+#: common/forms.py:74
msgid "User Guide URL"
msgstr "用户向导URL"
-#: common/forms.py:69
+#: common/forms.py:75
msgid "User first login update profile done redirect to it"
msgstr "用户第一次登录,修改profile后重定向到地址"
-#: common/forms.py:72
+#: common/forms.py:78
msgid "Email Subject Prefix"
msgstr "Email主题前缀"
-#: common/forms.py:79
+#: common/forms.py:85
msgid "SMTP host"
msgstr "SMTP主机"
-#: common/forms.py:81
+#: common/forms.py:87
msgid "SMTP port"
msgstr "SMTP端口"
-#: common/forms.py:83
+#: common/forms.py:89
msgid "SMTP user"
msgstr "SMTP账号"
-#: common/forms.py:86
+#: common/forms.py:92
msgid "SMTP password"
msgstr "SMTP密码"
-#: common/forms.py:87
+#: common/forms.py:93
msgid "Some provider use token except password"
msgstr "一些邮件提供商需要输入的是Token"
-#: common/forms.py:90 common/forms.py:127
+#: common/forms.py:96 common/forms.py:136
msgid "Use SSL"
msgstr "使用SSL"
-#: common/forms.py:91
+#: common/forms.py:97
msgid "If SMTP port is 465, may be select"
msgstr "如果SMTP端口是465,通常需要启用SSL"
-#: common/forms.py:94
+#: common/forms.py:100
msgid "Use TLS"
msgstr "使用TLS"
-#: common/forms.py:95
+#: common/forms.py:101
msgid "If SMTP port is 587, may be select"
msgstr "如果SMTP端口是587,通常需要启用TLS"
-#: common/forms.py:101
+#: common/forms.py:107
msgid "LDAP server"
msgstr "LDAP地址"
-#: common/forms.py:104
+#: common/forms.py:110
msgid "Bind DN"
msgstr "绑定DN"
-#: common/forms.py:111
+#: common/forms.py:117
msgid "User OU"
msgstr "用户OU"
-#: common/forms.py:114
+#: common/forms.py:120
msgid "User search filter"
msgstr "用户过滤器"
-#: common/forms.py:117
+#: common/forms.py:121
+#, python-format
+msgid "User search filter must contain ([cn,uid,sAMAccountName,...]=%(user)s)"
+msgstr "用户过滤器必须包含([cn,uid,sAMAccountName,...]=%(user)s)"
+
+#: common/forms.py:124
msgid "User attr map"
msgstr "LDAP属性映射"
-#: common/forms.py:130
-msgid "Enable LDAP Auth"
-msgstr "开启LDAP认证"
+#: common/forms.py:131
+msgid ""
+"User attr map present how to map LDAP user attr to jumpserver, username,name,"
+"email is jumpserver attr"
+msgstr ""
+"用户属性映射代表怎样将LDAP中用户属性映射到jumpserver用户上,username, name,"
+"email 是jumpserver的属性"
+
+#: common/forms.py:138
+msgid "Enable LDAP auth"
+msgstr "启用LDAP认证"
+
+#: common/forms.py:147
+msgid "List sort by"
+msgstr "资产列表排序"
+
+#: common/forms.py:150
+msgid "Heartbeat interval"
+msgstr "心跳间隔"
+
+#: common/forms.py:150 ops/models.py:32
+msgid "Units: seconds"
+msgstr "单位: 秒"
+
+#: common/forms.py:153
+msgid "Password auth"
+msgstr "密码认证"
+
+#: common/forms.py:156
+msgid "Public key auth"
+msgstr "密钥认证"
+
+#: common/forms.py:159 common/templates/common/terminal_setting.html:63
+#: terminal/forms.py:30 terminal/models.py:20
+msgid "Command storage"
+msgstr "命令存储"
+
+#: common/forms.py:160
+msgid ""
+"Set terminal storage setting, `default` is the using as default,You can set "
+"other storage and some terminal using"
+msgstr "设置终端命令存储,default是默认用的存储方式"
+
+#: common/forms.py:165 common/templates/common/terminal_setting.html:81
+#: terminal/forms.py:34 terminal/models.py:21
+msgid "Replay storage"
+msgstr "录像存储"
+
+#: common/forms.py:166
+msgid ""
+"Set replay storage setting, `default` is the using as default,You can set "
+"other storage and some terminal using"
+msgstr "设置终端录像存储,default是默认用的存储方式"
#: common/mixins.py:29
msgid "is discard"
@@ -1375,43 +1227,56 @@ msgstr ""
msgid "discard time"
msgstr ""
-#: common/models.py:26
-msgid "Value"
-msgstr "值"
-
-#: common/models.py:27
+#: common/models.py:29
msgid "Enabled"
msgstr "启用"
#: common/templates/common/basic_setting.html:15
#: common/templates/common/email_setting.html:15
-#: common/templates/common/ldap_setting.html:15 common/views.py:18
+#: common/templates/common/ldap_setting.html:15
+#: common/templates/common/terminal_setting.html:16
+#: common/templates/common/terminal_setting.html:42 common/views.py:21
msgid "Basic setting"
msgstr "基本设置"
#: common/templates/common/basic_setting.html:18
#: common/templates/common/email_setting.html:18
-#: common/templates/common/ldap_setting.html:18 common/views.py:44
+#: common/templates/common/ldap_setting.html:18
+#: common/templates/common/terminal_setting.html:20 common/views.py:47
msgid "Email setting"
msgstr "邮件设置"
#: common/templates/common/basic_setting.html:21
#: common/templates/common/email_setting.html:21
-#: common/templates/common/ldap_setting.html:21 common/views.py:70
+#: common/templates/common/ldap_setting.html:21
+#: common/templates/common/terminal_setting.html:24 common/views.py:73
msgid "LDAP setting"
msgstr "LDAP设置"
-#: common/templates/common/email_setting.html:55
-#: common/templates/common/ldap_setting.html:55
+#: common/templates/common/basic_setting.html:24
+#: common/templates/common/email_setting.html:24
+#: common/templates/common/ldap_setting.html:24
+#: common/templates/common/terminal_setting.html:28 common/views.py:103
+msgid "Terminal setting"
+msgstr "终端设置"
+
+#: common/templates/common/email_setting.html:58
+#: common/templates/common/ldap_setting.html:58
msgid "Test connection"
msgstr "测试连接"
-#: common/views.py:17 common/views.py:43 common/views.py:69
-#: templates/_nav.html:69
+#: common/templates/common/terminal_setting.html:68
+#: common/templates/common/terminal_setting.html:86
+#: users/templates/users/login_log_list.html:50
+msgid "Type"
+msgstr "类型"
+
+#: common/views.py:20 common/views.py:46 common/views.py:72 common/views.py:102
+#: templates/_nav.html:72
msgid "Settings"
msgstr "系统设置"
-#: common/views.py:28 common/views.py:54 common/views.py:82
+#: common/views.py:31 common/views.py:57 common/views.py:85 common/views.py:115
msgid "Update setting successfully, please restart program"
msgstr "更新设置成功, 请手动重启程序"
@@ -1419,10 +1284,6 @@ msgstr "更新设置成功, 请手动重启程序"
msgid "Interval"
msgstr "间隔"
-#: ops/models.py:32
-msgid "Units: seconds"
-msgstr "单位: 秒"
-
#: ops/models.py:33
msgid "Crontab"
msgstr "Crontab"
@@ -1566,7 +1427,7 @@ msgstr "执行历史"
#: ops/templates/ops/adhoc_history.html:52
#: ops/templates/ops/adhoc_history_detail.html:58
-#: ops/templates/ops/task_history.html:55 terminal/models.py:101
+#: ops/templates/ops/task_history.html:55 terminal/models.py:132
#: terminal/templates/terminal/session_list.html:77
msgid "Date start"
msgstr "开始日期"
@@ -1665,6 +1526,10 @@ msgstr "成功"
msgid "Date"
msgstr "日期"
+#: ops/templates/ops/task_list.html:70
+msgid "Run"
+msgstr "执行"
+
#: ops/templates/ops/task_list.html:125
msgid "Task start: "
msgstr "任务开始: "
@@ -1674,7 +1539,7 @@ msgstr "任务开始: "
msgid "Ops"
msgstr "作业中心"
-#: ops/views.py:37
+#: ops/views.py:37 templates/_nav.html:58
msgid "Task list"
msgstr "任务列表"
@@ -1682,59 +1547,27 @@ msgstr "任务列表"
msgid "Task run history"
msgstr "执行历史"
-#: perms/forms.py:16 users/forms.py:147 users/forms.py:152 users/forms.py:164
-#: users/forms.py:195
-msgid "Select users"
-msgstr "选择用户"
-
-#: perms/forms.py:18 perms/models.py:15
-#: perms/templates/perms/asset_permission_create_update.html:36
-#: perms/templates/perms/asset_permission_list.html:26 templates/_nav.html:12
-#: terminal/backends/command/models.py:10 terminal/models.py:92
-#: terminal/templates/terminal/command_list.html:32
-#: terminal/templates/terminal/command_list.html:72
-#: terminal/templates/terminal/session_list.html:33
-#: terminal/templates/terminal/session_list.html:71 users/forms.py:191
-#: users/models/user.py:30 users/templates/users/user_group_detail.html:78
-#: users/views/user.py:337
-msgid "User"
-msgstr "用户"
-
-#: perms/forms.py:31 perms/templates/perms/asset_permission_user.html:116
-msgid "Select user groups"
-msgstr "选择用户组"
-
-#: perms/forms.py:52
-msgid "User or group at least one required"
-msgstr "用户和组至少需要选一个"
-
-#: perms/forms.py:60
-msgid "Asset or group at least one required"
-msgstr "资产或组至少需要选择一个"
-
-#: perms/forms.py:78
-msgid "Asset {} of cluster {} not have [{}] system users, please check \n"
-msgstr "资产 {} 所在集群 {} 不包含系统用户 [{}] 请检查\n"
-
-#: perms/forms.py:87
-msgid ""
-"Asset {}(group {}) of cluster {} not have [{}] system users, please check \n"
-msgstr "资产 {}(组 {}) 所在集群 {} 不包含系统用户 [{}] 请检查\n"
-
-#: perms/models.py:16 perms/templates/perms/asset_permission_list.html:27
-#: templates/_nav.html:13 users/models/user.py:37
+#: perms/forms.py:22 perms/models.py:16 perms/models.py:75
+#: perms/templates/perms/asset_permission_list.html:68 templates/_nav.html:14
+#: users/models/group.py:25 users/models/user.py:37
#: users/templates/users/_select_user_modal.html:16
-#: users/templates/users/user_detail.html:178
+#: users/templates/users/user_detail.html:179
#: users/templates/users/user_list.html:26
msgid "User group"
msgstr "用户组"
-#: perms/models.py:21 perms/templates/perms/asset_permission_detail.html:86
-#: users/models/user.py:49 users/templates/users/user_detail.html:94
+#: perms/models.py:21 perms/models.py:78
+#: perms/templates/perms/asset_permission_detail.html:86
+#: perms/templates/perms/asset_permission_list.html:71 users/models/user.py:49
+#: users/templates/users/user_detail.html:95
#: users/templates/users/user_profile.html:96
msgid "Date expired"
msgstr "失效日期"
+#: perms/models.py:88 templates/_nav.html:33
+msgid "Asset permission"
+msgstr "资产授权"
+
#: perms/templates/perms/asset_permission_asset.html:22
#: perms/templates/perms/asset_permission_detail.html:22
#: perms/templates/perms/asset_permission_user.html:22
@@ -1751,21 +1584,28 @@ msgstr "资产或资产组"
msgid "Add asset to this permission"
msgstr "添加资产"
+#: perms/templates/perms/asset_permission_asset.html:97
+#: perms/templates/perms/asset_permission_detail.html:153
+#: perms/templates/perms/asset_permission_user.html:97
+#: perms/templates/perms/asset_permission_user.html:125
+#: users/templates/users/user_group_detail.html:95
+msgid "Add"
+msgstr "添加"
+
#: perms/templates/perms/asset_permission_asset.html:108
msgid "Add asset group to this permission"
msgstr "添加资产组"
+#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:245
+msgid "Select asset groups"
+msgstr "选择资产组"
+
#: perms/templates/perms/asset_permission_asset.html:125
-#: users/templates/users/user_detail.html:195
+#: users/templates/users/user_detail.html:196
msgid "Join"
msgstr "加入"
-#: perms/templates/perms/asset_permission_create_update.html:17
-msgid "Create asset permission "
-msgstr "创建资产权限"
-
#: perms/templates/perms/asset_permission_detail.html:66
-#: users/templates/users/user_group_list.html:13
msgid "User count"
msgstr "用户数量"
@@ -1785,14 +1625,14 @@ msgstr "资产组数量"
msgid "System user count"
msgstr "系统用户数量"
-#: perms/templates/perms/asset_permission_list.html:16
+#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:248
+msgid "Select system users"
+msgstr "选择系统用户"
+
+#: perms/templates/perms/asset_permission_list.html:58
msgid "Create permission"
msgstr "创建授权规则"
-#: perms/templates/perms/asset_permission_list.html:31
-msgid "Is valid"
-msgstr "有效"
-
#: perms/templates/perms/asset_permission_user.html:35
msgid "User list of "
msgstr "用户列表"
@@ -1810,71 +1650,62 @@ msgstr "选择用户"
msgid "Add user group to asset permission"
msgstr "添加用户组"
-#: perms/views.py:28 perms/views.py:44 perms/views.py:60 perms/views.py:74
-#: perms/views.py:111 perms/views.py:141 templates/_nav.html:30
+#: perms/templates/perms/asset_permission_user.html:116
+msgid "Select user groups"
+msgstr "选择用户组"
+
+#: perms/views.py:23 perms/views.py:47 perms/views.py:67 templates/_nav.html:30
msgid "Perms"
msgstr "权限管理"
-#: perms/views.py:29
+#: perms/views.py:24
msgid "Asset permission list"
msgstr "资产授权列表"
-#: perms/views.py:45
+#: perms/views.py:48
msgid "Create asset permission"
msgstr "创建权限规则"
-#: perms/views.py:61
+#: perms/views.py:68
msgid "Update asset permission"
msgstr "更新资产授权"
-#: perms/views.py:75
-msgid "Asset permission detail"
-msgstr "资产授权详情"
-
-#: perms/views.py:112
-msgid "Asset permission user list"
-msgstr "资产授权包含用户"
-
-#: perms/views.py:142
-msgid "Asset permission asset list"
-msgstr "资产组授权包含资产"
-
-#: templates/_header_bar.html:14
-msgid "Welcome to use Jumpserver system"
-msgstr "欢迎使用Jumpserver开源跳板机系统"
-
#: templates/_header_bar.html:18
-msgid "Help"
-msgstr "帮助"
+msgid "Supports"
+msgstr "商业支持"
-#: templates/_header_bar.html:33 templates/_nav_user.html:9
-#: users/templates/users/_user.html:42
+#: templates/_header_bar.html:23
+msgid "Docs"
+msgstr "文档"
+
+#: templates/_header_bar.html:37 templates/_nav_user.html:9
+#: users/templates/users/_user.html:36
#: users/templates/users/user_password_update.html:37
#: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37
#: users/templates/users/user_profile_update.html:57
-#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:319
+#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:316
msgid "Profile"
msgstr "个人信息"
-#: templates/_header_bar.html:37
+#: templates/_header_bar.html:40
msgid "Admin page"
msgstr "管理页面"
-#: templates/_header_bar.html:39
+#: templates/_header_bar.html:42
msgid "User page"
msgstr "用户页面"
-#: templates/_header_bar.html:42
+#: templates/_header_bar.html:45
msgid "Logout"
msgstr "注销登录"
-#: templates/_header_bar.html:46 users/templates/users/login.html:42
-#: users/templates/users/login.html:61
+#: templates/_header_bar.html:49 users/templates/users/login.html:44
+#: users/templates/users/login.html:64
msgid "Login"
msgstr "登录"
-#: templates/_header_bar.html:59 templates/_nav.html:4
+#: templates/_header_bar.html:62 templates/_nav.html:4
msgid "Dashboard"
msgstr "仪表盘"
@@ -1891,7 +1722,7 @@ msgstr ""
" 补充完整\n"
" "
-#: templates/_message.html:16
+#: templates/_message.html:17
#, python-format
msgid ""
"\n"
@@ -1900,7 +1731,7 @@ msgid ""
" "
msgstr ""
"\n"
-" 您的ssh秘钥没有设置或已失效,请点击 链接 更新\n"
" "
@@ -1908,62 +1739,53 @@ msgstr ""
msgid "Close"
msgstr "关闭"
-#: templates/_nav.html:9 users/views/group.py:28 users/views/group.py:44
-#: users/views/group.py:62 users/views/group.py:79 users/views/login.py:197
-#: users/views/login.py:246 users/views/user.py:57 users/views/user.py:72
-#: users/views/user.py:91 users/views/user.py:147 users/views/user.py:304
-#: users/views/user.py:318 users/views/user.py:355 users/views/user.py:377
+#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44
+#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95
+#: users/views/login.py:209 users/views/login.py:258 users/views/user.py:59
+#: users/views/user.py:74 users/views/user.py:93 users/views/user.py:149
+#: users/views/user.py:304 users/views/user.py:351 users/views/user.py:373
msgid "Users"
msgstr "用户管理"
-#: templates/_nav.html:14
+#: templates/_nav.html:13 users/views/user.py:60
+msgid "User list"
+msgstr "用户列表"
+
+#: templates/_nav.html:15
msgid "Login logs"
msgstr "登录日志"
-#: templates/_nav.html:33
-msgid "Asset permission"
-msgstr "资产授权"
-
#: templates/_nav.html:39
-msgid "Job Center"
-msgstr "作业中心"
+msgid "Sessions"
+msgstr "会话管理"
#: templates/_nav.html:42
-msgid "Task"
-msgstr "任务"
+msgid "Session online"
+msgstr "在线会话"
-#: templates/_nav.html:47 templates/_nav.html:50
-#: terminal/templates/terminal/session_list.html:75
+#: templates/_nav.html:43
+msgid "Session offline"
+msgstr "历史会话"
+
+#: templates/_nav.html:44
+msgid "Commands"
+msgstr "命令记录"
+
+#: templates/_nav.html:47 templates/_nav_user.html:14
+msgid "Web terminal"
+msgstr "Web终端"
+
+#: templates/_nav.html:50 terminal/templates/terminal/session_list.html:75
#: terminal/views/command.py:47 terminal/views/session.py:75
-#: terminal/views/session.py:92 terminal/views/session.py:114
+#: terminal/views/session.py:93 terminal/views/session.py:115
#: terminal/views/terminal.py:31 terminal/views/terminal.py:46
#: terminal/views/terminal.py:58
msgid "Terminal"
msgstr "终端管理"
-#: templates/_nav.html:51
-msgid "Session online"
-msgstr "在线会话"
-
-#: templates/_nav.html:52
-msgid "Session offline"
-msgstr "离线会话"
-
-#: templates/_nav.html:53 terminal/models.py:99
-#: terminal/templates/terminal/command_list.html:55
-#: terminal/templates/terminal/command_list.html:71
-#: terminal/templates/terminal/session_detail.html:48
-#: terminal/templates/terminal/session_list.html:76
-msgid "Command"
-msgstr "命令"
-
-#: templates/_nav_user.html:4
-msgid "My assets"
-msgstr "我的资产"
-
-#: templates/_nav_user.html:14
-msgid "Web terminal"
-msgstr "Web终端"
+#: templates/_nav.html:55
+msgid "Job Center"
+msgstr "作业中心"
#: templates/captcha/image.html:3
msgid "Play CAPTCHA as audio file"
@@ -1987,67 +1809,79 @@ msgstr "输出"
#: terminal/backends/command/models.py:15
#: terminal/templates/terminal/command_list.html:75
+#: terminal/templates/terminal/terminal_list.html:33
msgid "Session"
msgstr "会话"
-#: terminal/forms.py:15
+#: terminal/forms.py:44
msgid "Coco ssh listen port"
msgstr "SSH 监听端口"
-#: terminal/forms.py:16
+#: terminal/forms.py:45
msgid "Coco http/ws listen port"
msgstr "Http/Websocket 监听端口"
-#: terminal/models.py:15
+#: terminal/models.py:17
msgid "Remote Address"
msgstr "远端地址"
-#: terminal/models.py:16
+#: terminal/models.py:18
msgid "SSH Port"
msgstr "SSH端口"
-#: terminal/models.py:17
+#: terminal/models.py:19
msgid "HTTP Port"
msgstr "HTTP端口"
-#: terminal/models.py:68
+#: terminal/models.py:98
msgid "Session Online"
msgstr "在线会话"
-#: terminal/models.py:69
+#: terminal/models.py:99
msgid "CPU Usage"
msgstr "CPU使用"
-#: terminal/models.py:70
+#: terminal/models.py:100
msgid "Memory Used"
msgstr "内存使用"
-#: terminal/models.py:71
+#: terminal/models.py:101
msgid "Connections"
msgstr "连接数"
-#: terminal/models.py:72
+#: terminal/models.py:102
msgid "Threads"
msgstr "线程数"
-#: terminal/models.py:73
+#: terminal/models.py:103
msgid "Boot Time"
msgstr "运行时间"
-#: terminal/models.py:96 terminal/templates/terminal/session_list.html:74
+#: terminal/models.py:126 terminal/templates/terminal/session_list.html:74
#: terminal/templates/terminal/terminal_detail.html:47
msgid "Remote addr"
msgstr "远端地址"
-#: terminal/models.py:98 terminal/templates/terminal/session_list.html:100
+#: terminal/models.py:128 terminal/templates/terminal/session_list.html:102
msgid "Replay"
msgstr "回放"
-#: terminal/models.py:102
+#: terminal/models.py:129 terminal/templates/terminal/command_list.html:55
+#: terminal/templates/terminal/command_list.html:71
+#: terminal/templates/terminal/session_detail.html:48
+#: terminal/templates/terminal/session_list.html:76
+msgid "Command"
+msgstr "命令"
+
+#: terminal/models.py:131
+msgid "Date last active"
+msgstr "最后活跃日期"
+
+#: terminal/models.py:133
msgid "Date end"
msgstr "结束日期"
-#: terminal/models.py:119
+#: terminal/models.py:150
msgid "Args"
msgstr "参数"
@@ -2056,7 +1890,7 @@ msgid "Goto"
msgstr "转到"
#: terminal/templates/terminal/session_detail.html:17
-#: terminal/views/session.py:115
+#: terminal/views/session.py:116
msgid "Session detail"
msgstr "会话详情"
@@ -2086,19 +1920,23 @@ msgstr "监控"
msgid "Terminate session"
msgstr "终止会话"
-#: terminal/templates/terminal/session_list.html:78
+#: terminal/templates/terminal/session_list.html:79
msgid "Duration"
msgstr "时长"
-#: terminal/templates/terminal/session_list.html:102
+#: terminal/templates/terminal/session_list.html:104
msgid "Monitor"
msgstr "监控"
-#: terminal/templates/terminal/session_list.html:103
+#: terminal/templates/terminal/session_list.html:105
msgid "Terminate"
msgstr "终断"
-#: terminal/templates/terminal/session_list.html:119
+#: terminal/templates/terminal/session_list.html:116
+msgid "Terminate selected"
+msgstr "终断所选"
+
+#: terminal/templates/terminal/session_list.html:136
msgid "Terminate task send, waiting ..."
msgstr "终断任务已发送,请等待"
@@ -2121,9 +1959,9 @@ msgstr "HTTP端口"
msgid "Addr"
msgstr "地址"
-#: terminal/templates/terminal/terminal_list.html:33
-msgid "Sessions"
-msgstr "会话"
+#: terminal/templates/terminal/terminal_list.html:35
+msgid "Alive"
+msgstr "在线"
#: terminal/templates/terminal/terminal_list.html:76
msgid "Accept"
@@ -2145,7 +1983,7 @@ msgstr "信息"
msgid "Session online list"
msgstr "在线会话"
-#: terminal/views/session.py:93
+#: terminal/views/session.py:94
msgid "Session offline list"
msgstr "离线会话"
@@ -2221,7 +2059,7 @@ msgstr ""
msgid "Invalid token or cache refreshed."
msgstr ""
-#: users/forms.py:43 users/templates/users/user_detail.html:186
+#: users/forms.py:43 users/templates/users/user_detail.html:187
msgid "Join user groups"
msgstr "添加到用户组"
@@ -2265,31 +2103,35 @@ msgstr "不能和原来的密钥相同"
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
-#: users/models/authentication.py:35
+#: users/forms.py:147 users/forms.py:152 users/forms.py:164 users/forms.py:194
+msgid "Select users"
+msgstr "选择用户"
+
+#: users/models/authentication.py:36
msgid "Private Token"
msgstr "ssh密钥"
-#: users/models/authentication.py:45
+#: users/models/authentication.py:46
msgid "Login type"
msgstr "登录方式"
-#: users/models/authentication.py:46
+#: users/models/authentication.py:47
msgid "Login ip"
msgstr "登录IP"
-#: users/models/authentication.py:47
+#: users/models/authentication.py:48
msgid "Login city"
msgstr "登录城市"
-#: users/models/authentication.py:48
+#: users/models/authentication.py:49
msgid "User agent"
msgstr "Agent"
-#: users/models/authentication.py:49
+#: users/models/authentication.py:50
msgid "Date login"
msgstr "登录日期"
-#: users/models/user.py:29 users/models/user.py:254
+#: users/models/user.py:29 users/models/user.py:262
msgid "Administrator"
msgstr "管理员"
@@ -2297,13 +2139,13 @@ msgstr "管理员"
msgid "Application"
msgstr "应用程序"
-#: users/models/user.py:36 users/templates/users/user_detail.html:70
+#: users/models/user.py:36 users/templates/users/user_detail.html:71
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
#: users/models/user.py:38 users/templates/users/_select_user_modal.html:15
-#: users/templates/users/user_detail.html:86
+#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
#: users/templates/users/user_profile.html:55
msgid "Role"
@@ -2313,7 +2155,7 @@ msgstr "角色"
msgid "Avatar"
msgstr "头像"
-#: users/models/user.py:40 users/templates/users/user_detail.html:81
+#: users/models/user.py:40 users/templates/users/user_detail.html:82
msgid "Wechat"
msgstr "微信"
@@ -2328,7 +2170,7 @@ msgstr "二次验证"
msgid "Public key"
msgstr "ssh公钥"
-#: users/models/user.py:257
+#: users/models/user.py:265
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
@@ -2336,6 +2178,10 @@ msgstr "Administrator是初始的超级管理员"
msgid "Please Select User"
msgstr "选择用户"
+#: users/templates/users/_select_user_modal.html:17
+msgid "Asset num"
+msgstr "资产数量"
+
#: users/templates/users/_user.html:13
#: users/templates/users/user_profile_update.html:51
msgid "Account"
@@ -2351,7 +2197,7 @@ msgstr "导入"
#: users/templates/users/_user_import_modal.html:6
msgid "Download template or use export csv format"
-msgstr "下载模板"
+msgstr "下载模板或使用导出的csv格式"
#: users/templates/users/_user_import_modal.html:14
msgid "Users csv file"
@@ -2395,15 +2241,15 @@ msgid " for more information"
msgstr "获取更多信息"
#: users/templates/users/forgot_password.html:26
-#: users/templates/users/login.html:64
+#: users/templates/users/login.html:73
msgid "Forgot password"
msgstr "忘记密码"
#: users/templates/users/forgot_password.html:33
msgid "Input your email, that will send a mail to your"
-msgstr "输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中"
+msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中"
-#: users/templates/users/login.html:47
+#: users/templates/users/login.html:50
msgid "Captcha invalid"
msgstr "验证码错误"
@@ -2416,8 +2262,8 @@ msgid "City"
msgstr "城市"
#: users/templates/users/reset_password.html:45
-#: users/templates/users/user_detail.html:324
-#: users/templates/users/user_profile.html:136 users/utils.py:68
+#: users/templates/users/user_detail.html:325
+#: users/templates/users/user_profile.html:132 users/utils.py:71
msgid "Reset password"
msgstr "重置密码"
@@ -2425,8 +2271,13 @@ msgstr "重置密码"
msgid "Password again"
msgstr "再次输入密码"
+#: users/templates/users/reset_password.html:57
+#: users/templates/users/user_profile.html:20
+msgid "Setting"
+msgstr "设置"
+
#: users/templates/users/user_create.html:4
-#: users/templates/users/user_list.html:16 users/views/user.py:72
+#: users/templates/users/user_list.html:16 users/views/user.py:74
msgid "Create user"
msgstr "创建用户"
@@ -2435,113 +2286,102 @@ msgid "Reset link will be generated and sent to the user. "
msgstr "生成重置密码连接,通过邮件发送给用户"
#: users/templates/users/user_detail.html:19
-#: users/templates/users/user_granted_asset.html:18
-#: users/templates/users/user_group_granted_asset.html:18
-#: users/views/user.py:148
+#: users/templates/users/user_granted_asset.html:18 users/views/user.py:150
msgid "User detail"
msgstr "用户详情"
#: users/templates/users/user_detail.html:22
#: users/templates/users/user_granted_asset.html:21
+#: users/templates/users/user_group_detail.html:25
#: users/templates/users/user_group_granted_asset.html:21
msgid "Asset granted"
msgstr "授权的资产"
-#: users/templates/users/user_detail.html:106
+#: users/templates/users/user_detail.html:107
#: users/templates/users/user_profile.html:92
msgid "Last login"
msgstr "最后登录"
-#: users/templates/users/user_detail.html:156
+#: users/templates/users/user_detail.html:157
msgid "Send reset password mail"
msgstr "发送重置密码邮件"
-#: users/templates/users/user_detail.html:159
-#: users/templates/users/user_detail.html:167
+#: users/templates/users/user_detail.html:160
+#: users/templates/users/user_detail.html:168
msgid "Send"
msgstr "发送"
-#: users/templates/users/user_detail.html:164
+#: users/templates/users/user_detail.html:165
msgid "Send reset ssh key mail"
msgstr "发送重置密钥邮件"
-#: users/templates/users/user_detail.html:323
+#: users/templates/users/user_detail.html:324
msgid "An e-mail has been sent to the user\\'s mailbox."
msgstr "已发送邮件到用户邮箱"
-#: users/templates/users/user_detail.html:334
-msgid ""
-"This will reset the user's password. A password-reset email will be sent to "
-"the user\\'s mailbox."
-msgstr "重设密码邮件将会发送到用户邮箱"
+#: users/templates/users/user_detail.html:335
+msgid "This will reset the user password and send a reset mail"
+msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
-#: users/templates/users/user_detail.html:348
+#: users/templates/users/user_detail.html:349
msgid ""
"The reset-ssh-public-key E-mail has been sent successfully. Please inform "
"the user to update his new ssh public key."
-msgstr "重设秘钥邮件将会发送到用户邮箱"
+msgstr "重设密钥邮件将会发送到用户邮箱"
-#: users/templates/users/user_detail.html:349
-#: users/templates/users/user_profile.html:144
+#: users/templates/users/user_detail.html:350
+#: users/templates/users/user_profile.html:140
msgid "Reset SSH public key"
msgstr "重置SSH密钥"
-#: users/templates/users/user_detail.html:359
-msgid "This will reset the user\\"
-msgstr "重置"
+#: users/templates/users/user_detail.html:360
+msgid "This will reset the user public key and send a reset mail"
+msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱"
-#: users/templates/users/user_detail.html:376
-#: users/templates/users/user_profile.html:170
+#: users/templates/users/user_detail.html:377
+#: users/templates/users/user_profile.html:166
msgid "Successfully updated the SSH public key."
msgstr "更新ssh密钥成功"
-#: users/templates/users/user_detail.html:377
-#: users/templates/users/user_detail.html:381
-#: users/templates/users/user_profile.html:171
-#: users/templates/users/user_profile.html:176
+#: users/templates/users/user_detail.html:378
+#: users/templates/users/user_detail.html:382
+#: users/templates/users/user_profile.html:167
+#: users/templates/users/user_profile.html:172
msgid "User SSH public key update"
msgstr "ssh密钥"
-#: users/templates/users/user_granted_asset.html:29
-#: users/templates/users/user_group_granted_asset.html:29
-msgid "Assets granted of "
-msgstr "授权资产"
-
-#: users/templates/users/user_granted_asset.html:62
-#: users/templates/users/user_group_granted_asset.html:65
-msgid "Asset groups granted of "
-msgstr "授权资产组"
-
#: users/templates/users/user_group_create_update.html:31
msgid "Cancel"
msgstr "取消"
-#: users/templates/users/user_group_detail.html:22 users/views/group.py:80
+#: users/templates/users/user_group_detail.html:22
+#: users/templates/users/user_group_granted_asset.html:18
+#: users/views/group.py:80
msgid "User group detail"
-msgstr "资产组详情"
+msgstr "用户组详情"
#: users/templates/users/user_group_detail.html:86
msgid "Add user"
msgstr "添加用户"
-#: users/templates/users/user_group_granted_asset.html:53
-msgid "Valid"
-msgstr "可用"
-
#: users/templates/users/user_group_list.html:5 users/views/group.py:45
msgid "Create user group"
msgstr "创建用户组"
-#: users/templates/users/user_group_list.html:86
+#: users/templates/users/user_group_list.html:82
+msgid "This will delete the selected groups !!!"
+msgstr "删除选择组"
+
+#: users/templates/users/user_group_list.html:90
msgid "UserGroups Deleted."
msgstr "用户组删除"
-#: users/templates/users/user_group_list.html:87
-#: users/templates/users/user_group_list.html:92
+#: users/templates/users/user_group_list.html:91
+#: users/templates/users/user_group_list.html:96
msgid "UserGroups Delete"
msgstr "用户组删除"
-#: users/templates/users/user_group_list.html:91
+#: users/templates/users/user_group_list.html:95
msgid "UserGroup Deleting failed."
msgstr "用户组删除失败"
@@ -2566,16 +2406,12 @@ msgstr "用户删除失败"
msgid "OTP"
msgstr ""
-#: users/templates/users/user_profile.html:100 users/views/user.py:177
-#: users/views/user.py:229
+#: users/templates/users/user_profile.html:100 users/views/user.py:179
+#: users/views/user.py:233
msgid "User groups"
msgstr "用户组"
-#: users/templates/users/user_profile.html:114
-msgid "Perm assets"
-msgstr "资产"
-
-#: users/templates/users/user_profile.html:174
+#: users/templates/users/user_profile.html:170
msgid "Failed to update SSH public key."
msgstr "更新密钥失败"
@@ -2591,9 +2427,9 @@ msgstr "指纹"
msgid "Update public key"
msgstr "更新密钥"
-#: users/templates/users/user_update.html:4 users/views/user.py:91
+#: users/templates/users/user_update.html:4 users/views/user.py:93
msgid "Update user"
-msgstr "编辑用户"
+msgstr "更新用户"
#: users/utils.py:35
msgid "Create account successfully"
@@ -2640,7 +2476,7 @@ msgstr ""
" \n"
" "
-#: users/utils.py:70
+#: users/utils.py:73
#, python-format
msgid ""
"\n"
@@ -2684,11 +2520,11 @@ msgstr ""
" \n"
" "
-#: users/utils.py:101
+#: users/utils.py:104
msgid "SSH Key Reset"
msgstr "重置ssh密钥"
-#: users/utils.py:103
+#: users/utils.py:106
#, python-format
msgid ""
"\n"
@@ -2713,17 +2549,17 @@ msgstr ""
" \n"
" "
-#: users/utils.py:136
+#: users/utils.py:139
msgid "User not exist"
msgstr "用户不存在"
-#: users/utils.py:138
+#: users/utils.py:141
msgid "Disabled or expired"
msgstr "禁用或失效"
-#: users/utils.py:151
+#: users/utils.py:154
msgid "Password or SSH public key invalid"
-msgstr "密码或秘钥不合法"
+msgstr "密码或密钥不合法"
#: users/views/group.py:29
msgid "User group list"
@@ -2731,66 +2567,66 @@ msgstr "用户组列表"
#: users/views/group.py:63
msgid "Update user group"
-msgstr "编辑用户组"
+msgstr "更新用户组"
-#: users/views/login.py:54
+#: users/views/group.py:96
+msgid "User group granted asset"
+msgstr "用户组授权资产"
+
+#: users/views/login.py:57
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
-#: users/views/login.py:87
+#: users/views/login.py:99
msgid "Logout success"
msgstr "退出登录成功"
-#: users/views/login.py:88
+#: users/views/login.py:100
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
-#: users/views/login.py:104
+#: users/views/login.py:116
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
-#: users/views/login.py:117
+#: users/views/login.py:129
msgid "Send reset password message"
msgstr "发送重置密码邮件"
-#: users/views/login.py:118
+#: users/views/login.py:130
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
-#: users/views/login.py:132
+#: users/views/login.py:144
msgid "Reset password success"
msgstr "重置密码成功"
-#: users/views/login.py:133
+#: users/views/login.py:145
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
-#: users/views/login.py:150 users/views/login.py:163
+#: users/views/login.py:162 users/views/login.py:175
msgid "Token invalid or expired"
msgstr "Token错误或失效"
-#: users/views/login.py:159
+#: users/views/login.py:171
msgid "Password not same"
msgstr "密码不一致"
-#: users/views/login.py:197
+#: users/views/login.py:209
msgid "First login"
msgstr "首次登陆"
-#: users/views/login.py:247
+#: users/views/login.py:259
msgid "Login log list"
msgstr "登录日志"
-#: users/views/user.py:58
-msgid "User list"
-msgstr "用户列表"
-
-#: users/views/user.py:101
+#: users/views/user.py:103
msgid "Bulk update user success"
msgstr "批量更新用户成功"
-#: users/views/user.py:206
+#: users/views/user.py:208
msgid "Invalid file."
msgstr "文件不合法"
@@ -2798,17 +2634,17 @@ msgstr "文件不合法"
msgid "User granted assets"
msgstr "用户授权资产"
-#: users/views/user.py:338
+#: users/views/user.py:334
msgid "Profile setting"
msgstr "个人信息设置"
-#: users/views/user.py:356
+#: users/views/user.py:352
msgid "Password update"
msgstr "密码更新"
-#: users/views/user.py:378
+#: users/views/user.py:374
msgid "Public key update"
-msgstr "秘钥更新"
+msgstr "密钥更新"
-#~ msgid "Connect"
-#~ msgstr "连接"
+#~ msgid "Create asset permission "
+#~ msgstr "创建资产权限"
diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py
index 7a8aa422e..04fe210b2 100644
--- a/apps/jumpserver/settings.py
+++ b/apps/jumpserver/settings.py
@@ -17,7 +17,6 @@ import ldap
from django_auth_ldap.config import LDAPSearch
from django.urls import reverse_lazy
-
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(BASE_DIR)
@@ -39,11 +38,9 @@ SECRET_KEY = CONFIG.SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = CONFIG.DEBUG or False
-
# Absolute url for some case, for example email link
SITE_URL = CONFIG.SITE_URL or 'http://localhost'
-
# LOG LEVEL
LOG_LEVEL = 'DEBUG' if DEBUG else CONFIG.LOG_LEVEL or 'WARNING'
@@ -114,7 +111,7 @@ LOGIN_URL = reverse_lazy('users:login')
SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN or None
CSRF_COOKIE_DOMAIN = CONFIG.CSRF_COOKIE_DOMAIN or None
-SESSION_COOKIE_AGE = CONFIG.SESSION_COOKIE_AGE or 3600*24
+SESSION_COOKIE_AGE = CONFIG.SESSION_COOKIE_AGE or 3600 * 24
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
# Database
@@ -241,7 +238,7 @@ USE_L10N = True
USE_TZ = True
# I18N translation
-LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale'), ]
+LOCALE_PATHS = [os.path.join(BASE_DIR, 'i18n'), ]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
@@ -250,7 +247,6 @@ STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(PROJECT_DIR, "data", "static")
STATIC_DIR = os.path.join(BASE_DIR, "static")
-
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
@@ -265,7 +261,7 @@ MEDIA_ROOT = os.path.join(PROJECT_DIR, 'data', 'media').replace('\\', '/') + '/'
# BOOTSTRAP_COLUMN_COUNT = 11
# 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_HOST = CONFIG.EMAIL_HOST
@@ -298,7 +294,7 @@ REST_FRAMEWORK = {
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S %z',
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S %z'],
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
- 'PAGE_SIZE': 15
+ # 'PAGE_SIZE': 15
}
AUTHENTICATION_BACKENDS = [
@@ -308,7 +304,6 @@ AUTHENTICATION_BACKENDS = [
# Custom User Auth model
AUTH_USER_MODEL = 'users.User'
-
# Auth LDAP settings
AUTH_LDAP = CONFIG.AUTH_LDAP
AUTH_LDAP_SERVER_URI = CONFIG.AUTH_LDAP_SERVER_URI
@@ -319,7 +314,7 @@ AUTH_LDAP_SEARCH_FILTER = CONFIG.AUTH_LDAP_SEARCH_FILTER
AUTH_LDAP_START_TLS = CONFIG.AUTH_LDAP_START_TLS
AUTH_LDAP_USER_ATTR_MAP = CONFIG.AUTH_LDAP_USER_ATTR_MAP
AUTH_LDAP_USER_SEARCH = LDAPSearch(
- AUTH_LDAP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER,
+ AUTH_LDAP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER,
)
AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU
AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER
@@ -332,7 +327,6 @@ AUTH_LDAP_BACKEND = 'django_auth_ldap.backend.LDAPBackend'
if AUTH_LDAP:
AUTHENTICATION_BACKENDS.insert(0, AUTH_LDAP_BACKEND)
-
# Celery using redis as broker
CELERY_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % {
'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '',
@@ -354,7 +348,6 @@ CELERY_REDIRECT_STDOUTS = True
CELERY_REDIRECT_STDOUTS_LEVEL = "INFO"
CELERY_WORKER_HIJACK_ROOT_LOGGER = False
-
# Cache use redis
CACHES = {
'default': {
@@ -373,7 +366,25 @@ CAPTCHA_FOREGROUND_COLOR = '#001100'
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE
-COMMAND_STORAGE_BACKEND = 'terminal.backends.command.db'
+COMMAND_STORAGE = {
+ 'ENGINE': 'terminal.backends.command.db',
+}
+
+TERMINAL_COMMAND_STORAGE = {
+ "default": {
+ "TYPE": "server",
+ },
+ # 'ali-es': {
+ # 'TYPE': 'elasticsearch',
+ # 'HOSTS': ['http://elastic:changeme@localhost:9200'],
+ # },
+}
+
+TERMINAL_REPLAY_STORAGE = {
+ "default": {
+ "TYPE": "server",
+ },
+}
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3 = {
@@ -386,6 +397,6 @@ BOOTSTRAP3 = {
}
TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION or 3600
-DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE
+DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE or 25
DEFAULT_EXPIRED_YEARS = 70
USER_GUIDE_URL = ""
diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py
index 2eb54d87d..fe8a4417a 100644
--- a/apps/jumpserver/urls.py
+++ b/apps/jumpserver/urls.py
@@ -4,16 +4,16 @@ from __future__ import unicode_literals
from django.conf.urls import url, include
from django.conf import settings
from django.conf.urls.static import static
-from django.views.static import serve as static_serve
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer
-from .views import IndexView
+from .views import IndexView, LunaView
schema_view = get_schema_view(title='Users API', renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer])
urlpatterns = [
url(r'^$', IndexView.as_view(), name='index'),
+ url(r'^luna/', LunaView.as_view(), name='luna-error'),
url(r'^users/', include('users.urls.views_urls', namespace='users')),
url(r'^assets/', include('assets.urls.views_urls', namespace='assets')),
url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py
index 0ddba94bf..c6a2dc4f3 100644
--- a/apps/jumpserver/views.py
+++ b/apps/jumpserver/views.py
@@ -1,4 +1,7 @@
-from django.views.generic import TemplateView
+import datetime
+
+from django.http import HttpResponse
+from django.views.generic import TemplateView, View
from django.utils import timezone
from django.db.models import Count
from django.contrib.auth.mixins import LoginRequiredMixin
@@ -45,15 +48,22 @@ class IndexView(LoginRequiredMixin, TemplateView):
return self.session_week.values('user').distinct().count()
def get_week_login_asset_count(self):
- return self.session_week.values('asset').distinct().count()
+ return self.session_week.count()
+ # return self.session_week.values('asset').distinct().count()
def get_month_day_metrics(self):
month_str = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
return month_str
def get_month_login_metrics(self):
- return [self.session_month.filter(date_start__date=d).count()
- for d in self.session_month_dates]
+ data = []
+ time_min = datetime.datetime.min.time()
+ time_max = datetime.datetime.max.time()
+ for d in self.session_month_dates:
+ ds = datetime.datetime.combine(d, time_min).replace(tzinfo=timezone.get_current_timezone())
+ de = datetime.datetime.combine(d, time_max).replace(tzinfo=timezone.get_current_timezone())
+ data.append(self.session_month.filter(date_start__range=(ds, de)).count())
+ return data
def get_month_active_user_metrics(self):
if self.session_month_dates_archive:
@@ -119,10 +129,18 @@ class IndexView(LoginRequiredMixin, TemplateView):
self.session_week = Session.objects.filter(date_start__gt=week_ago)
self.session_month = Session.objects.filter(date_start__gt=month_ago)
self.session_month_dates = self.session_month.dates('date_start', 'day')
- self.session_month_dates_archive = [
- self.session_month.filter(date_start__date=d)
- for d in self.session_month_dates
- ]
+
+ self.session_month_dates_archive = []
+ time_min = datetime.datetime.min.time()
+ time_max = datetime.datetime.max.time()
+
+ for d in self.session_month_dates:
+ ds = datetime.datetime.combine(d, time_min).replace(
+ tzinfo=timezone.get_current_timezone())
+ de = datetime.datetime.combine(d, time_max).replace(
+ tzinfo=timezone.get_current_timezone())
+ self.session_month_dates_archive.append(
+ self.session_month.filter(date_start__range=(ds, de)))
context = {
'assets_count': self.get_asset_count(),
@@ -149,3 +167,12 @@ class IndexView(LoginRequiredMixin, TemplateView):
kwargs.update(context)
return super(IndexView, self).get_context_data(**kwargs)
+
+
+class LunaView(View):
+ def get(self, request):
+ msg = """
+ Luna是单独部署的一个程序,你需要部署luna,coco,配置nginx做url分发,
+ 如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运
+ """
+ return HttpResponse(msg)
\ No newline at end of file
diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo
deleted file mode 100644
index 7be8a996a..000000000
Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/ops/utils.py b/apps/ops/utils.py
index 8ff9c321f..55862dd44 100644
--- a/apps/ops/utils.py
+++ b/apps/ops/utils.py
@@ -43,7 +43,7 @@ def update_or_create_ansible_task(
new_adhoc.become = become_info
if not adhoc or adhoc != new_adhoc:
- logger.debug("Task create new adhoc: {}".format(task_name))
+ print("Task create new adhoc: {}".format(task_name))
new_adhoc.save()
task.latest_adhoc = new_adhoc
created = True
diff --git a/apps/perms/api.py b/apps/perms/api.py
index 9165a9098..6b0b15f76 100644
--- a/apps/perms/api.py
+++ b/apps/perms/api.py
@@ -3,17 +3,14 @@
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView, Response
-from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveUpdateAPIView
+from rest_framework.generics import ListAPIView, get_object_or_404
from rest_framework import viewsets
-from common.utils import get_object_or_none
-from users.permissions import IsValidUser, IsSuperUser, IsAppUser, IsSuperUserOrAppUser
-from .utils import get_user_granted_assets, get_user_granted_asset_groups, \
- get_user_asset_permissions, get_user_group_asset_permissions, \
- get_user_group_granted_assets, get_user_group_granted_asset_groups
-from .models import AssetPermission
-from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, \
- AssetGroup, AssetGroupGrantedSerializer, SystemUser, MyAssetGroupGrantedSerializer
+from users.permissions import IsValidUser, IsSuperUser, IsSuperUserOrAppUser
+from .utils import NodePermissionUtil
+from .models import NodePermission
+from .hands import AssetGrantedSerializer, User, UserGroup, Asset, \
+ NodeGrantedSerializer, SystemUser, NodeSerializer
from . import serializers
@@ -21,102 +18,23 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
"""
资产授权列表的增删改查api
"""
- queryset = AssetPermission.objects.all()
- serializer_class = serializers.AssetPermissionSerializer
+ queryset = NodePermission.objects.all()
+ serializer_class = serializers.AssetPermissionCreateUpdateSerializer
permission_classes = (IsSuperUser,)
- def get_queryset(self):
- queryset = super(AssetPermissionViewSet, self).get_queryset()
- user_id = self.request.query_params.get('user', '')
- user_group_id = self.request.query_params.get('user_group', '')
-
- if user_id and user_id.isdigit():
- user = get_object_or_404(User, id=int(user_id))
- queryset = get_user_asset_permissions(user)
-
- if user_group_id:
- user_group = get_object_or_404(UserGroup, id=user_group_id)
- queryset = get_user_group_asset_permissions(user_group)
- return queryset
-
def get_serializer_class(self):
- if getattr(self, 'user_id', ''):
- return serializers.UserAssetPermissionSerializer
- return serializers.AssetPermissionSerializer
+ if self.action in ("list", 'retrieve'):
+ return serializers.AssetPermissionListSerializer
+ return self.serializer_class
+ def get_queryset(self):
+ queryset = super().get_queryset()
+ node_id = self.request.query_params.get('node_id')
-class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
- """
- 将用户从授权中移除,Detail页面会调用
- """
- permission_classes = (IsSuperUser,)
- serializer_class = serializers.AssetPermissionUpdateUserSerializer
- queryset = AssetPermission.objects.all()
+ if node_id:
+ queryset = queryset.filter(node__id=node_id)
- def update(self, request, *args, **kwargs):
- perm = self.get_object()
- serializer = self.serializer_class(data=request.data)
- if serializer.is_valid():
- users = serializer.validated_data.get('users')
- if users:
- perm.users.remove(*tuple(users))
- return Response({"msg": "ok"})
- else:
- return Response({"error": serializer.errors})
-
-
-class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
- permission_classes = (IsSuperUser,)
- serializer_class = serializers.AssetPermissionUpdateUserSerializer
- queryset = AssetPermission.objects.all()
-
- def update(self, request, *args, **kwargs):
- perm = self.get_object()
- serializer = self.serializer_class(data=request.data)
- if serializer.is_valid():
- users = serializer.validated_data.get('users')
- if users:
- perm.users.add(*tuple(users))
- return Response({"msg": "ok"})
- else:
- return Response({"error": serializer.errors})
-
-
-class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
- """
- 将用户从授权中移除,Detail页面会调用
- """
- permission_classes = (IsSuperUser,)
- serializer_class = serializers.AssetPermissionUpdateAssetSerializer
- queryset = AssetPermission.objects.all()
-
- def update(self, request, *args, **kwargs):
- perm = self.get_object()
- serializer = self.serializer_class(data=request.data)
- if serializer.is_valid():
- assets = serializer.validated_data.get('assets')
- if assets:
- perm.assets.remove(*tuple(assets))
- return Response({"msg": "ok"})
- else:
- return Response({"error": serializer.errors})
-
-
-class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
- permission_classes = (IsSuperUser,)
- serializer_class = serializers.AssetPermissionUpdateAssetSerializer
- queryset = AssetPermission.objects.all()
-
- def update(self, request, *args, **kwargs):
- perm = self.get_object()
- serializer = self.serializer_class(data=request.data)
- if serializer.is_valid():
- assets = serializer.validated_data.get('assets')
- if assets:
- perm.assets.add(*tuple(assets))
- return Response({"msg": "ok"})
- else:
- return Response({"error": serializer.errors})
+ return queryset
class UserGrantedAssetsApi(ListAPIView):
@@ -128,41 +46,43 @@ class UserGrantedAssetsApi(ListAPIView):
def get_queryset(self):
user_id = self.kwargs.get('pk', '')
-
queryset = []
+
if user_id:
user = get_object_or_404(User, id=user_id)
- for k, v in get_user_granted_assets(user).items():
- k.system_users_granted = v
- queryset.append(k)
+ else:
+ user = self.request.user
+
+ for k, v in NodePermissionUtil.get_user_assets(user).items():
+ if k.is_unixlike():
+ system_users_granted = [s for s in v if s.protocol == 'ssh']
+ else:
+ system_users_granted = [s for s in v if s.protocol == 'rdp']
+ k.system_users_granted = system_users_granted
+ queryset.append(k)
return queryset
-
-class UserGrantedAssetGroupsApi(APIView):
- permission_classes = (IsValidUser,)
-
- def get(self, request, *args, **kwargs):
- asset_groups = {}
- user_id = kwargs.get('pk', '')
- user = get_object_or_404(User, id=user_id)
-
- assets = get_user_granted_assets(user)
- for asset in assets:
- for asset_group in asset.groups.all():
- if asset_group.id in asset_groups:
- asset_groups[asset_group.id]['assets_amount'] += 1
- else:
- asset_groups[asset_group.id] = {
- 'id': asset_group.id,
- 'name': asset_group.name,
- 'comment': asset_group.comment,
- 'assets_amount': 1
- }
- asset_groups_json = asset_groups.values()
- return Response(asset_groups_json, status=200)
+ def get_permissions(self):
+ if self.kwargs.get('pk') is None:
+ self.permission_classes = (IsValidUser,)
+ return super().get_permissions()
-class UserGrantedAssetGroupsWithAssetsApi(ListAPIView):
+class UserGrantedNodesApi(ListAPIView):
+ permission_classes = (IsSuperUser,)
+ serializer_class = NodeSerializer
+
+ def get_queryset(self):
+ user_id = self.kwargs.get('pk', '')
+ if user_id:
+ user = get_object_or_404(User, id=user_id)
+ else:
+ user = self.request.user
+ nodes = NodePermissionUtil.get_user_nodes(user)
+ return nodes.keys()
+
+
+class UserGrantedNodesWithAssetsApi(ListAPIView):
"""
授权用户的资产组,注:这里的资产组并非是授权列表中授权的,
而是把所有资产取出来,然后反查出所有资产组,然后合并得到,
@@ -171,7 +91,7 @@ class UserGrantedAssetGroupsWithAssetsApi(ListAPIView):
[
{
"id": 1,
- "name": "资产组1",
+ "value": "node",
... 其它属性
"assets_granted": [
{
@@ -191,133 +111,35 @@ class UserGrantedAssetGroupsWithAssetsApi(ListAPIView):
]
"""
permission_classes = (IsSuperUserOrAppUser,)
- serializer_class = AssetGroupGrantedSerializer
+ serializer_class = NodeGrantedSerializer
def get_queryset(self):
user_id = self.kwargs.get('pk', '')
+ queryset = []
if not user_id:
- return []
+ user = self.request.user
+ else:
+ user = get_object_or_404(User, id=user_id)
- user = get_object_or_404(User, id=user_id)
- asset_groups = get_user_granted_asset_groups(user)
-
- queryset = []
- for asset_group, assets_system_users in asset_groups.items():
- assets = []
- for asset, system_users in assets_system_users:
- asset.system_users_granted = system_users
- assets.append(asset)
- asset_group.assets_granted = assets
- queryset.append(asset_group)
+ nodes = NodePermissionUtil.get_user_nodes_with_assets(user)
+ assets = {}
+ for k, v in NodePermissionUtil.get_user_assets(user).items():
+ if k.is_unixlike():
+ system_users_granted = [s for s in v if s.protocol == 'ssh']
+ else:
+ system_users_granted = [s for s in v if s.protocol == 'rdp']
+ assets[k] = system_users_granted
+ for node, v in nodes.items():
+ for asset in v['assets']:
+ asset.system_users_granted = assets[asset]
+ node.assets_granted = v['assets']
+ queryset.append(node)
return queryset
-
-class MyGrantedAssetsApi(ListAPIView):
- """
- 用户自己查询授权的资产列表
- """
- permission_classes = (IsValidUser,)
- serializer_class = AssetGrantedSerializer
-
- def get_queryset(self):
- queryset = []
- user = self.request.user
- if user:
- for asset, system_users in get_user_granted_assets(user).items():
- asset.system_users_granted = system_users
- queryset.append(asset)
- return queryset
-
-
-class MyGrantedAssetGroupsApi(APIView):
- """
- 授权的所有资产组,并非是授权列表中的,而是经过计算得来的
- """
- permission_classes = (IsValidUser,)
-
- def get(self, request, *args, **kwargs):
- asset_groups = {}
- user = request.user
-
- if user:
- assets = get_user_granted_assets(user)
- for asset in assets:
- for asset_group in asset.groups.all():
- if asset_group.id in asset_groups:
- asset_groups[asset_group.id]['assets_amount'] += 1
- else:
- asset_groups[asset_group.id] = {
- 'id': asset_group.id,
- 'name': asset_group.name,
- 'comment': asset_group.comment,
- 'assets_amount': 1
- }
- asset_groups_json = asset_groups.values()
- return Response(asset_groups_json, status=200)
-
-
-class MyGrantedAssetGroupsWithAssetsApi(ListAPIView):
- """
- 授权当前用户的资产组,注:这里的资产组并非是授权列表中授权的,
- 而是把所有资产取出来,然后反查出所有资产组,然后合并得到,
- 结果里也包含资产组下授权的资产
- 数据结构如下:
- [
- {
- "id": 1,
- "name": "资产组1",
- ... 其它属性
- "assets_granted": [
- {
- "id": 1,
- "hostname": "testserver",
- "system_users_granted": [
- "id": 1,
- "name": "web",
- "username": "web",
- "protocol": "ssh",
- ]
- }
- ]
- }
- ]
- """
- permission_classes = (IsValidUser,)
- serializer_class = MyAssetGroupGrantedSerializer
-
- def get_queryset(self):
- user = self.request.user
- asset_groups = get_user_granted_asset_groups(user)
-
- queryset = []
- for asset_group, assets_system_users in asset_groups.items():
- assets = []
- for asset, system_users in assets_system_users:
- asset.system_users_granted = system_users
- assets.append(asset)
- asset_group.assets_granted = assets
- queryset.append(asset_group)
- return queryset
-
-
-class MyAssetGroupOfAssetsApi(ListAPIView):
- """授权用户资产组下的资产列表, 非该资产组的所有资产,而是被授权的"""
- permission_classes = (IsValidUser,)
- serializer_class = AssetGrantedSerializer
-
- def get_queryset(self):
- queryset = []
- asset_group_id = self.kwargs.get('pk', -1)
- user = self.request.user
- asset_group = get_object_or_none(AssetGroup, id=asset_group_id)
-
- if user and asset_group:
- assets = get_user_granted_assets(user)
- for asset in asset_group.assets.all():
- if asset in assets:
- asset.system_users_granted = assets[asset]
- queryset.append(asset)
- return queryset
+ def get_permissions(self):
+ if self.kwargs.get('pk') is None:
+ self.permission_classes = (IsValidUser,)
+ return super().get_permissions()
class UserGroupGrantedAssetsApi(ListAPIView):
@@ -326,27 +148,52 @@ class UserGroupGrantedAssetsApi(ListAPIView):
def get_queryset(self):
user_group_id = self.kwargs.get('pk', '')
+ queryset = []
- if user_group_id:
- user_group = get_object_or_404(UserGroup, id=user_group_id)
- queryset = get_user_group_granted_assets(user_group)
- else:
- queryset = []
+ if not user_group_id:
+ return queryset
+
+ user_group = get_object_or_404(UserGroup, id=user_group_id)
+ assets = NodePermissionUtil.get_user_group_assets(user_group)
+ for k, v in assets.items():
+ k.system_users_granted = v
+ queryset.append(k)
return queryset
-class UserGroupGrantedAssetGroupsApi(ListAPIView):
+class UserGroupGrantedNodesApi(ListAPIView):
permission_classes = (IsSuperUser,)
- serializer_class = AssetGroupGrantedSerializer
+ serializer_class = NodeSerializer
+
+ def get_queryset(self):
+ group_id = self.kwargs.get('pk', '')
+ queryset = []
+
+ if group_id:
+ group = get_object_or_404(UserGroup, id=group_id)
+ nodes = NodePermissionUtil.get_user_group_nodes(group)
+ queryset = nodes.keys()
+ return queryset
+
+
+class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
+ permission_classes = (IsSuperUser,)
+ serializer_class = NodeGrantedSerializer
def get_queryset(self):
user_group_id = self.kwargs.get('pk', '')
+ queryset = []
- if user_group_id:
- user_group = get_object_or_404(UserGroup, id=user_group_id)
- queryset = get_user_group_granted_asset_groups(user_group)
- else:
- queryset = []
+ if not user_group_id:
+ return queryset
+
+ user_group = get_object_or_404(UserGroup, id=user_group_id)
+ nodes = NodePermissionUtil.get_user_group_nodes_with_assets(user_group)
+ for node, v in nodes.items():
+ for asset in v['assets']:
+ asset.system_users_granted = v['system_users']
+ node.assets_granted = v['assets']
+ queryset.append(node)
return queryset
@@ -363,7 +210,7 @@ class ValidateUserAssetPermissionView(APIView):
asset = get_object_or_404(Asset, id=asset_id)
system_user = get_object_or_404(SystemUser, id=system_id)
- assets_granted = get_user_granted_assets(user)
+ assets_granted = NodePermissionUtil.get_user_assets(user)
if system_user in assets_granted.get(asset, []):
return Response({'msg': True}, status=200)
else:
diff --git a/apps/perms/apps.py b/apps/perms/apps.py
index d40373e08..216e9c3d7 100644
--- a/apps/perms/apps.py
+++ b/apps/perms/apps.py
@@ -5,3 +5,7 @@ from django.apps import AppConfig
class PermsConfig(AppConfig):
name = 'perms'
+
+ def ready(self):
+ from . import signals_handler
+ return super().ready()
diff --git a/apps/perms/forms.py b/apps/perms/forms.py
index 4e3c0bf72..f84e56693 100644
--- a/apps/perms/forms.py
+++ b/apps/perms/forms.py
@@ -4,94 +4,27 @@ from __future__ import absolute_import, unicode_literals
from django import forms
from django.utils.translation import ugettext_lazy as _
-# from .hands import User, UserGroup, Asset, AssetGroup, SystemUser
-from .models import AssetPermission
-from users.models import User
+from .models import NodePermission
class AssetPermissionForm(forms.ModelForm):
- users = forms.ModelMultipleChoiceField(
- queryset=User.objects.exclude(role=User.ROLE_APP),
- widget=forms.SelectMultiple(
- attrs={'class': 'select2', 'data-placeholder': _('Select users')},
- ),
- label=_("User"),
- required=False,
- )
-
class Meta:
- model = AssetPermission
+ model = NodePermission
fields = [
- 'name', 'users', 'user_groups', 'assets', 'asset_groups',
- 'system_users', 'is_active', 'date_expired', 'comment',
+ 'node', 'user_group', 'system_user', 'is_active',
+ 'date_expired', 'comment',
]
widgets = {
- 'user_groups': forms.SelectMultiple(
- attrs={'class': 'select2',
- 'data-placeholder': _('Select user groups')}),
- 'assets': forms.SelectMultiple(
- attrs={'class': 'select2',
- 'data-placeholder': _('Select assets')}),
- 'asset_groups': forms.SelectMultiple(
- attrs={'class': 'select2',
- 'data-placeholder': _('Select asset groups')}),
- 'system_users': forms.SelectMultiple(
- attrs={'class': 'select2',
- 'data-placeholder': _('Select system users')}),
- }
- help_texts = {
- 'name': '* required',
- 'system_users': '* required',
+ 'node': forms.Select(
+ attrs={'style': 'display:none'}
+ ),
+ 'user_group': forms.Select(
+ attrs={'class': 'select2', 'data-placeholder': _("User group")}
+ ),
+ 'system_user': forms.Select(
+ attrs={'class': 'select2', 'data-placeholder': _('System user')}
+ ),
}
- def clean_user_groups(self):
- users = self.cleaned_data.get('users')
- user_groups = self.cleaned_data.get('user_groups')
-
- if not users and not user_groups:
- raise forms.ValidationError(_("User or group at least one required"))
- return self.cleaned_data["user_groups"]
-
- def clean_asset_groups(self):
- assets = self.cleaned_data.get('assets')
- asset_groups = self.cleaned_data.get('asset_groups')
-
- if not assets and not asset_groups:
- raise forms.ValidationError(_("Asset or group at least one required"))
-
- return self.cleaned_data["asset_groups"]
-
- def clean_system_users(self):
- from assets.utils import check_assets_have_system_user
-
- errors = []
- assets = self.cleaned_data['assets']
- asset_groups = self.cleaned_data.get('asset_groups')
- system_users = self.cleaned_data.get('system_users')
-
- if not asset_groups and not assets:
- return self.cleaned_data.get("system_users")
-
- error_data = check_assets_have_system_user(assets, system_users)
- if error_data:
- for asset, system_users in error_data.items():
- msg = _("Asset {} of cluster {} not have [{}] system users, please check \n")
- error = forms.ValidationError(msg.format(
- asset.hostname,
- asset.cluster.name,
- ", ".join(system_user.name for system_user in system_users)
- ))
- errors.append(error)
-
- for group in asset_groups:
- msg = _("Asset {}(group {}) of cluster {} not have [{}] system users, please check \n")
- assets = group.assets.all()
- error_data = check_assets_have_system_user(assets, system_users)
- for asset, system_users in error_data.items():
- errors.append(msg.format(
- asset.hostname, group.name, asset.cluster.name,
- ", ".join(system_user.name for system_user in system_users)
- ))
- if errors:
- raise forms.ValidationError(errors)
- return self.cleaned_data['system_users']
+ def clean_system_user(self):
+ return self.cleaned_data['system_user']
diff --git a/apps/perms/hands.py b/apps/perms/hands.py
index 54af4f788..22f290069 100644
--- a/apps/perms/hands.py
+++ b/apps/perms/hands.py
@@ -3,8 +3,8 @@
from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup
-from assets.models import Asset, AssetGroup, SystemUser
-from assets.serializers import AssetGrantedSerializer, AssetGroupGrantedSerializer, MyAssetGroupGrantedSerializer
+from assets.models import Asset, AssetGroup, SystemUser, Node
+from assets.serializers import AssetGrantedSerializer, NodeGrantedSerializer, NodeSerializer
diff --git a/apps/perms/models.py b/apps/perms/models.py
index 18e8ab6f2..49825e16d 100644
--- a/apps/perms/models.py
+++ b/apps/perms/models.py
@@ -67,3 +67,22 @@ class AssetPermission(models.Model):
if cluster_remain:
errors[system_user] = cluster_remain
return errors
+
+
+class NodePermission(models.Model):
+ id = models.UUIDField(default=uuid.uuid4, primary_key=True)
+ node = models.ForeignKey('assets.Node', on_delete=models.CASCADE, verbose_name=_("Node"))
+ user_group = models.ForeignKey('users.UserGroup', on_delete=models.CASCADE, verbose_name=_("User group"))
+ system_user = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, verbose_name=_("System user"))
+ is_active = models.BooleanField(default=True, verbose_name=_('Active'))
+ date_expired = models.DateTimeField(default=date_expired_default, verbose_name=_('Date expired'))
+ created_by = models.CharField(max_length=128, blank=True, verbose_name=_('Created by'))
+ date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
+ comment = models.TextField(verbose_name=_('Comment'), blank=True)
+
+ def __str__(self):
+ return "{}:{}:{}".format(self.node.value, self.user_group.name, self.system_user.name)
+
+ class Meta:
+ unique_together = ('node', 'user_group', 'system_user')
+ verbose_name = _("Asset permission")
diff --git a/apps/perms/serializers.py b/apps/perms/serializers.py
index 820a27af6..6decf663b 100644
--- a/apps/perms/serializers.py
+++ b/apps/perms/serializers.py
@@ -4,41 +4,28 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.utils import get_object_or_none
-from .models import AssetPermission
-from .hands import User
+from common.fields import StringIDField
+from .models import AssetPermission, NodePermission
-class AssetPermissionSerializer(serializers.ModelSerializer):
- assets_ = serializers.SerializerMethodField()
- asset_groups_ = serializers.SerializerMethodField()
- users_ = serializers.SerializerMethodField()
- user_groups_ = serializers.SerializerMethodField()
- system_users_ = serializers.SerializerMethodField()
+class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = NodePermission
+ fields = [
+ 'id', 'node', 'user_group', 'system_user',
+ 'is_active', 'date_expired'
+ ]
+
+
+class AssetPermissionListSerializer(serializers.ModelSerializer):
+ node = StringIDField(read_only=True)
+ user_group = StringIDField(read_only=True)
+ system_user = StringIDField(read_only=True)
class Meta:
- model = AssetPermission
+ model = NodePermission
fields = '__all__'
- @staticmethod
- def get_assets_(obj):
- return [asset.hostname for asset in obj.assets.all()]
-
- @staticmethod
- def get_asset_groups_(obj):
- return [group.name for group in obj.asset_groups.all()]
-
- @staticmethod
- def get_users_(obj):
- return [user.username for user in obj.users.all()]
-
- @staticmethod
- def get_user_groups_(obj):
- return [group.name for group in obj.user_groups.all()]
-
- @staticmethod
- def get_system_users_(obj):
- return [user.username for user in obj.system_users.all()]
-
class AssetPermissionUpdateUserSerializer(serializers.ModelSerializer):
@@ -54,7 +41,7 @@ class AssetPermissionUpdateAssetSerializer(serializers.ModelSerializer):
fields = ['id', 'assets']
-class UserAssetPermissionSerializer(AssetPermissionSerializer):
+class UserAssetPermissionCreateUpdateSerializer(AssetPermissionCreateUpdateSerializer):
is_inherited = serializers.SerializerMethodField()
@staticmethod
diff --git a/apps/perms/signals_handler.py b/apps/perms/signals_handler.py
new file mode 100644
index 000000000..ab16a85e7
--- /dev/null
+++ b/apps/perms/signals_handler.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+#
+
+from django.db.models.signals import post_save, post_delete
+from django.dispatch import receiver
+
+from common.utils import get_logger
+from .models import NodePermission
+
+
+logger = get_logger(__file__)
+
+
+@receiver(post_save, sender=NodePermission, dispatch_uid="my_unique_identifier")
+def on_asset_permission_create_or_update(sender, instance=None, **kwargs):
+ if instance and instance.node and instance.system_user:
+ instance.system_user.nodes.add(instance.node)
+
diff --git a/apps/perms/tasks.py b/apps/perms/tasks.py
index 4b66b5727..b8696e7c8 100644
--- a/apps/perms/tasks.py
+++ b/apps/perms/tasks.py
@@ -7,7 +7,4 @@ from common.utils import get_logger, encrypt_password
logger = get_logger(__file__)
-@shared_task(bind=True)
-def push_users(self, assets, users):
- pass
diff --git a/apps/perms/templates/perms/asset_permission_asset.html b/apps/perms/templates/perms/asset_permission_asset.html
index 5cfc44c05..12369574d 100644
--- a/apps/perms/templates/perms/asset_permission_asset.html
+++ b/apps/perms/templates/perms/asset_permission_asset.html
@@ -191,7 +191,7 @@ function updateGroup(groups) {
}
jumpserver.assets_selected = {};
-jumpserver.groups_selected = {};
+jumpserver.nodes_selected = {};
$(document).ready(function () {
$('.select2.asset').select2()
@@ -206,11 +206,11 @@ $(document).ready(function () {
$('.select2.group').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
- jumpserver.groups_selected[data.id] = data.text;
+ jumpserver.nodes_selected[data.id] = data.text;
})
.on('select2:unselect', function(evt) {
var data = evt.params.data;
- delete jumpserver.groups_selected[data.id]
+ delete jumpserver.nodes_selected[data.id]
})
})
.on('click', '.btn-add-assets', function () {
@@ -232,7 +232,7 @@ $(document).ready(function () {
removeAssets(assets)
})
.on('click', '#btn-add-group', function () {
- if (Object.keys(jumpserver.groups_selected).length === 0) {
+ if (Object.keys(jumpserver.nodes_selected).length === 0) {
return false;
}
@@ -240,7 +240,7 @@ $(document).ready(function () {
return $(this).data('gid');
}).get();
- $.map(jumpserver.groups_selected, function(group_name, index) {
+ $.map(jumpserver.nodes_selected, function(group_name, index) {
groups.push(index);
$('#opt_' + index).remove();
$('.group_edit tbody').append(
diff --git a/apps/perms/templates/perms/asset_permission_create_update.html b/apps/perms/templates/perms/asset_permission_create_update.html
index 85883a77b..d02b354f5 100644
--- a/apps/perms/templates/perms/asset_permission_create_update.html
+++ b/apps/perms/templates/perms/asset_permission_create_update.html
@@ -14,7 +14,7 @@
- {% trans 'Create asset permission ' %}
+ {{ action }}
+ {% if form.non_field_errors %}
+
+ {{ form.non_field_errors }}
+
+ {% endif %}
-
+
{% bootstrap_field form.comment layout="horizontal" %}
@@ -23,7 +23,7 @@
@@ -36,7 +36,7 @@
- Online user
+ Online users
@@ -57,7 +57,7 @@
-
- Copyright Jumpserver.org
-
-
- © 2014-2017
+
+ {% include '_copyright.html' %}
diff --git a/apps/users/templates/users/login.html b/apps/users/templates/users/login.html
index 284b6bc43..13276f6fe 100644
--- a/apps/users/templates/users/login.html
+++ b/apps/users/templates/users/login.html
@@ -22,24 +22,27 @@
- 欢迎使用Jumpserver开源跳板机
+ 欢迎使用Jumpserver开源堡垒机
- Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理
+ 全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计系统。
- 我们自五湖四海,我们对开源精神无比敬仰和崇拜,我们对完美、整洁、优雅 无止境的追求
+ 使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
- 专注自动化运维,努力打造 易用、稳定、安全、自动化 的跳板机, 这是我们的不懈的追求和动力
+ 采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发访问限制。
- 永远年轻,永远热泪盈眶 stay foolish stay hungry
+ 改变世界,从一点点开始。
- {% trans 'Login' %}
+
+
+ {% trans 'Login' %}
+
+ {% if demo_mode %}
+
+ Demo账号: admin 密码: admin
+
+ {% endif %}
+
{% trans 'Forgot password' %}?
-
-
@@ -74,11 +81,8 @@
-
- Copyright Jumpserver.org
-
-
- © 2014-2017
+
+ {% include '_copyright.html' %}
diff --git a/apps/users/templates/users/reset_password.html b/apps/users/templates/users/reset_password.html
index cf8003a86..0bab32809 100644
--- a/apps/users/templates/users/reset_password.html
+++ b/apps/users/templates/users/reset_password.html
@@ -70,11 +70,8 @@
-
- Copyright Jumpserver.org
-
-
- © 2014-2017
+
+ {% include '_copyright.html' %}
diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html
index 78c5ac542..573276bcb 100644
--- a/apps/users/templates/users/user_detail.html
+++ b/apps/users/templates/users/user_detail.html
@@ -24,8 +24,9 @@
{% trans 'Update' %}
+
-
+
{% trans 'Delete' %}
@@ -128,7 +129,7 @@
-
+
|
-
- {% trans 'Perm assets' %} |
- {{ assets | length }} |
-
{% trans 'Comment' %}: |
{{ user.comment }} |
diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py
index 99df9c5d8..ce681c146 100644
--- a/apps/users/urls/api_urls.py
+++ b/apps/users/urls/api_urls.py
@@ -17,8 +17,11 @@ router.register(r'v1/groups', api.UserGroupViewSet, 'user-group')
urlpatterns = [
# url(r'', api.UserListView.as_view()),
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
+ url(r'^v1/connection-token/$', api.UserConnectionTokenApi.as_view(), name='connection-token'),
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'),
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
+ url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/password/$',
+ api.ChangeUserPasswordApi.as_view(), name='change-user-password'),
url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/password/reset/$',
api.UserResetPasswordApi.as_view(), name='user-reset-password'),
url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/pubkey/reset/$',
diff --git a/apps/users/utils.py b/apps/users/utils.py
index fd03ad97d..abc4b2018 100644
--- a/apps/users/utils.py
+++ b/apps/users/utils.py
@@ -59,7 +59,10 @@ def send_user_created_mail(user):
'login_url': reverse('users:login', external=True),
}
if settings.DEBUG:
- print(message)
+ try:
+ print(message)
+ except OSError:
+ pass
send_mail_async.delay(subject, message, recipient_list, html_message=message)
diff --git a/apps/users/views/group.py b/apps/users/views/group.py
index 4b11e7901..958c00ccc 100644
--- a/apps/users/views/group.py
+++ b/apps/users/views/group.py
@@ -88,15 +88,12 @@ class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = UserGroup
template_name = 'users/user_group_granted_asset.html'
context_object_name = 'user_group'
-
- def get(self, request, *args, **kwargs):
- self.object = self.get_object(queryset=self.model.objects.all())
- return super().get(request, *args, **kwargs)
+ object = None
def get_context_data(self, **kwargs):
context = {
- 'app': 'User',
- 'action': 'User group granted asset',
+ 'app': _('Users'),
+ 'action': _('User group granted asset'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
diff --git a/apps/users/views/login.py b/apps/users/views/login.py
index 614f321db..0b70238c0 100644
--- a/apps/users/views/login.py
+++ b/apps/users/views/login.py
@@ -1,6 +1,7 @@
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
+import os
from django import forms
from django.shortcuts import render
from django.contrib.auth import login as auth_login, logout as auth_logout
@@ -29,10 +30,12 @@ from ..tasks import write_login_log_async
from .. import forms
-__all__ = ['UserLoginView', 'UserLogoutView',
- 'UserForgotPasswordView', 'UserForgotPasswordSendmailSuccessView',
- 'UserResetPasswordView', 'UserResetPasswordSuccessView',
- 'UserFirstLoginView', 'LoginLogListView']
+__all__ = [
+ 'UserLoginView', 'UserLogoutView',
+ 'UserForgotPasswordView', 'UserForgotPasswordSendmailSuccessView',
+ 'UserResetPasswordView', 'UserResetPasswordSuccessView',
+ 'UserFirstLoginView', 'LoginLogListView'
+]
@method_decorator(sensitive_post_parameters(), name='dispatch')
@@ -54,7 +57,8 @@ class UserLoginView(FormView):
return HttpResponse(_("Please enable cookies and try again."))
auth_login(self.request, form.get_user())
x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
- if x_forwarded_for:
+
+ if x_forwarded_for and x_forwarded_for[0]:
login_ip = x_forwarded_for[0]
else:
login_ip = self.request.META.get('REMOTE_ADDR', '')
@@ -73,6 +77,13 @@ class UserLoginView(FormView):
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name, reverse('index')))
+ def get_context_data(self, **kwargs):
+ context = {
+ 'demo_mode': os.environ.get("DEMO_MODE"),
+ }
+ kwargs.update(context)
+ return super().get_context_data(**kwargs)
+
@method_decorator(never_cache, name='dispatch')
class UserLogoutView(TemplateView):
@@ -80,7 +91,8 @@ class UserLogoutView(TemplateView):
def get(self, request, *args, **kwargs):
auth_logout(request)
- return super().get(request, *args, **kwargs)
+ response = super().get(request, *args, **kwargs)
+ return response
def get_context_data(self, **kwargs):
context = {
@@ -234,7 +246,7 @@ class LoginLogListView(DatetimeSearchMixin, ListView):
if self.user:
queryset = queryset.filter(username=self.user)
if self.keyword:
- queryset = self.queryset.filter(
+ queryset = queryset.filter(
Q(ip__contains=self.keyword) |
Q(city__contains=self.keyword) |
Q(username__contains=self.keyword)
diff --git a/apps/users/views/user.py b/apps/users/views/user.py
index 9110e2887..bf2ecca57 100644
--- a/apps/users/views/user.py
+++ b/apps/users/views/user.py
@@ -6,6 +6,7 @@ import json
import uuid
import csv
import codecs
+import chardet
from io import StringIO
from django.contrib import messages
@@ -20,6 +21,7 @@ from django.utils.translation import ugettext as _
from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic.base import TemplateView
+from django.db import transaction
from django.views.generic.edit import (
CreateView, UpdateView, FormMixin, FormView
)
@@ -33,7 +35,7 @@ from common.utils import get_logger, get_object_or_none, is_uuid
from .. import forms
from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin
-from ..signals import on_user_created
+from ..signals import post_user_create
__all__ = [
@@ -212,8 +214,10 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
# todo: need be patch, method to long
def form_valid(self, form):
- file = form.cleaned_data['file']
- data = file.read().decode('utf-8').strip(codecs.BOM_UTF8.decode('utf-8'))
+ f = form.cleaned_data['file']
+ det_result = chardet.detect(f.read())
+ f.seek(0) # reset file seek index
+ data = f.read().decode(det_result['encoding']).strip(codecs.BOM_UTF8.decode())
csv_file = StringIO(data)
reader = csv.reader(csv_file)
csv_data = [row for row in reader]
@@ -252,15 +256,15 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
else:
continue
user_dict[k] = v
-
- user = get_object_or_none(User, id=id_) if is_uuid(id_) else None
+ user = get_object_or_none(User, id=id_) if id_ and is_uuid(id_) else None
if not user:
try:
- groups = user_dict.pop('groups')
- user = User.objects.create(**user_dict)
- user.groups.set(groups)
- created.append(user_dict['username'])
- on_user_created.send(self.__class__, user=user)
+ with transaction.atomic():
+ groups = user_dict.pop('groups')
+ user = User.objects.create(**user_dict)
+ user.groups.set(groups)
+ created.append(user_dict['username'])
+ post_user_create.send(self.__class__, user=user)
except Exception as e:
failed.append('%s: %s' % (user_dict['username'], str(e)))
else:
@@ -295,10 +299,6 @@ class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
template_name = 'users/user_granted_asset.html'
object = None
- def get(self, request, *args, **kwargs):
- self.object = self.get_object(queryset=User.objects.all())
- return super().get(request, *args, **kwargs)
-
def get_context_data(self, **kwargs):
context = {
'app': _('Users'),
@@ -312,12 +312,8 @@ class UserProfileView(LoginRequiredMixin, TemplateView):
template_name = 'users/user_profile.html'
def get_context_data(self, **kwargs):
- from perms.utils import get_user_granted_assets
- assets = get_user_granted_assets(self.request.user)
context = {
- 'app': _('Users'),
'action': _('Profile'),
- 'assets': assets,
}
kwargs.update(context)
return super().get_context_data(**kwargs)
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 000000000..526c8f5df
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+SPHINXPROJ = Jumpserver
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
diff --git a/docs/_static/img/logo-text.png b/docs/_static/img/logo-text.png
new file mode 100644
index 000000000..cb76b555a
Binary files /dev/null and b/docs/_static/img/logo-text.png differ
diff --git a/docs/_static/img/structure.png b/docs/_static/img/structure.png
new file mode 100644
index 000000000..90476014c
Binary files /dev/null and b/docs/_static/img/structure.png differ
diff --git a/docs/admin_asset.rst b/docs/admin_asset.rst
new file mode 100644
index 000000000..758db8912
--- /dev/null
+++ b/docs/admin_asset.rst
@@ -0,0 +1,2 @@
+资产管理模块
+=============
\ No newline at end of file
diff --git a/docs/admin_guide.rst b/docs/admin_guide.rst
new file mode 100644
index 000000000..df59969cc
--- /dev/null
+++ b/docs/admin_guide.rst
@@ -0,0 +1,10 @@
+管理文档
+=========
+
+这里介绍管理员功能。
+
+.. toctree::
+ :maxdepth: 1
+
+ admin_user
+ admin_asset
diff --git a/docs/admin_user.rst b/docs/admin_user.rst
new file mode 100644
index 000000000..cdce938e1
--- /dev/null
+++ b/docs/admin_user.rst
@@ -0,0 +1,51 @@
+用户管理
+========
+
+这里介绍用户管理模块的功能。
+
+点击页面左侧“用户列表”菜单下的“用户列表,进入用户列表页面。
+
+.. contents:: Topics
+
+.. _create_user:
+
+创建用户
+````````
+
+点击页面左上角“创建用户”按钮,进入创建用户页面,填写账户,角色安全,个人等信息,点击“提交”按钮,用户创建完成。
+
+
+.. _update_user:
+
+更新用户
+````````
+
+点击页面右边的“更新”按钮,进入编辑用户页面,编辑用户信息,点击“提交”按钮,更新用户完成。
+
+.. _delete_user:
+
+删除用户
+````````
+
+点击页面右边的“删除”按钮,弹出是否删除确认框,点击“确定”按钮,删除用户完成。
+
+.. _export_user:
+
+导出用户
+````````
+
+选中用户,点击右上角的“导出”按钮,导出用户完成。
+
+.. _inport_user:
+
+导入用户
+````````
+
+点击右上角的“导入”按钮,弹出导入对话框,选择要导入的CSV格式文件,点击“确认”按钮,导入用户完成。
+
+.. _batch_operation:
+
+批量操作
+````````
+
+选中用户,选择页面左下角的批量操作选项,点击”提交“按钮,批量操作完成。
\ No newline at end of file
diff --git a/docs/api_style_guide.rst b/docs/api_style_guide.rst
new file mode 100644
index 000000000..438a6d94a
--- /dev/null
+++ b/docs/api_style_guide.rst
@@ -0,0 +1,166 @@
+REST API规范约定
+----------------
+
+这里仅考虑REST API的基本情况。参考
+
+`RESTful API 设计指南`_
+
+`github api文档`_
+
+协议
+~~~~
+
+API与用户的通信协议,总是使用HTTPs协议。
+
+域名
+~~~~
+
+这版api相对简单, 没有前后端分离, 没有独立app, 所以放在主域名下
+
+::
+
+ https://example.org/api/
+
+版本
+~~~~
+
+将API的版本号放入URL中, 由于一个项目多个app所以Jumpserver使用以下风格,
+将版本号放到app后面
+
+::
+
+ 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]
+
+路径
+~~~~
+
+路径又称“终点”(endpoint),表示API的具体网址。
+在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的“集合”(collection),所以API中的名词也应该使用复数。
+举例来说 cmdb中的assets列表, idc列表
+
+::
+
+ 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]
+ https://example.com/api/assets/v1.0/idcs [GET, POST]
+
+一般性的增删查改(CRUD)API,完全使用HTTP
+method加上url提供的语义,url中的可变部分(比如上面提到的)
+一般用来传递该API操作的核心实体对象的唯一ID,如果有更多的参数需要提供,GET方法请使用url
+parameter
+(例如:“?client_id=xxxxx&app_id=xxxxxx”),PUT/POST/DELETE方法请使用请求体传递参数。
+
+HTTP Method
+~~~~~~~~~~~
+
+对于资源的具体操作类型,由HTTP动词表示。
+
+常用的HTTP动词有下面五个(括号里是对应的SQL命令)。
+
+- GET(SELECT):从服务器取出资源(一项或多项)。
+- POST(CREATE):在服务器新建一个资源。
+- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源, 幂等
+- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
+- DELETE(DELETE):从服务器删除资源。
+
+.. _RESTful API 设计指南: http://www.ruanyifeng.com/blog/2014/05/restful_api.html
+.. _github api文档: https://developer.github.com/v3/
+
+
+过滤信息
+~~~~~~~~
+
+常见参数约定
+
+::
+
+ ?keyword=localhost 模糊搜索
+ ?limit=10:指定返回记录的数量
+ ?offset=10:指定返回记录的开始位置。
+ ?page=2&per_page=100:指定第几页,以及每页的记录数。
+ ?sort=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
+ ?asset_id=1:指定筛选条件
+
+状态码
+~~~~~~
+
+服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
+
+- 200 OK -
+ [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
+- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
+- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
+- 204 NO CONTENT - [DELETE]:用户删除数据成功。
+- 400 INVALID REQUEST -
+ [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
+- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
+- 403 Forbidden - [*]
+ 表示用户得到授权(与401错误相对),但是访问是被禁止的。
+- 404 NOT FOUND -
+ [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
+- 406 Not Acceptable -
+ [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
+- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
+- 422 Unprocesable entity - [POST/PUT/PATCH]
+ 当创建一个对象时,发生一个验证错误。
+- 500 INTERNAL SERVER ERROR -
+ [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
+
+错误处理
+~~~~~~~~
+
+如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
+
+::
+
+ {
+ error: "Invalid API key"
+ }
+
+
+返回结果
+~~~~~~~~
+
+针对不同操作,服务器向用户返回的结果应该符合以下规范。
+
+::
+
+ GET /collection:返回资源对象的列表(数组)
+ GET /collection/resource:返回单个资源对象
+ POST /collection:返回新生成的资源对象
+ PUT /collection/resource:返回完整的资源对象
+ PATCH /collection/resource:返回完整的资源对象
+ DELETE /collection/resource:返回一个空文档
+
+Hypermedia API
+~~~~~~~~~~~~~~
+
+RESTful
+API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
+比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。
+
+::
+
+ {"link": {
+ "rel": "collection https://www.example.com/zoos",
+ "href": "https://api.example.com/zoos",
+ "title": "List of zoos",
+ "type": "application/vnd.yourformat+json"
+ }}
+
+上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。
+
+rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),
+
+href表示API的路径,title表示API的标题,type表示返回类型。 Hypermedia
+API的设计被称为HATEOAS。 Github的API就是这种设计.
+
+其它
+~~~~
+
+(1)API的身份认证应该使用OAuth 2.0框架。
+(2)服务器返回的数据格式,应该尽量使用JSON
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 000000000..068048c0f
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/stable/config
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+import sphinx_rtd_theme
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'jumpserver'
+copyright = '北京堆栈科技有限公司 © 2014-2018'
+author = 'Jumpserver team'
+
+# The short X.Y version
+version = ''
+# The full version, including alpha/beta/rc tags
+release = '0.5.0'
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.viewcode',
+ 'sphinx.ext.githubpages',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = 'zh_CN'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path .
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+html_show_sourcelink = False
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+# html_theme = 'alabaster'
+html_theme = "sphinx_rtd_theme"
+html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+html_theme_options = {
+ 'logo_only': True,
+ 'display_version': False
+}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself. Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Jumpserver 文档'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'jumpserver.tex', 'jumpserver Documentation',
+ 'Jumpserver team', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'jumpserver', 'jumpserver Documentation',
+ [author], 1)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'jumpserver', 'jumpserver Documentation',
+ author, 'jumpserver', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+
+# -- Extension configuration -------------------------------------------------
+html_logo = '_static/img/logo-text.png'
diff --git a/docs/contact.rst b/docs/contact.rst
new file mode 100644
index 000000000..767554517
--- /dev/null
+++ b/docs/contact.rst
@@ -0,0 +1,33 @@
+联系方式
++++++++++++++++++++++++++
+
+QQ群
+~~~~~~~~
+
+群1: 390139816
+群2: 399218702
+群3: 552054376
+
+
+Github
+~~~~~~~~
+
+https://github.com/jumpserver/jumpserver.git
+
+
+官网
+~~~~~~~~
+
+http://www.jumpserver.org
+
+
+Demo
+~~~~~~~~
+
+http://demo.jumpserver.org:8080
+
+
+邮件
+~~~~~~~~
+
+ibuler#fit2cloud.com (#替换为@)
\ No newline at end of file
diff --git a/docs/contributor.rst b/docs/contributor.rst
new file mode 100644
index 000000000..05c2604f1
--- /dev/null
+++ b/docs/contributor.rst
@@ -0,0 +1,13 @@
+贡献者
+++++++++++++++++++++++++
+
+感谢一下朋友为Jumpserver做出的贡献,世界因你们而不同,排名不分先后
+
+
+- **小彧 <李磊>** Django资深开发者,为用户模块贡献了很多代码
+- **sofia <周小侠>** 资深前端工程师, 前端代码贡献者
+- **liuz <刘正> 全栈工程师** 编写了Web terminal大部分代码
+- **jiaxiangkong <陈尚委>** Jumpserver测试运营
+- **halcyon <王墉>** DevOps 资深开发者, 0.3.2 核心开发者之一
+- **yumaojun03 <喻茂峻>** DevOps 资深开发者,擅长Python, Go以及PAAS平台开发
+- **kelianchun <柯连春>** DevOps 资产开发者,fix了很多bug
\ No newline at end of file
diff --git a/docs/development.rst b/docs/development.rst
new file mode 100644
index 000000000..9e2411ea9
--- /dev/null
+++ b/docs/development.rst
@@ -0,0 +1,12 @@
+开发文档
+======================================
+
+.. toctree::
+ :maxdepth: 1
+ :caption: 开发文档
+
+ api_style_guide
+ python_style_guide
+ project_structure
+
+
diff --git a/docs/faq.rst b/docs/faq.rst
new file mode 100644
index 000000000..d02cca126
--- /dev/null
+++ b/docs/faq.rst
@@ -0,0 +1,2 @@
+FAQ
++++++++++++++++++++++
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 000000000..787f05564
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,29 @@
+.. jumpserver documentation master file, created by
+ sphinx-quickstart on Mon Feb 26 23:28:27 2018.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Jumpserver 文档
+======================================
+
+.. toctree::
+ :maxdepth: 2
+ :caption: 文档:
+
+ intro
+ installation
+ admin_guide
+ user_guide
+ development
+ contributor
+ contact
+ faq
+
+
+
+索引
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/install.md b/docs/install.md
deleted file mode 100644
index e6b240ab1..000000000
--- a/docs/install.md
+++ /dev/null
@@ -1,129 +0,0 @@
-## Jumpserver v0.4.0 版本安装详细过程
-
-### 环境
-
-- 系统: CentOS 6.5 x86\_64 mini
-- Python: 版本 3.6 大部分功能兼容 2.7
-- 安装目录
- - /opt/jumpserver
- - /opt/coco
-
-#### 一. 环境准备
-
-##### 1.1 安装基本工具和库
-
- $ yum -y install sqlite-devel git epel-release
- $ yum -y install sshpass python-devel libffi-devel openssl-devel
- $ yum -y install gcc gcc-c++
-
-
-##### 1.2 安装Python 3.6 和 虚拟环境
-略
-
-
-#### 二. Jumpserver安装
-
-##### 2.1 下载仓库代码
-
- $ cd /opt
- $ git clone https://github.com/jumpserver/jumpserver.git
- $ cd jumpserver
- $ git checkout dev
-
-##### 2.2 安装依赖
-
- $ cd requirements
- $ sudo yum -y install `cat rpm_requirements.txt`
- $ pip install -r requirements.txt -i https://pypi.doubanio.com/simple
-
- // 解决Mac安装ldap提示 Modules/LDAPObject.c:18:10: fatal error: 'sasl.h' file not found
- pip install python-ldap \
- --global-option=build_ext \
- --global-option="-I$(xcrun --show-sdk-path)/usr/include/sasl"
-
-##### 2.3 准备配置文件
-
- $ cd ..
- $ cp config_example.py config.py
- $ vim config.py
-
- // 默认使用的是 DevelpmentConfig 所以应该去修改这部分
- class DevelopmentConfig(Config):
- EMAIL_HOST = 'smtp.exmail.qq.com'
- EMAIL_PORT = 465
- EMAIL_HOST_USER = 'ask@jumpserver.org'
- EMAIL_HOST_PASSWORD = 'xxx'
- EMAIL_USE_SSL = True // 端口是 465 设置 True 否则 False
- EMAIL_USE_TLS = False // 端口是 587 设置为 True 否则 False
- SITE_URL = 'http://localhost:8080' // 发送邮件会使用这个地址
-
-##### 2.4 初始化数据库
-
- $ cd utils
- $ sh make_migrations.sh
- $ sh init_db.sh
-
-##### 2.5 安装redis server
-
- $ yum -y install redis
- $ service redis start
-
-**2.6 启动**
-```
-$ cd ..
-$ python run_server.py
-```
-访问 http://ip:8080
-账号密码: admin admin
-
-**2.7 测试使用**
-- 创建用户
- 会发送邮件,测试是否正常修改密码,登录
-
-- 创建管理用户
- 创建一个管理用户, 创建资产时需要关联
-
-- 创建资产
- 创建一个 资产,关联刚创建的管理用户
-
-- 创建系统用户
- 系统用户是用来登录资产的,授权时需要
-
-- 创建授权规则
- 关联用户,资产,系统用户 形成授权规则,授权的系统用户会自动推送到资产上
-
-
-#### 三. 安装 SSH SERVER - COCO
-**3.1 下载代码库**
-```
-$ cd /opt
-$ git clone https://github.com/jumpserver/coco.git
-```
-
-**3.2 安装依赖**
-```
-$ cd coco
-$ pip install -r requirements.txt # -i https://pypi.doubanio.com/simple
-```
-
-**3.3 启动**
-
-```
-$ python run_server.py
-```
-
-说明: Coco启动后会向jumpserver注册,请去 jumpserver页面 - 应用程序 - terminal - coco - Accept 允许, 这时 coco就 运行在 2222端口,可以ssh来连接
-
-命令行:
-```
-ssh admin@YourServerIP -p2222
-```
-
-**3.5 测试**
-- 测试登录 ssh server
-- 测试跳转
-- 测试命令记录回
-
-[1]: https://segmentfault.com/a/1190000000654227
-[2]: https://github.com/jumpserver/jumpserver.git
-[3]: https://github.com/jumpserver/coco.git
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 000000000..e9dde1f48
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,9 @@
+安装文档
+++++++++++++++++++++++++
+
+.. toctree::
+ :maxdepth: 1
+
+ quickstart
+ step_by_step
+ upgrade
diff --git a/docs/intro.rst b/docs/intro.rst
new file mode 100644
index 000000000..a8810e14f
--- /dev/null
+++ b/docs/intro.rst
@@ -0,0 +1,51 @@
+简介
+============
+
+Jumpserver是混合云下更好用的堡垒机, 分布式架构设计无限扩展,轻松对接混合云资产,支持使用云存储(AWS S3, ES等)存储录像、命令
+
+Jumpserver颠覆传统堡垒机, 无主机和并发数量限制,支持水平扩容,FIT2CLOUD提供完备的商业服务支持,用户无后顾之忧
+
+Jumpserver拥有极致的用户体验, 极致UI体验,容器化的部署方式,部署过程方便快捷,可持续升级
+
+
+组件说明
+++++++++++++++++++++++++
+
+Jumpserver
+```````````
+现指Jumpserver管理后台,是核心组件(Core), 使用 Django Class Based View 风格开发,支持Restful API。
+
+`Github `_
+
+
+Coco
+````````
+实现了SSH Server 和 Web Terminal Server的组件,提供ssh和websocket接口, 使用 Paramiko 和 Flask 开发。
+
+
+`Github `__
+
+
+Luna
+````````
+现在是Web Terminal前端,计划前端页面都由该项目提供,Jumpserver只提供API,不再负责后台渲染html等。
+
+`Github `__
+
+
+Guacamole
+```````````
+Apache 跳板机项目,Jumpserver使用其组件实现RDP功能,Jumpserver并没有修改其代码而是添加了额外的插件,支持Jumpserver调用
+
+
+Jumpserver-python-sdk
+```````````````````````
+Jumpserver API Python SDK,Coco目前使用该SDK与Jumpserver API交互
+
+`Github `__
+
+
+组件架构图
+++++++++++++++++++++++++
+.. image:: _static/img/structure.png
+ :alt: 组件架构图
diff --git a/docs/README.md b/docs/old/README.md
similarity index 100%
rename from docs/README.md
rename to docs/old/README.md
diff --git a/docs/api_style_guide.md b/docs/old/api_style_guide.md
similarity index 100%
rename from docs/api_style_guide.md
rename to docs/old/api_style_guide.md
diff --git a/docs/django_class_base_view_inheritance.py b/docs/old/django_class_base_view_inheritance.py
similarity index 100%
rename from docs/django_class_base_view_inheritance.py
rename to docs/old/django_class_base_view_inheritance.py
diff --git a/docs/old/install.md b/docs/old/install.md
new file mode 100644
index 000000000..1a6892f53
--- /dev/null
+++ b/docs/old/install.md
@@ -0,0 +1 @@
+More see [安装文档](https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7)
diff --git a/docs/project_structure.md b/docs/old/project_structure.md
similarity index 97%
rename from docs/project_structure.md
rename to docs/old/project_structure.md
index d66f043f9..3cc9b8f94 100644
--- a/docs/project_structure.md
+++ b/docs/old/project_structure.md
@@ -45,6 +45,6 @@
│ │ └── wsgi.py
│ ├── manage.py
│ ├── static // 项目静态资源目录
-│ ├── static // 项目多语言目录
+│ ├── i18n // 项目多语言目录
│ └── templates // 项目模板目录
```
diff --git a/docs/python_style_guide.md b/docs/old/python_style_guide.md
similarity index 100%
rename from docs/python_style_guide.md
rename to docs/old/python_style_guide.md
diff --git a/docs/table_design.xml b/docs/old/table_design.xml
similarity index 100%
rename from docs/table_design.xml
rename to docs/old/table_design.xml
diff --git a/docs/project_structure.rst b/docs/project_structure.rst
new file mode 100644
index 000000000..151cda143
--- /dev/null
+++ b/docs/project_structure.rst
@@ -0,0 +1,51 @@
+项目骨架
+--------
+
+说明如下:
+
+::
+
+ .
+ ├── config-example.py // 配置文件样例
+ ├── docs // 所有doc文件放到该目录
+ │ └── README.md
+ ├── LICENSE
+ ├── README.md
+ ├── install // 安装说明
+ ├── logs // 日志目录
+ ├── apps // 管理后台目录,也是各app所在目录
+ │ └── assets // app目录
+ │ │ ├── admin.py
+ │ │ ├── apps.py // 新版本django app设置文件
+ │ │ ├── api.py // api文件
+ │ │ ├── __init__.py // 对外暴露的接口,放到该文件中,方便别的app引用
+ │ │ ├── migrations // models Migrations版本控制目录
+ │ │ │ └── __init__.py
+ │ │ ├── models.py // 数据模型目录
+ │ │ ├── static // app下静态资源目录,如果需要
+ │ │ │ └── assets // 多一层目录,防止资源重名
+ │ │ │ └── some_image.png
+ │ │ ├── templates // app下模板目录
+ │ │ │ └── assets // 多一层目录,防止资源重名
+ │ │ │ └── asset_list.html
+ │ │ ├── templatetags // 模板标签目录
+ │ │ ├── tests.py // 测试用例文件
+ │ │ ├── urls.py // urlconf文件
+ │ │ ├── utils.py // 将views和api可复用的代码放在这里, api和views只是请求和返回不同
+ │ │ └── views.py // views文件
+ │ ├── common
+ │ │ ├── templatetags // 通用template tag
+ │ │ ├── utils.py // 通用的函数方法
+ │ │ └── views.py
+ │ ├── fixtures // 初始化数据目录
+ │ │ ├── init.json // 初始化项目数据库
+ │ │ └── fake.json // 生成大量测试数据
+ │ ├── jumpserver // 项目设置目录
+ │ │ ├── __init__.py
+ │ │ ├── settings.py // 项目设置文件
+ │ │ ├── urls.py // 项目入口urlconf
+ │ │ └── wsgi.py
+ │ ├── manage.py
+ │ ├── static // 项目静态资源目录
+ │ ├── i18n // 项目多语言目录
+ │ └── templates // 项目模板目录
\ No newline at end of file
diff --git a/docs/python_style_guide.rst b/docs/python_style_guide.rst
new file mode 100644
index 000000000..de7bae56f
--- /dev/null
+++ b/docs/python_style_guide.rst
@@ -0,0 +1,216 @@
+Jumpserver 项目规范(Draft)
+============================
+
+语言框架
+--------
+
+1. Python 3.6.1 (当前最新)
+2. Django 1.11 (当前最新)
+3. Flask 0.12 Luna (当前最新)
+4. Paramiko 2.12 Coco (当前最新)
+
+Django规范
+----------
+
+1. 尽量使用Class Base View编程,更少代码
+2. 使用Django Form
+3. 每个url独立命名,不要硬编码,同理static也是
+4. 数据库表名手动指定,不要使用默认
+5. 代码优雅简洁
+6. 注释明确优美
+7. 测试案例尽可能完整
+8. 尽可能利用Django造好的轮子
+
+代码风格
+--------
+
+Python方面大致的风格,我们采用pocoo的\ `Style
+Guidance`_\ ,但是有些细节部分会尽量放开 参考国内翻译
+
+基本的代码布局
+~~~~~~~~~~~~~~
+
+缩进
+^^^^
+
+1. Python严格采用4个空格的缩进,任何python代码都都必须遵守此规定。
+2. web部分代码(HTML, CSS,
+ JavaScript),Node.js采用2空格缩进,同样不使用tab (:raw-latex:`\t`)。
+ 之所以与Python不同,是因为js中有大量回调式的写法,2空格可以显著降低视觉上的负担。
+
+最大行长度
+^^^^^^^^^^
+
+按PEP8规范,Python一般限制最大79个字符,
+但是Django的命名,url等通常比较长,
+而且21世纪都是宽屏了,所以我们限制最大120字符
+
+**补充说明:HTML代码不受此规范约束。**
+
+长语句缩进
+^^^^^^^^^^
+
+编写长语句时,可以使用换行符()换行。在这种情况下,下一行应该与上一行的最后
+一个“.”句点或“=”对齐,或者是缩进4个空格符
+
+::
+
+ this_is_a_very_long(function_call, 'with many parameters') \
+ .that_returns_an_object_with_an_attribute
+
+ MyModel.query.filter(MyModel.scalar > 120) \
+ .order_by(MyModel.name.desc()) \
+ .limit(10)
+
+如果你使用括号“()”或花括号“{}”为长语句换行,那么下一行应与括号或花括号对齐:
+
+::
+
+ this_is_a_very_long(function_call, 'with many parameters',
+ 23, 42, 'and even more')
+
+对于元素众多的列表或元组,在第一个“[”或“(”之后马上换行:
+
+::
+
+ items = [
+ 'this is the first', 'set of items', 'with more items',
+ 'to come in this line', 'like this'
+ ]
+
+.. _Style Guidance: http://www.pocoo.org/internal/styleguide/
+
+
+空行
+^^^^
+
+顶层函数与类之间空两行,此外都只空一行。不要在代码中使用太多的空行来区分不同的逻辑模块。
+
+::
+
+ def hello(name):
+ print 'Hello %s!' % name
+
+
+ def goodbye(name):
+ print 'See you %s.' % name
+
+
+ class MyClass(object):
+ """This is a simple docstring."""
+
+ def __init__(self, name):
+ self.name = name
+
+ def get_annoying_name(self):
+ return self.name.upper() + '!!!!111'
+
+语句和表达式
+~~~~~~~~~~~~
+
+一般空格规则
+^^^^^^^^^^^^
+
+1. 单目运算符与运算对象之间不空格(例如,-,~等),即使单目运算符位于括号内部也一样。
+2. 双目运算符与运算对象之间要空格。
+
+::
+
+ exp = -1.05
+ value = (item_value / item_count) * offset / exp
+ value = my_list[index]
+ value = my_dict['key']
+
+比较
+^^^^
+
+1. 任意类型之间的比较,使用“==”和“!=”。
+2. 与单例(singletons)进行比较时,使用is和is not。
+3. 永远不要与True或False进行比较(例如,不要这样写:foo ==
+ False,而应该这样写:not foo)。
+
+否定成员关系检查
+^^^^^^^^^^^^^^^^
+
+使用foo not in bar,而不是not foo in bar。
+
+命名约定
+~~~~~~~~
+
+1. 类名称:采用骆驼拼写法(CamelCase),首字母缩略词保持大写不变(HTTPWriter,而不是HttpWriter)。
+2. 变量名:小写_以及_下划线(lowercase_with_underscores)。
+3. 方法与函数名:小写_以及_下划线(lowercase_with_underscores)。
+4. 常量:大写_以及_下划线(UPPERCASE_WITH_UNDERSCORES)。
+5. 预编译的正则表达式:name_re。
+6. 受保护的元素以一个下划线为前缀。双下划线前缀只有定义混入类(mixin
+ classes)时才使用。
+7. 如果使用关键词(keywords)作为类名称,应在名称后添加后置下划线(trailing
+ underscore)。
+ 允许与内建变量重名,不要在变量名后添加下划线进行区分。如果函数需要访问重名的内建变量,请将内建变量重新绑定为其他名称。
+8. 命名要有寓意, 不使用拼音,不使用无意义简单字母命名 (循环中计数例外 for
+ i in)
+9. 命名缩写要谨慎, 尽量是大家认可的缩写
+
+函数和方法的参数:
+^^^^^^^^^^^^^^^^^^
+
+1. 类方法:cls为第一个参数。
+2. 实例方法:self为第一个参数。
+3. property函数中使用匿名函数(lambdas)时,匿名函数的第一个参数可以用x替代,
+ 例如:display_name = property(lambda x: x.real_name or x.username)。
+
+
+文档注释(Docstring,即各方法,类的说明文档注释)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+所有文档字符串均以reStructuredText格式编写,方便Sphinx处理。文档字符串的行数不同,布局也不一样。
+如果只有一行,代表字符串结束的三个引号与代表字符串开始的三个引号在同一行。
+如果为多行,文档字符串中的文本紧接着代表字符串开始的三个引号编写,代表字符串结束的三个引号则自己独立成一行。
+(有能力尽可能用英文, 否则请中文优雅注释)
+
+::
+
+ def foo():
+ """This is a simple docstring."""
+
+
+ def bar():
+ """This is a longer docstring with so much information in there
+ that it spans three lines. In this case, the closing triple quote
+ is on its own line.
+ """
+
+文档字符串应分成简短摘要(尽量一行)和详细介绍。如果必要的话,摘要与详细介绍之间空一行。
+
+模块头部
+~~~~~~~~
+
+模块文件的头部包含有utf-8编码声明(如果模块中使用了非ASCII编码的字符,建议进行声明),以及标准的文档字符串。
+
+::
+
+ # -*- coding: utf-8 -*-
+ """
+ package.module
+ ~~~~~~~~~~~~~~
+
+ A brief description goes here.
+
+ :copyright: (c) YEAR by AUTHOR.
+ :license: LICENSE_NAME, see LICENSE_FILE for more details.
+ """
+
+注释(comment)
+~~~~~~~~~~~~~
+
+注释的规范与文档字符串编写规范类似。二者均以reStructuredText格式编写。
+如果使用注释来编写类属性的文档,请在#符号后添加一个冒号“:”。
+(有能力尽可能用英文, 否则请中文优雅注释)
+
+::
+
+ class User(object):
+ #: the name of the user as unicode string
+ name = Column(String)
+ #: the sha1 hash of the password + inline salt
+ pw_hash = Column(String)
\ No newline at end of file
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
new file mode 100644
index 000000000..7892999fe
--- /dev/null
+++ b/docs/quickstart.rst
@@ -0,0 +1,48 @@
+快速安装
+==========================
+
+Jumpserver 封装了一个All in one Docker,可以快速启动。该镜像集成了所有需要的组件,可以使用外置db和redis
+
+Tips: 不建议在生产中使用
+
+
+Docker 安装见: `Docker官方安装文档 `_
+
+
+快速启动
+```````````````
+使用root命令行输入::
+
+ $ docker run -p 8080:80 -p 2222:2222 jumpserver/jumpserver:0.5.0-beta2
+
+访问
+```````````````
+
+浏览器访问: http://localhost:8080
+
+ssh访问: ssh -p 2222 localhost
+
+
+额外环境变量
+```````````````
+
+- DB_ENGINE = mysql
+- DB_HOST = mysql_host
+- DB_PORT = 3306
+- DB_USER = xxx
+- DB_PASSWORD = xxxx
+- DB_NAME = jumpserver
+
+- REDIS_HOST = ''
+- REDIS_PORT = ''
+- REDIS_PASSWORD = ''
+
+ ::
+
+ docker run -p 8080:80 -p 2222:2222 -e DB_ENGINE=mysql -e DB_HOST=192.168.1.1 -e DB_PORT=3306 -e DB_USER=root -e DB_PASSWORD=xxx -e DB_NAME=jumpserver jumpserver/jumpserver:0.5.0-beta2
+
+
+仓库地址
+```````````````
+
+https://github.com/jumpserver/Dockerfile
diff --git a/docs/step_by_step.rst b/docs/step_by_step.rst
new file mode 100644
index 000000000..db4b08608
--- /dev/null
+++ b/docs/step_by_step.rst
@@ -0,0 +1,294 @@
+一步一步安装
+--------------------------
+
+环境
+~~~~
+
+- 系统: CentOS 7
+- IP: 192.168.244.144
+- 关闭 selinux和防火墙
+
+::
+
+ # CentOS 7
+ $ setenforce 0 # 可以设置配置文件永久关闭
+ $ systemctl stop iptables.service
+ $ systemctl stop firewalld.service
+
+ # CentOS6
+ $ setenforce 0
+ $ service iptables stop
+
+一. 准备Python3和Python虚拟环境
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**1.1 安装依赖包**
+
+::
+
+ $ yum -y install wget sqlite-devel xz gcc automake zlib-devel openssl-devel epel-release
+
+**1.2 编译安装**
+
+::
+
+ $ wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tar.xz
+ $ tar xvf Python-3.6.1.tar.xz && cd Python-3.6.1
+ $ ./configure && make && make install
+
+**1.3 建立python虚拟环境**
+
+因为CentOS
+6/7自带的是Python2,而Yum等工具依赖原来的Python,为了不扰乱原来的环境我们来使用Python虚拟环境
+
+::
+
+ $ cd /opt
+ $ python3 -m venv py3
+ $ source /opt/py3/bin/activate
+
+ # 看到下面的提示符代表成功,以后运行jumpserver都要先运行以上source命令,以下所有命令均在该虚拟环境中运行
+ (py3) [root@localhost py3]#
+
+二. 安装Jumpserver 0.5.0
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+**2.1 下载或clone项目**
+
+项目提交较多git clone时较大,你可以选择去github项目页面直接下载
+zip包,我的网速好,我直接clone了
+
+::
+
+ $ cd /opt/
+ $ git clone --depth=1 https://github.com/jumpserver/jumpserver.git && cd jumpserver && git checkout dev
+
+**2.2 安装依赖rpm包**
+
+::
+
+ $ cd /opt/jumpserver/requirements
+ $ yum -y install $(cat rpm_requirements.txt) # 如果没有任何报错请继续
+
+**2.3 安装python库依赖**
+
+::
+
+ $ pip install -r requirements.txt # 不要指定-i参数,因为镜像上可能没有最新的包,如果没有任何报错请继续
+
+**2.4 安装Redis, jumpserver使用redis做cache和celery broker**
+
+::
+
+ $ yum -y install redis
+ $ service redis start
+
+**2.5 安装MySQL**
+
+本教程使用mysql作为数据库,如果不使用mysql可以跳过相关mysql安装和配置
+
+::
+
+ # centos7
+ $ yum -y install mariadb mariadb-devel mariadb-server # centos7下安装的是mariadb
+ $ service mariadb start
+
+ # centos6
+ $ yum -y install mysql mysql-devel mysql-server
+ $ service mysqld start
+
+**2.6 创建数据库 jumpserver并授权**
+
+::
+
+ $ mysql
+ > create database jumpserver default charset 'utf8';
+ > grant all on jumpserver.* to 'jumpserver'@'127.0.0.1' identified by 'somepassword';
+
+**2.7 修改jumpserver配置文件**
+
+::
+
+ $ cd /opt/jumpserver
+ $ cp config_example.py config.py
+ $ vi config.py # 我们计划修改 DevelopmentConfig中的配置,因为默认jumpserver是使用该配置,它继承自Config
+
+**注意: 配置文件是python格式,不要用tab,而要用空格** **注意:
+配置文件是python格式,不要用tab,而要用空格** **注意:
+配置文件是python格式,不要用tab,而要用空格**
+
+::
+
+ class DevelopmentConfig(Config):
+ DEBUG = True
+ DB_ENGINE = 'mysql'
+ DB_HOST = '127.0.0.1'
+ DB_PORT = 3306
+ DB_USER = 'jumpserver'
+ DB_PASSWORD = 'somepassword'
+ DB_NAME = 'jumpserver'
+
+ ...
+
+ config = DevelopmentConfig() # 确保使用的是刚才设置的配置文件
+
+**2.8 生成数据库表结构和初始化数据**
+
+::
+
+ $ cd /opt/jumpserver/utils
+ $ bash make_migrations.sh
+
+**2.9 运行Jumpserver**
+
+::
+
+ $ cd /opt/jumpserver
+ $ python run_server.py all
+
+运行不报错,请浏览器访问 http://192.168.244.144:8080/
+(这里只是jumpserver, 没有web terminal,所以访问web terminal会报错)
+
+账号:admin 密码: admin
+
+三. 安装 SSH Server和Web Socket Server: Coco
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**3.1 下载clone项目**
+
+新开一个终端,连接测试机,别忘了 source /opt/py3/bin/activate
+
+::
+
+ $ cd /opt
+ $ git clone https://github.com/jumpserver/coco.git && cd coco && git checkout dev
+
+**3.2 安装依赖**
+
+::
+
+ $ cd /opt/coco/requirements $ yum -y install $(cat rpm_requirements.txt) $ pip install requirements.txt
+
+
+**3.2 安装依赖**
+
+::
+
+ $ cd /opt/coco/requirements
+ $ yum -y install $(cat rpm_requirements.txt)
+ $ pip install -r requirements.txt
+
+**3.3 查看配置文件并运行**
+
+::
+
+ $ cd /opt/coco
+ $ cp conf_example.py conf.py
+ $ python run_server.py
+
+这时需要去
+jumpserver管理后台-终端-终端(http://192.168.244.144:8080/terminal/terminal/)接受coco的注册
+
+::
+
+ Coco version 0.4.0, more see https://www.jumpserver.org
+ Starting ssh server at 0.0.0.0:2222
+ Quit the server with CONTROL-C.
+
+**3.4 测试连接**
+
+::
+
+ $ ssh -p2222 admin@192.168.244.144
+ 密码: admin
+
+ 如果是用在windows下,Xshell terminal登录语法如下
+ $ssh admin@192.168.244.144 2222
+ 密码: admin
+ 如果能登陆代表部署成功
+
+四. 安装 Web Terminal 前端: Luna
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Luna已改为纯前端,需要nginx来运行访问
+
+下载 release包,直接解压,不需要编译
+
+访问 https://github.com/jumpserver/luna/releases,下载对应release包
+
+4.1 解压luna
+
+::
+
+ $ pwd
+ /opt/
+
+ $ tar xvf luna.tar.gz
+ $ ls /opt/luna
+ ...
+
+五. 安装Windows支持组件
+~~~~~~~~~~~~~~~~~~~~~~~
+
+使用docker启动 guacamole
+
+.. code:: shell
+
+ docker run \
+ -p 8080:8080 \
+ -e JUMPSERVER_SERVER=http://:8080 \
+ jumpserver/guacamole
+
+这里所需要注意的是guacamole暴露出来的端口是8080,若与jumpserver部署在同一主机上自定义一下。
+
+修改JUMPSERVER_SERVER的配置,填上jumpserver的内网地址
+
+六. 配置 nginx 整合各组件
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+6.1 安装nginx 根据喜好选择安装方式和版本
+
+6.2 配置文件
+
+::
+
+ server {
+ listen 80;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ location /luna/ {
+ try_files $uri / /index.html;
+ alias /opt/luna/;
+ }
+
+ location /media/ {
+ add_header Content-Encoding gzip;
+ root /opt/jumpserver/data/;
+ }
+
+ location /static/ {
+ root /opt/jumpserver/data/;
+ }
+
+ location /socket.io/ {
+ proxy_pass http://localhost:5000/socket.io/;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+
+ location /guacamole/ {
+ proxy_pass http://:8080/;
+ }
+
+ location / {
+ proxy_pass http://localhost:8080;
+ }
+ }
+
+6.3 运行 nginx
+
+6.4 访问 http://192.168.244.144
\ No newline at end of file
diff --git a/docs/upgrade.rst b/docs/upgrade.rst
new file mode 100644
index 000000000..422aba51b
--- /dev/null
+++ b/docs/upgrade.rst
@@ -0,0 +1,18 @@
+升级
+----
+
+1. 升级 jumpserver
+
+::
+
+ $ git pull && pip install -r requirements/requirements.txt && cd utils && sh make_migrations.sh
+
+2. 升级 coco
+
+::
+
+ $ git pull && cd requirements && pip install -r requirements.txt # 不要指定 -i参数
+
+3. 升级 luna
+
+重新下载release包
\ No newline at end of file
diff --git a/docs/user_asset.rst b/docs/user_asset.rst
new file mode 100644
index 000000000..71855f76e
--- /dev/null
+++ b/docs/user_asset.rst
@@ -0,0 +1,27 @@
+个人资产
+=========
+
+这里介绍用户个人资产相关的功能。
+
+.. contents:: Topics
+
+.. _view_personal_assets:
+
+查看个人资产
+````````````
+
+登录个人用户,默认展示个人资产列表。点击主机名,查看资产的详细信息。
+
+.. _host_login:
+
+主机登录
+`````````
+
+点解页面左侧的"Web终端",进入主机登录页,然后点击页面右侧的主机IP地址,连接主机,页面右侧会展示当前连接的终端信息。
+
+.. _host_logout:
+
+主机登出
+`````````
+
+在主机登录页面,选择左上角的“服务器”按钮,出现两个选项,一个“断开链接“按钮,断开当前连接的主机;另一个”断开所有链接“,断开当前所有连接的主机。
\ No newline at end of file
diff --git a/docs/user_guide.rst b/docs/user_guide.rst
new file mode 100644
index 000000000..97d9810b3
--- /dev/null
+++ b/docs/user_guide.rst
@@ -0,0 +1,10 @@
+用户使用文档
+=============
+
+这部分给您介绍Jumpserver的用户管理模块的使用方法。
+
+.. toctree::
+ :maxdepth: 1
+
+ user_asset
+ user_info
\ No newline at end of file
diff --git a/docs/user_info.rst b/docs/user_info.rst
new file mode 100644
index 000000000..23feb4754
--- /dev/null
+++ b/docs/user_info.rst
@@ -0,0 +1,34 @@
+个人信息
+=========
+
+这里介绍个人信息相关的功能。
+
+.. contents:: Topics
+
+.. _view_personal_info:
+
+查看个人信息
+````````````
+
+点击页面左侧的“个人信息”,查看用户的个人信息、SSH密钥。
+
+.. _modify_personal_info:
+
+修改个人信息
+````````````
+
+在个人信息页,点击页面右上角的“设置”按钮,进入个人信息修改页面,填写个人信息,点击“提交”按钮,完成个人信息修改。
+
+.. _update_password:
+
+更新密码
+`````````
+
+在个人信息页,点击页面右上角的“重置密码“按钮,进入密码更新页面,填写原来密码、新密码等信息,点击“提交”按钮,完成密码更新。
+
+.. _update_ssh_key:
+
+密钥更新
+`````````
+
+在个人信息页,点击页面左上角的“重置SSH密钥“按钮,进入密钥更新页面,填写SSH公钥,点击“提交”按钮,完成密钥更新。
\ No newline at end of file
diff --git a/requirements/deb_requirements.txt b/requirements/deb_requirements.txt
index 93709db9e..a0ddb7642 100644
--- a/requirements/deb_requirements.txt
+++ b/requirements/deb_requirements.txt
@@ -1 +1 @@
-libtiff4-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk
+libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite gcc automake
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index d4807ceba..42f3bc36a 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -56,6 +56,8 @@ uritemplate==3.0.0
urllib3==1.22
vine==1.1.4
gunicorn==19.7.1
-django_celery_beat==1.1.0
+#https://github.com/celery/django-celery-beat/zipball/master#egg=django-celery-beat
+django_celery_beat==1.1.1
ephem==3.7.6.0
python-gssapi==0.6.4
+jms-es-sdk
diff --git a/run_server.py b/run_server.py
index 1a389e5eb..011453a0c 100644
--- a/run_server.py
+++ b/run_server.py
@@ -13,7 +13,7 @@ from apps import __version__
try:
from config import config as CONFIG
except ImportError:
- CONFIG = type('_', (), {'__getattr__': None})()
+ CONFIG = type('_', (), {'__getattr__': lambda *arg: None})()
os.environ["PYTHONIOENCODING"] = "UTF-8"
|