Merge with dev

pull/968/head
ibuler 2018-01-23 15:13:24 +08:00
commit f37b331630
69 changed files with 771 additions and 467 deletions

View File

@ -26,36 +26,7 @@ Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互
### Install 安装 ### Install 安装
1. 安装 Python3    [详细安装](https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7)
2. 安装依赖
```
$ cd requirements && yum -y install $(cat rpm_requirements.txt) && pip install -r requirements.txt
```
3. 修改配置文件
```
$ cp config_example.py config.py
```
4. 修改表结构
```
$ cd apps && python manage.py makemigrations && python manage.py migrate
```
5. 运行
```
$ python run_server.py
```
6. 其它
整合luna,coco需要nginx来配合, 详见详细安装文档
### Usage 使用 ### Usage 使用

View File

@ -1,5 +1,5 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
# Copyright (C) 2014-2017 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved. # Copyright (C) 2014-2018 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
# #
# Licensed under the GNU General Public License v2.0 (the "License"); # Licensed under the GNU General Public License v2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -87,7 +87,7 @@ class AssetGroupViewSet(CustomFilterMixin, BulkModelViewSet):
""" """
Asset group api set, for add,delete,update,list,retrieve resource Asset group api set, for add,delete,update,list,retrieve resource
""" """
queryset = AssetGroup.objects.all() queryset = AssetGroup.objects.all().annotate(asset_count=Count("assets"))
serializer_class = serializers.AssetGroupSerializer serializer_class = serializers.AssetGroupSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
@ -298,9 +298,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
class LabelViewSet(BulkModelViewSet): class LabelViewSet(BulkModelViewSet):
queryset = Label.objects.annotate(asset_count=Count("assets"))\ queryset = Label.objects.annotate(asset_count=Count("assets"))
.annotate(admin_user_count=Count("adminuser")) \
.annotate(system_user_count=Count("systemuser"))
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
serializer_class = serializers.LabelSerializer serializer_class = serializers.LabelSerializer

View File

@ -6,7 +6,7 @@
Other module of this app shouldn't connect with other app. Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-2017 by Jumpserver Team. :copyright: (c) 2014-2018 by Jumpserver Team.
:license: GPL v2, see LICENSE for more details. :license: GPL v2, see LICENSE for more details.
""" """

View File

@ -16,7 +16,7 @@ class Label(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_("Name")) name = models.CharField(max_length=128, verbose_name=_("Name"))
value = models.CharField(max_length=128, verbose_name=_("Value")) value = models.CharField(max_length=128, verbose_name=_("Value"))
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES, verbose_name=_("Category")) category = models.CharField(max_length=128, choices=CATEGORY_CHOICES, default=USER_CATEGORY, verbose_name=_("Category"))
is_active = models.BooleanField(default=True, verbose_name=_("Is active")) is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment")) comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
date_created = models.DateTimeField( date_created = models.DateTimeField(

View File

@ -30,7 +30,6 @@ class AssetUser(models.Model):
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ]) _private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key')) _public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
labels = models.ManyToManyField('assets.Label', blank=True, verbose_name=_("Labels"))
comment = models.TextField(blank=True, verbose_name=_('Comment')) comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True) date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True) date_updated = models.DateTimeField(auto_now=True)

View File

@ -22,7 +22,7 @@ class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
@staticmethod @staticmethod
def get_assets_amount(obj): def get_assets_amount(obj):
return obj.assets.count() return obj.asset_count
class AssetUpdateSystemUserSerializer(serializers.ModelSerializer): class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
@ -288,8 +288,6 @@ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer):
class LabelSerializer(serializers.ModelSerializer): class LabelSerializer(serializers.ModelSerializer):
asset_count = serializers.SerializerMethodField() asset_count = serializers.SerializerMethodField()
admin_user_count = serializers.SerializerMethodField()
system_user_count = serializers.SerializerMethodField()
class Meta: class Meta:
model = Label model = Label
@ -300,14 +298,6 @@ class LabelSerializer(serializers.ModelSerializer):
def get_asset_count(obj): def get_asset_count(obj):
return obj.asset_count return obj.asset_count
@staticmethod
def get_admin_user_count(obj):
return obj.admin_user_count
@staticmethod
def get_system_user_count(obj):
return obj.system_user_count
def get_field_names(self, declared_fields, info): def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info) fields = super().get_field_names(declared_fields, info)
fields.extend(['get_category_display']) fields.extend(['get_category_display'])

View File

@ -121,7 +121,7 @@ function initTable() {
{data: "type" }, {data: "is_connective" }], {data: "type" }, {data: "is_connective" }],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initServerSideDataTable(options);
} }
$(document).ready(function () { $(document).ready(function () {

View File

@ -184,7 +184,7 @@ function initTable() {
{data: "get_type_display" }, {data: "is_connective" }, {data: "id"}], {data: "get_type_display" }, {data: "is_connective" }, {data: "id"}],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initServerSideDataTable(options);
} }

View File

@ -34,10 +34,6 @@ $(document).ready(function(){
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}}, }},
{targets: 3, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
}},
{targets: 4, createdCell: function (td, cellData, rowData) { {targets: 4, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-group-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "assets:asset-group-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_group_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_group_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);

View File

@ -176,7 +176,7 @@ function initTable() {
{data: "get_type_display" }, {data: "is_connective" }, {data: "id"}], {data: "get_type_display" }, {data: "is_connective" }, {data: "id"}],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initServerSideDataTable(options);
} }

View File

@ -14,8 +14,6 @@
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Value' %}</th> <th class="text-center">{% trans 'Value' %}</th>
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Admin user' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
</thead> </thead>
@ -36,7 +34,7 @@ function initTable() {
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}}, }},
{targets: 6, createdCell: function (td, cellData, rowData) { {targets: 4, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:cluster-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "assets:cluster-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_cluster_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_cluster_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn) $(td).html(update_btn + del_btn)
@ -44,8 +42,7 @@ function initTable() {
ajax_url: '{% url "api-assets:label-list" %}?sort=name', ajax_url: '{% url "api-assets:label-list" %}?sort=name',
columns: [ columns: [
{data: "id"}, {data: "name" }, {data: "value" }, {data: "id"}, {data: "name" }, {data: "value" },
{data: "asset_count" }, {data: "admin_user_count" }, {data: "asset_count" }, {data: "id"}
{data: "system_user_count" }, {data: "id"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -121,7 +121,7 @@ function initAssetsTable() {
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "hostname" }], columns: [{data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "hostname" }],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initServerSideDataTable(options);
} }
$(document).ready(function () { $(document).ready(function () {

View File

@ -60,11 +60,6 @@ class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView)
success_url = reverse_lazy('assets:cluster-list') success_url = reverse_lazy('assets:cluster-list')
success_message = update_success_msg 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): def get_context_data(self, **kwargs):
context = { context = {
'app': _('assets'), 'app': _('assets'),

View File

@ -63,8 +63,6 @@ class LDAPTestingAPI(APIView):
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"] search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"] attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
print(serializer.validated_data)
try: try:
attr_map = json.loads(attr_map) attr_map = json.loads(attr_map)
except json.JSONDecodeError: except json.JSONDecodeError:
@ -77,9 +75,6 @@ class LDAPTestingAPI(APIView):
except Exception as e: except Exception as e:
return Response({"error": str(e)}, status=401) 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": "*"}), ok = conn.search(search_ou, search_filter % ({"user": "*"}),
attributes=list(attr_map.values())) attributes=list(attr_map.values()))
if not ok: if not ok:

View File

@ -5,6 +5,7 @@ import json
from django import forms from django import forms
from django.utils import six from django.utils import six
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
class DictField(forms.Field): class DictField(forms.Field):
@ -18,16 +19,16 @@ class DictField(forms.Field):
# we don't need to handle that explicitly. # we don't need to handle that explicitly.
if isinstance(value, six.string_types): if isinstance(value, six.string_types):
try: try:
print(value)
value = json.loads(value) value = json.loads(value)
return value return value
except json.JSONDecodeError: except json.JSONDecodeError:
pass return ValidationError(_("Not a valid json"))
value = {} else:
return value return ValidationError(_("Not a string type"))
def validate(self, value): def validate(self, value):
print(value) if isinstance(value, ValidationError):
raise value
if not value and self.required: if not value and self.required:
raise ValidationError(self.error_messages['required'], code='required') raise ValidationError(self.error_messages['required'], code='required')

View File

@ -4,7 +4,9 @@ import json
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.html import escape
from django.db import transaction from django.db import transaction
from django.conf import settings
from .models import Setting from .models import Setting
from .fields import DictField from .fields import DictField
@ -24,34 +26,38 @@ def to_form_value(value):
data = value data = value
return data return data
except json.JSONDecodeError: except json.JSONDecodeError:
return '' return ""
class BaseForm(forms.Form): class BaseForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
settings = Setting.objects.all() db_settings = Setting.objects.all()
for name, field in self.fields.items(): for name, field in self.fields.items():
db_value = getattr(settings, name).value db_value = getattr(db_settings, name).value
if db_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) 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: if not self.is_bound:
raise ValueError("Form is not bound") raise ValueError("Form is not bound")
settings = Setting.objects.all() db_settings = Setting.objects.all()
if self.is_valid(): if self.is_valid():
with transaction.atomic(): with transaction.atomic():
for name, value in self.cleaned_data.items(): for name, value in self.cleaned_data.items():
field = self.fields[name] field = self.fields[name]
if isinstance(field.widget, forms.PasswordInput) and not value: if isinstance(field.widget, forms.PasswordInput) and not value:
continue continue
if value == to_form_value(getattr(settings, name).value): if value == to_form_value(getattr(db_settings, name).value):
continue continue
defaults = { defaults = {
'name': name, 'name': name,
'category': category,
'value': to_model_value(value) 'value': to_model_value(value)
} }
Setting.objects.update_or_create(defaults=defaults, name=name) Setting.objects.update_or_create(defaults=defaults, name=name)
@ -72,9 +78,6 @@ class BasicSettingForm(BaseForm):
max_length=1024, label=_("Email Subject Prefix"), max_length=1024, label=_("Email Subject Prefix"),
initial="[Jumpserver] " initial="[Jumpserver] "
) )
AUTH_LDAP = forms.BooleanField(
label=_("Enable LDAP Auth"), initial=False, required=False
)
class EmailSettingForm(BaseForm): class EmailSettingForm(BaseForm):
@ -129,3 +132,28 @@ class LDAPSettingForm(BaseForm):
AUTH_LDAP_START_TLS = forms.BooleanField( AUTH_LDAP_START_TLS = forms.BooleanField(
label=_("Use SSL"), initial=False, required=False label=_("Use SSL"), 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"
)
)

View File

@ -2,6 +2,7 @@ import json
import ldap import ldap
from django.db import models from django.db import models
from django.db.utils import ProgrammingError, OperationalError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
from django_auth_ldap.config import LDAPSearch from django_auth_ldap.config import LDAPSearch
@ -24,6 +25,7 @@ class SettingManager(models.Manager):
class Setting(models.Model): class Setting(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name")) name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
value = models.TextField(verbose_name=_("Value")) value = models.TextField(verbose_name=_("Value"))
category = models.CharField(max_length=128, default="default")
enabled = models.BooleanField(verbose_name=_("Enabled"), default=True) enabled = models.BooleanField(verbose_name=_("Enabled"), default=True)
comment = models.TextField(verbose_name=_("Comment")) comment = models.TextField(verbose_name=_("Comment"))
@ -33,17 +35,28 @@ class Setting(models.Model):
return self.name return self.name
@property @property
def value_(self): def cleaned_value(self):
try: try:
return json.loads(self.value) return json.loads(self.value)
except json.JSONDecodeError: except json.JSONDecodeError:
return None 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 @classmethod
def refresh_all_settings(cls): def refresh_all_settings(cls):
try:
settings_list = cls.objects.all() settings_list = cls.objects.all()
for setting in settings_list: for setting in settings_list:
setting.refresh_setting() setting.refresh_setting()
except (ProgrammingError, OperationalError):
pass
def refresh_setting(self): def refresh_setting(self):
try: try:
@ -53,9 +66,9 @@ class Setting(models.Model):
setattr(settings, self.name, value) setattr(settings, self.name, value)
if self.name == "AUTH_LDAP": 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) 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) settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
if self.name == "AUTH_LDAP_SEARCH_FILTER": if self.name == "AUTH_LDAP_SEARCH_FILTER":

View File

@ -20,6 +20,9 @@
<li> <li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a> <a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li> </li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">

View File

@ -20,6 +20,9 @@
<li> <li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a> <a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li> </li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">

View File

@ -20,6 +20,9 @@
<li class="active"> <li class="active">
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a> <a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li> </li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">

View File

@ -0,0 +1,129 @@
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li class="active">
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-12" style="padding-left:0">
<div class="ibox-content" style="border-width: 0;padding-top: 40px;">
<form action="" method="post" class="form-horizontal">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>{% trans "Basic setting" %}</h3>
{% for field in form %}
{% if not field.field|is_bool_field %}
{% bootstrap_field field layout="horizontal" %}
{% else %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-8">
<div class="col-sm-1">
{{ field }}
</div>
<div class="col-sm-9">
<span class="help-block" >{{ field.help_text }}</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
<div class="hr-line-dashed"></div>
<h3>{% trans "Command storage" %}</h3>
<table class="table table-hover " id="task-history-list-table" >
<thead>
<tr>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Type' %}</th>
</tr>
</thead>
<tbody>
{% for name, setting in command_storage.items %}
<tr>
<td>{{ name }}</td>
<td>{{ setting.TYPE }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{# <button class="btn btn-default btn-circle btn-add-command-storage" data-toggle="modal" data-target="#add_command_storage_model" tabindex="0" type="button"><i class="fa fa-plus"></i></button>#}
{# <div class="hr-line-dashed"></div>#}
{# <h3>{% trans "Replay storage" %}</h3>#}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
})
.on("click", ".btn-test", function () {
var data = {};
var form = $("form").serializeArray();
$.each(form, function (i, field) {
data[field.name] = field.value;
});
var the_url = "{% url 'api-common:ldap-testing' %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg)
}
APIUpdateAttr({
url: the_url,
body: JSON.stringify(data),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
.on('click', '', function () {
})
</script>
{% endblock %}

View File

@ -10,4 +10,5 @@ urlpatterns = [
url(r'^$', views.BasicSettingView.as_view(), name='basic-setting'), url(r'^$', views.BasicSettingView.as_view(), name='basic-setting'),
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'), url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'), url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
] ]

View File

@ -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.shortcuts import render, redirect
from django.contrib import messages from django.contrib import messages
from django.utils.translation import ugettext as _ 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 .mixins import AdminUserRequiredMixin
from .signals import ldap_auth_enable from .signals import ldap_auth_enable
@ -25,8 +28,6 @@ class BasicSettingView(AdminUserRequiredMixin, TemplateView):
form = self.form_class(request.POST) form = self.form_class(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
if "AUTH_LDAP" in form.cleaned_data:
ldap_auth_enable.send(form.cleaned_data["AUTH_LDAP"])
msg = _("Update setting successfully, please restart program") msg = _("Update setting successfully, please restart program")
messages.success(request, msg) messages.success(request, msg)
return redirect('settings:basic-setting') return redirect('settings:basic-setting')
@ -79,6 +80,8 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
form = self.form_class(request.POST) form = self.form_class(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
if "AUTH_LDAP" in form.cleaned_data:
ldap_auth_enable.send(form.cleaned_data["AUTH_LDAP"])
msg = _("Update setting successfully, please restart program") msg = _("Update setting successfully, please restart program")
messages.success(request, msg) messages.success(request, msg)
return redirect('settings:ldap-setting') return redirect('settings:ldap-setting')
@ -86,3 +89,31 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
context = self.get_context_data() context = self.get_context_data()
context.update({"form": form}) context.update({"form": form})
return render(request, self.template_name, context) 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
context = {
'app': _('Settings'),
'action': _('Terminal setting'),
'form': self.form_class(),
'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)

View File

@ -274,7 +274,7 @@ EMAIL_HOST_USER = CONFIG.EMAIL_HOST_USER
EMAIL_HOST_PASSWORD = CONFIG.EMAIL_HOST_PASSWORD EMAIL_HOST_PASSWORD = CONFIG.EMAIL_HOST_PASSWORD
EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL
EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS
EMAIL_SUBJECT_PREFIX = CONFIG.EMAIL_SUBJECT_PREFIX EMAIL_SUBJECT_PREFIX = CONFIG.EMAIL_SUBJECT_PREFIX or ''
REST_FRAMEWORK = { REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions, # Use Django's standard `django.contrib.auth` permissions,
@ -298,7 +298,7 @@ REST_FRAMEWORK = {
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S %z', 'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S %z',
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S %z'], 'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S %z'],
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 15 # 'PAGE_SIZE': 15
} }
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
@ -373,7 +373,20 @@ CAPTCHA_FOREGROUND_COLOR = '#001100'
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',) CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE 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'],
# },
}
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html # Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3 = { BOOTSTRAP3 = {

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n" "Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-12 18:51+0800\n" "POT-Creation-Date: 2018-01-23 11:56+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n" "Language-Team: Jumpserver team<ibuler@qq.com>\n"
@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: assets/forms.py:23 assets/forms.py:53 assets/forms.py:99 perms/forms.py:37 #: 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 #: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:245
msgid "Select asset groups" msgid "Select asset groups"
msgstr "选择资产组" msgstr "选择资产组"
@ -44,7 +44,7 @@ msgstr "默认使用管理用户"
#: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127 #: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127
#: assets/templates/assets/asset_group_detail.html:75 perms/forms.py:34 #: assets/templates/assets/asset_group_detail.html:75 perms/forms.py:34
#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:243 #: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:242
msgid "Select assets" msgid "Select assets"
msgstr "选择资产" msgstr "选择资产"
@ -66,7 +66,7 @@ msgstr "端口"
#: assets/templates/assets/system_user_list.html:26 perms/models.py:17 #: 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_create_update.html:40
#: perms/templates/perms/asset_permission_list.html:28 templates/_nav.html:22 #: 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:116
#: terminal/templates/terminal/command_list.html:40 #: terminal/templates/terminal/command_list.html:40
#: terminal/templates/terminal/command_list.html:73 #: terminal/templates/terminal/command_list.html:73
#: terminal/templates/terminal/session_list.html:41 #: terminal/templates/terminal/session_list.html:41
@ -77,7 +77,7 @@ msgid "Asset"
msgstr "资产" msgstr "资产"
#: assets/forms.py:161 perms/forms.py:40 #: assets/forms.py:161 perms/forms.py:40
#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:249 #: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:248
msgid "Select system users" msgid "Select system users"
msgstr "选择系统用户" msgstr "选择系统用户"
@ -99,14 +99,15 @@ msgstr "选择的系统用户将会在该集群资产上创建"
#: assets/templates/assets/cluster_detail.html:57 #: assets/templates/assets/cluster_detail.html:57
#: assets/templates/assets/cluster_list.html:19 #: assets/templates/assets/cluster_list.html:19
#: assets/templates/assets/system_user_detail.html:58 #: assets/templates/assets/system_user_detail.html:58
#: assets/templates/assets/system_user_list.html:24 common/models.py:25 #: assets/templates/assets/system_user_list.html:24 common/models.py:26
#: ops/models.py:31 ops/templates/ops/task_detail.html:56 #: common/templates/common/terminal_setting.html:62 ops/models.py:31
#: ops/templates/ops/task_list.html:34 perms/models.py:14 #: 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_create_update.html:33
#: perms/templates/perms/asset_permission_detail.html:62 #: perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:25 #: perms/templates/perms/asset_permission_list.html:25
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:14 #: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:15
#: terminal/models.py:118 terminal/templates/terminal/terminal_detail.html:43 #: terminal/models.py:141 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14 #: 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/models/user.py:35 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:62 #: users/templates/users/user_detail.html:62
@ -126,10 +127,10 @@ msgstr "集群级别管理用户"
#: assets/forms.py:200 #: assets/forms.py:200
msgid "Password or private key password" msgid "Password or private key password"
msgstr "密码或秘钥不合法" msgstr "密码或秘钥密码"
#: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30 #: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30
#: common/forms.py:110 users/forms.py:16 users/forms.py:24 #: common/forms.py:113 users/forms.py:16 users/forms.py:24
#: users/templates/users/login.html:56 #: users/templates/users/login.html:56
#: users/templates/users/reset_password.html:52 #: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11 #: users/templates/users/user_create.html:11
@ -239,7 +240,7 @@ msgstr "测试环境"
#: assets/templates/assets/asset_list.html:31 #: assets/templates/assets/asset_list.html:31
#: assets/templates/assets/cluster_assets.html:52 #: assets/templates/assets/cluster_assets.html:52
#: assets/templates/assets/system_user_asset.html:53 #: assets/templates/assets/system_user_asset.html:53
#: assets/templates/assets/user_asset_list.html:20 #: assets/templates/assets/user_asset_list.html:20 common/forms.py:140
#: perms/templates/perms/asset_permission_asset.html:55 #: perms/templates/perms/asset_permission_asset.html:55
#: users/templates/users/login_log_list.html:52 #: users/templates/users/login_log_list.html:52
#: users/templates/users/user_granted_asset.html:49 #: users/templates/users/user_granted_asset.html:49
@ -253,7 +254,7 @@ msgstr "IP"
#: assets/templates/assets/asset_list.html:30 #: assets/templates/assets/asset_list.html:30
#: assets/templates/assets/cluster_assets.html:51 #: assets/templates/assets/cluster_assets.html:51
#: assets/templates/assets/system_user_asset.html:52 #: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/user_asset_list.html:19 #: assets/templates/assets/user_asset_list.html:19 common/forms.py:139
#: perms/templates/perms/asset_permission_asset.html:54 #: perms/templates/perms/asset_permission_asset.html:54
#: users/templates/users/user_granted_asset.html:48 #: users/templates/users/user_granted_asset.html:48
#: users/templates/users/user_group_granted_asset.html:49 #: users/templates/users/user_group_granted_asset.html:49
@ -400,9 +401,9 @@ msgstr "创建日期"
#: assets/templates/assets/asset_group_list.html:17 #: assets/templates/assets/asset_group_list.html:17
#: assets/templates/assets/cluster_detail.html:97 #: assets/templates/assets/cluster_detail.html:97
#: assets/templates/assets/system_user_detail.html:100 #: assets/templates/assets/system_user_detail.html:100
#: assets/templates/assets/system_user_list.html:30 common/models.py:28 #: assets/templates/assets/system_user_list.html:30 common/models.py:30
#: ops/models.py:37 perms/models.py:24 #: ops/models.py:37 perms/models.py:24
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:22 #: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:25
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15 #: 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:110
#: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_detail.html:67
@ -494,7 +495,7 @@ msgstr "Shell"
#: assets/models/user.py:269 perms/models.py:19 #: assets/models/user.py:269 perms/models.py:19
#: perms/templates/perms/asset_permission_detail.html:136 #: perms/templates/perms/asset_permission_detail.html:136
#: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26 #: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26
#: terminal/backends/command/models.py:12 terminal/models.py:94 #: terminal/backends/command/models.py:12 terminal/models.py:117
#: terminal/templates/terminal/command_list.html:48 #: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74 #: terminal/templates/terminal/command_list.html:74
#: terminal/templates/terminal/session_list.html:49 #: terminal/templates/terminal/session_list.html:49
@ -576,9 +577,9 @@ msgstr "仅修改你需要更新的字段"
#: assets/views/admin_user.py:106 assets/views/asset.py:48 #: 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:61 assets/views/asset.py:84 assets/views/asset.py:144
#: assets/views/asset.py:161 assets/views/asset.py:185 #: assets/views/asset.py:161 assets/views/asset.py:185
#: assets/views/cluster.py:26 assets/views/cluster.py:85 #: assets/views/cluster.py:26 assets/views/cluster.py:80
#: assets/views/cluster.py:102 assets/views/group.py:34 #: assets/views/cluster.py:97 assets/views/group.py:34 assets/views/group.py:52
#: assets/views/group.py:52 assets/views/group.py:69 assets/views/group.py:87 #: 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:28 assets/views/system_user.py:44
#: assets/views/system_user.py:60 assets/views/system_user.py:75 #: assets/views/system_user.py:60 assets/views/system_user.py:75
#: templates/_nav.html:19 #: templates/_nav.html:19
@ -650,7 +651,7 @@ msgstr "自动生成秘钥"
#: assets/templates/assets/asset_update.html:47 #: assets/templates/assets/asset_update.html:47
#: assets/templates/assets/cluster_create_update.html:46 #: assets/templates/assets/cluster_create_update.html:46
#: perms/templates/perms/asset_permission_create_update.html:45 #: perms/templates/perms/asset_permission_create_update.html:45
#: terminal/templates/terminal/terminal_update.html:40 #: terminal/templates/terminal/terminal_update.html:41
msgid "Other" msgid "Other"
msgstr "其它" msgstr "其它"
@ -661,12 +662,13 @@ msgstr "其它"
#: assets/templates/assets/asset_group_create.html:16 #: assets/templates/assets/asset_group_create.html:16
#: assets/templates/assets/asset_update.html:55 #: assets/templates/assets/asset_update.html:55
#: assets/templates/assets/cluster_create_update.html:54 #: assets/templates/assets/cluster_create_update.html:54
#: common/templates/common/basic_setting.html:56 #: common/templates/common/basic_setting.html:58
#: common/templates/common/email_setting.html:56 #: common/templates/common/email_setting.html:59
#: common/templates/common/ldap_setting.html:56 #: common/templates/common/ldap_setting.html:59
#: common/templates/common/terminal_setting.html:82
#: perms/templates/perms/asset_permission_create_update.html:67 #: perms/templates/perms/asset_permission_create_update.html:67
#: terminal/templates/terminal/terminal_update.html:45 #: terminal/templates/terminal/terminal_update.html:46
#: users/templates/users/_user.html:49 #: users/templates/users/_user.html:43
#: users/templates/users/user_bulk_update.html:23 #: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_password_update.html:58 #: users/templates/users/user_password_update.html:58
#: users/templates/users/user_profile.html:139 #: users/templates/users/user_profile.html:139
@ -681,15 +683,16 @@ msgstr "重置"
#: assets/templates/assets/asset_bulk_update.html:24 #: assets/templates/assets/asset_bulk_update.html:24
#: assets/templates/assets/asset_create.html:41 #: assets/templates/assets/asset_create.html:41
#: assets/templates/assets/asset_group_create.html:17 #: assets/templates/assets/asset_group_create.html:17
#: assets/templates/assets/asset_list.html:55 #: assets/templates/assets/asset_list.html:53
#: assets/templates/assets/asset_update.html:56 #: assets/templates/assets/asset_update.html:56
#: assets/templates/assets/cluster_create_update.html:55 #: assets/templates/assets/cluster_create_update.html:55
#: common/templates/common/basic_setting.html:57 #: common/templates/common/basic_setting.html:59
#: common/templates/common/email_setting.html:57 #: common/templates/common/email_setting.html:60
#: common/templates/common/ldap_setting.html:57 #: common/templates/common/ldap_setting.html:60
#: common/templates/common/terminal_setting.html:83
#: perms/templates/perms/asset_permission_create_update.html:68 #: perms/templates/perms/asset_permission_create_update.html:68
#: terminal/templates/terminal/terminal_update.html:46 #: terminal/templates/terminal/terminal_update.html:47
#: users/templates/users/_user.html:50 #: users/templates/users/_user.html:44
#: users/templates/users/first_login.html:62 #: users/templates/users/first_login.html:62
#: users/templates/users/forgot_password.html:44 #: users/templates/users/forgot_password.html:44
#: users/templates/users/user_bulk_update.html:24 #: users/templates/users/user_bulk_update.html:24
@ -726,8 +729,8 @@ msgstr "资产列表"
#: assets/templates/assets/asset_detail.html:24 #: assets/templates/assets/asset_detail.html:24
#: assets/templates/assets/asset_group_detail.html:18 #: assets/templates/assets/asset_group_detail.html:18
#: assets/templates/assets/asset_group_detail.html:177 #: assets/templates/assets/asset_group_detail.html:177
#: assets/templates/assets/asset_group_list.html:42 #: assets/templates/assets/asset_group_list.html:38
#: assets/templates/assets/asset_list.html:95 #: assets/templates/assets/asset_list.html:98
#: assets/templates/assets/cluster_assets.html:170 #: assets/templates/assets/cluster_assets.html:170
#: assets/templates/assets/cluster_detail.html:25 #: assets/templates/assets/cluster_detail.html:25
#: assets/templates/assets/cluster_list.html:43 #: assets/templates/assets/cluster_list.html:43
@ -750,8 +753,8 @@ msgstr "更新"
#: assets/templates/assets/admin_user_list.html:84 #: assets/templates/assets/admin_user_list.html:84
#: assets/templates/assets/asset_detail.html:28 #: assets/templates/assets/asset_detail.html:28
#: assets/templates/assets/asset_group_detail.html:22 #: assets/templates/assets/asset_group_detail.html:22
#: assets/templates/assets/asset_group_list.html:43 #: assets/templates/assets/asset_group_list.html:39
#: assets/templates/assets/asset_list.html:96 #: assets/templates/assets/asset_list.html:99
#: assets/templates/assets/cluster_detail.html:29 #: assets/templates/assets/cluster_detail.html:29
#: assets/templates/assets/cluster_list.html:44 #: assets/templates/assets/cluster_list.html:44
#: assets/templates/assets/system_user_detail.html:30 #: assets/templates/assets/system_user_detail.html:30
@ -776,9 +779,9 @@ msgstr "资产列表"
#: assets/templates/assets/admin_user_assets.html:62 #: assets/templates/assets/admin_user_assets.html:62
#: assets/templates/assets/asset_group_detail.html:53 #: assets/templates/assets/asset_group_detail.html:53
#: assets/templates/assets/asset_list.html:34
#: assets/templates/assets/cluster_assets.html:54 #: assets/templates/assets/cluster_assets.html:54
#: assets/templates/assets/user_asset_list.html:22 #: assets/templates/assets/user_asset_list.html:22
#: common/templates/common/terminal_setting.html:63
#: users/templates/users/login_log_list.html:50 #: users/templates/users/login_log_list.html:50
msgid "Type" msgid "Type"
msgstr "类型" msgstr "类型"
@ -786,7 +789,7 @@ msgstr "类型"
#: assets/templates/assets/admin_user_assets.html:63 #: assets/templates/assets/admin_user_assets.html:63
#: assets/templates/assets/admin_user_list.html:25 #: assets/templates/assets/admin_user_list.html:25
#: assets/templates/assets/asset_detail.html:376 #: assets/templates/assets/asset_detail.html:376
#: assets/templates/assets/asset_list.html:38 #: assets/templates/assets/asset_list.html:36
#: assets/templates/assets/system_user_asset.html:55 #: assets/templates/assets/system_user_asset.html:55
#: assets/templates/assets/system_user_list.html:27 #: assets/templates/assets/system_user_list.html:27
msgid "Reachable" msgid "Reachable"
@ -827,8 +830,8 @@ msgstr "使用集群管理用户"
#: assets/templates/assets/admin_user_detail.html:101 #: assets/templates/assets/admin_user_detail.html:101
#: assets/templates/assets/asset_detail.html:230 #: assets/templates/assets/asset_detail.html:230
#: assets/templates/assets/asset_group_list.html:85 #: assets/templates/assets/asset_group_list.html:81
#: assets/templates/assets/asset_list.html:214 #: assets/templates/assets/asset_list.html:220
#: assets/templates/assets/cluster_assets.html:104 #: assets/templates/assets/cluster_assets.html:104
#: assets/templates/assets/cluster_list.html:89 #: assets/templates/assets/cluster_list.html:89
#: assets/templates/assets/system_user_detail.html:164 #: assets/templates/assets/system_user_detail.html:164
@ -859,7 +862,7 @@ msgstr "比例"
#: assets/templates/assets/admin_user_list.html:29 #: assets/templates/assets/admin_user_list.html:29
#: assets/templates/assets/asset_group_detail.html:55 #: assets/templates/assets/asset_group_detail.html:55
#: assets/templates/assets/asset_group_list.html:18 #: assets/templates/assets/asset_group_list.html:18
#: assets/templates/assets/asset_list.html:39 #: assets/templates/assets/asset_list.html:37
#: assets/templates/assets/cluster_assets.html:56 #: assets/templates/assets/cluster_assets.html:56
#: assets/templates/assets/cluster_list.html:23 #: assets/templates/assets/cluster_list.html:23
#: assets/templates/assets/system_user_list.html:31 #: assets/templates/assets/system_user_list.html:31
@ -879,7 +882,7 @@ msgid "Group"
msgstr "组" msgstr "组"
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:186 #: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:186
#: assets/views/cluster.py:103 #: assets/views/cluster.py:98
msgid "Asset detail" msgid "Asset detail"
msgstr "资产详情" msgstr "资产详情"
@ -909,7 +912,7 @@ msgid "Quick modify"
msgstr "快速修改" msgstr "快速修改"
#: assets/templates/assets/asset_detail.html:175 #: assets/templates/assets/asset_detail.html:175
#: assets/templates/assets/asset_list.html:37 #: assets/templates/assets/asset_list.html:35
#: assets/templates/assets/user_asset_list.html:25 perms/models.py:20 #: 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_create_update.html:47
#: perms/templates/perms/asset_permission_detail.html:116 #: perms/templates/perms/asset_permission_detail.html:116
@ -970,8 +973,8 @@ msgstr "移除"
msgid "Create asset group" msgid "Create asset group"
msgstr "创建资产组" msgstr "创建资产组"
#: assets/templates/assets/asset_group_list.html:80 #: assets/templates/assets/asset_group_list.html:76
#: assets/templates/assets/asset_list.html:209 #: assets/templates/assets/asset_list.html:215
#: assets/templates/assets/cluster_list.html:84 #: assets/templates/assets/cluster_list.html:84
#: assets/templates/assets/system_user_list.html:129 #: assets/templates/assets/system_user_list.html:129
#: users/templates/users/user_detail.html:333 #: users/templates/users/user_detail.html:333
@ -981,29 +984,29 @@ msgstr "创建资产组"
msgid "Are you sure?" msgid "Are you sure?"
msgstr "你确认吗?" msgstr "你确认吗?"
#: assets/templates/assets/asset_group_list.html:81 #: assets/templates/assets/asset_group_list.html:77
#: users/templates/users/user_group_list.html:78 #: users/templates/users/user_group_list.html:78
msgid "This will delete the selected groups !!!" msgid "This will delete the selected groups !!!"
msgstr "删除选择组" msgstr "删除选择组"
#: assets/templates/assets/asset_group_list.html:89 #: assets/templates/assets/asset_group_list.html:85
msgid "Group deleted" msgid "Group deleted"
msgstr "组已被删除" msgstr "组已被删除"
#: assets/templates/assets/asset_group_list.html:90 #: assets/templates/assets/asset_group_list.html:86
#: assets/templates/assets/asset_group_list.html:95 #: assets/templates/assets/asset_group_list.html:91
msgid "Group Delete" msgid "Group Delete"
msgstr "删除" msgstr "删除"
#: assets/templates/assets/asset_group_list.html:94 #: assets/templates/assets/asset_group_list.html:90
msgid "Group deleting failed." msgid "Group deleting failed."
msgstr "删除失败" msgstr "删除失败"
#: assets/templates/assets/asset_group_list.html:157 #: assets/templates/assets/asset_group_list.html:153
msgid "The selected asset groups has been updated successfully." msgid "The selected asset groups has been updated successfully."
msgstr "更新成功" msgstr "更新成功"
#: assets/templates/assets/asset_group_list.html:158 #: assets/templates/assets/asset_group_list.html:154
msgid "AssetGroup Updated" msgid "AssetGroup Updated"
msgstr "资产组更新" msgstr "资产组更新"
@ -1021,52 +1024,47 @@ msgstr "导出"
msgid "Create asset" msgid "Create asset"
msgstr "创建资产" msgstr "创建资产"
#: assets/templates/assets/asset_list.html:35 #: assets/templates/assets/asset_list.html:34
#: assets/templates/assets/user_asset_list.html:23
msgid "Env"
msgstr "环境"
#: assets/templates/assets/asset_list.html:36
#: assets/templates/assets/user_asset_list.html:24 #: assets/templates/assets/user_asset_list.html:24
msgid "Hardware" msgid "Hardware"
msgstr "硬件" msgstr "硬件"
#: assets/templates/assets/asset_list.html:48 #: assets/templates/assets/asset_list.html:46
#: users/templates/users/user_list.html:37 #: users/templates/users/user_list.html:37
msgid "Delete selected" msgid "Delete selected"
msgstr "批量删除" msgstr "批量删除"
#: assets/templates/assets/asset_list.html:49 #: assets/templates/assets/asset_list.html:47
#: users/templates/users/user_list.html:38 #: users/templates/users/user_list.html:38
msgid "Update selected" msgid "Update selected"
msgstr "批量更新" msgstr "批量更新"
#: assets/templates/assets/asset_list.html:50 #: assets/templates/assets/asset_list.html:48
#: users/templates/users/user_list.html:39 #: users/templates/users/user_list.html:39
msgid "Deactive selected" msgid "Deactive selected"
msgstr "禁用所选" msgstr "禁用所选"
#: assets/templates/assets/asset_list.html:51 #: assets/templates/assets/asset_list.html:49
#: users/templates/users/user_list.html:40 #: users/templates/users/user_list.html:40
msgid "Active selected" msgid "Active selected"
msgstr "激活所选" msgstr "激活所选"
#: assets/templates/assets/asset_list.html:210 #: assets/templates/assets/asset_list.html:216
msgid "This will delete the selected assets !!!" msgid "This will delete the selected assets !!!"
msgstr "删除选择资产" msgstr "删除选择资产"
# msgid "Deleted!" # msgid "Deleted!"
# msgstr "删除" # msgstr "删除"
#: assets/templates/assets/asset_list.html:218 #: assets/templates/assets/asset_list.html:224
msgid "Asset Deleted." msgid "Asset Deleted."
msgstr "已被删除" msgstr "已被删除"
#: assets/templates/assets/asset_list.html:219 #: assets/templates/assets/asset_list.html:225
#: assets/templates/assets/asset_list.html:224 #: assets/templates/assets/asset_list.html:230
msgid "Asset Delete" msgid "Asset Delete"
msgstr "删除" msgstr "删除"
#: assets/templates/assets/asset_list.html:223 #: assets/templates/assets/asset_list.html:229
msgid "Asset Deleting failed." msgid "Asset Deleting failed."
msgstr "删除失败" msgstr "删除失败"
@ -1202,6 +1200,10 @@ msgstr "删除系统用户"
msgid "System Users Deleting failed." msgid "System Users Deleting failed."
msgstr "系统用户删除失败" 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:26
msgid "Connective" msgid "Connective"
msgstr "连接性" msgstr "连接性"
@ -1238,16 +1240,16 @@ msgstr "已经存在"
msgid "Cluster list" msgid "Cluster list"
msgstr "集群列表" msgstr "集群列表"
#: assets/views/cluster.py:42 assets/views/cluster.py:70 #: assets/views/cluster.py:42 assets/views/cluster.py:65
#: assets/views/system_user.py:96 #: assets/views/system_user.py:96
msgid "assets" msgid "assets"
msgstr "资产管理" msgstr "资产管理"
#: assets/views/cluster.py:71 #: assets/views/cluster.py:66
msgid "Update Cluster" msgid "Update Cluster"
msgstr "更新Cluster" msgstr "更新Cluster"
#: assets/views/cluster.py:86 #: assets/views/cluster.py:81
msgid "Cluster detail" msgid "Cluster detail"
msgstr "集群详情" msgstr "集群详情"
@ -1293,82 +1295,119 @@ msgstr "<b>%(name)s</b> 创建成功"
msgid "<b>%(name)s</b> was updated successfully" msgid "<b>%(name)s</b> was updated successfully"
msgstr "<b>%(name)s</b> 更新成功" msgstr "<b>%(name)s</b> 更新成功"
#: common/forms.py:64 #: common/fields.py:25
#, fuzzy
#| msgid "Not a valid ssh public key"
msgid "Not a valid json"
msgstr "ssh密钥不合法"
#: common/fields.py:27
msgid "Not a string type"
msgstr ""
#: common/forms.py:70
msgid "Current SITE URL" msgid "Current SITE URL"
msgstr "当前站点URL" msgstr "当前站点URL"
#: common/forms.py:68 #: common/forms.py:74
msgid "User Guide URL" msgid "User Guide URL"
msgstr "用户向导URL" msgstr "用户向导URL"
#: common/forms.py:69 #: common/forms.py:75
msgid "User first login update profile done redirect to it" msgid "User first login update profile done redirect to it"
msgstr "用户第一次登录修改profile后重定向到地址" msgstr "用户第一次登录修改profile后重定向到地址"
#: common/forms.py:72 #: common/forms.py:78
msgid "Email Subject Prefix" msgid "Email Subject Prefix"
msgstr "Email主题前缀" msgstr "Email主题前缀"
#: common/forms.py:76 #: common/forms.py:85
msgid "Enable LDAP Auth"
msgstr "二次验证"
#: common/forms.py:82
msgid "SMTP host" msgid "SMTP host"
msgstr "SMTP主机" msgstr "SMTP主机"
#: common/forms.py:84 #: common/forms.py:87
msgid "SMTP port" msgid "SMTP port"
msgstr "SMTP端口" msgstr "SMTP端口"
#: common/forms.py:86 #: common/forms.py:89
msgid "SMTP user" msgid "SMTP user"
msgstr "SMTP账号" msgstr "SMTP账号"
#: common/forms.py:89 #: common/forms.py:92
msgid "SMTP password" msgid "SMTP password"
msgstr "SMTP密码" msgstr "SMTP密码"
#: common/forms.py:90 #: common/forms.py:93
msgid "Some provider use token except password" msgid "Some provider use token except password"
msgstr "一些邮件提供商需要输入的是Token" msgstr "一些邮件提供商需要输入的是Token"
#: common/forms.py:93 common/forms.py:130 #: common/forms.py:96 common/forms.py:133
msgid "Use SSL" msgid "Use SSL"
msgstr "使用SSL" msgstr "使用SSL"
#: common/forms.py:94 #: common/forms.py:97
msgid "If SMTP port is 465, may be select" msgid "If SMTP port is 465, may be select"
msgstr "如果SMTP端口是465通常需要启用SSL" msgstr "如果SMTP端口是465通常需要启用SSL"
#: common/forms.py:97 #: common/forms.py:100
msgid "Use TLS" msgid "Use TLS"
msgstr "使用TLS" msgstr "使用TLS"
#: common/forms.py:98 #: common/forms.py:101
msgid "If SMTP port is 587, may be select" msgid "If SMTP port is 587, may be select"
msgstr "如果SMTP端口是587通常需要启用TLS" msgstr "如果SMTP端口是587通常需要启用TLS"
#: common/forms.py:104 #: common/forms.py:107
msgid "LDAP server" msgid "LDAP server"
msgstr "LDAP地址" msgstr "LDAP地址"
#: common/forms.py:107 #: common/forms.py:110
msgid "Bind DN" msgid "Bind DN"
msgstr "绑定DN" msgstr "绑定DN"
#: common/forms.py:114 #: common/forms.py:117
msgid "User OU" msgid "User OU"
msgstr "用户OU" msgstr "用户OU"
#: common/forms.py:117 #: common/forms.py:120
msgid "User search filter" msgid "User search filter"
msgstr "用户过滤器" msgstr "用户过滤器"
#: common/forms.py:120 #: common/forms.py:123
msgid "User attr map" msgid "User attr map"
msgstr "LDAP属性映射" msgstr "LDAP属性映射"
#: common/forms.py:143
msgid "List sort by"
msgstr "资产列表排序"
#: common/forms.py:146
msgid "Heartbeat interval"
msgstr "心跳间隔"
#: common/forms.py:146 ops/models.py:32
msgid "Units: seconds"
msgstr "单位: 秒"
#: common/forms.py:149
msgid "Password auth"
msgstr "密码认证"
#: common/forms.py:152
msgid "Public key auth"
msgstr "秘钥认证"
#: common/forms.py:155 common/templates/common/terminal_setting.html:58
#: terminal/forms.py:21 terminal/models.py:19
msgid "Command storage"
msgstr "命令存储"
#: common/forms.py:156
msgid ""
"Set terminal storage setting, `default` is the using as default,You can set "
"other storage and some terminal using"
msgstr "设置终端命令存储default是默认用的存储方式"
#: common/mixins.py:29 #: common/mixins.py:29
msgid "is discard" msgid "is discard"
msgstr "" msgstr ""
@ -1377,55 +1416,61 @@ msgstr ""
msgid "discard time" msgid "discard time"
msgstr "" msgstr ""
#: common/models.py:26 #: common/models.py:27
msgid "Value" msgid "Value"
msgstr "值" msgstr "值"
#: common/models.py:27 #: common/models.py:29
msgid "Enabled" msgid "Enabled"
msgstr "启用" msgstr "启用"
#: common/templates/common/basic_setting.html:15 #: common/templates/common/basic_setting.html:15
#: common/templates/common/email_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:15
#: common/templates/common/terminal_setting.html:38 common/views.py:21
msgid "Basic setting" msgid "Basic setting"
msgstr "基本设置" msgstr "基本设置"
#: common/templates/common/basic_setting.html:18 #: common/templates/common/basic_setting.html:18
#: common/templates/common/email_setting.html:18 #: common/templates/common/email_setting.html:18
#: common/templates/common/ldap_setting.html:18 common/views.py:45 #: common/templates/common/ldap_setting.html:18
#: common/templates/common/terminal_setting.html:18 common/views.py:47
msgid "Email setting" msgid "Email setting"
msgstr "邮件设置" msgstr "邮件设置"
#: common/templates/common/basic_setting.html:21 #: common/templates/common/basic_setting.html:21
#: common/templates/common/email_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:21 common/views.py:73
msgid "LDAP setting" msgid "LDAP setting"
msgstr "LDAP设置" msgstr "LDAP设置"
#: common/templates/common/basic_setting.html:55 #: common/templates/common/basic_setting.html:24
#: common/templates/common/email_setting.html:55 #: common/templates/common/email_setting.html:24
#: common/templates/common/ldap_setting.html:55 #: common/templates/common/ldap_setting.html:24
#: common/templates/common/terminal_setting.html:24 common/views.py:102
msgid "Terminal setting"
msgstr "终端设置"
#: common/templates/common/email_setting.html:58
#: common/templates/common/ldap_setting.html:58
msgid "Test connection" msgid "Test connection"
msgstr "测试连接" msgstr "测试连接"
#: common/views.py:17 common/views.py:44 common/views.py:69 #: common/views.py:20 common/views.py:46 common/views.py:72 common/views.py:101
#: templates/_nav.html:69 #: templates/_nav.html:69
msgid "Settings" msgid "Settings"
msgstr "系统设置" msgstr "系统设置"
#: common/views.py:30 common/views.py:55 common/views.py:80 #: common/views.py:31 common/views.py:57 common/views.py:85 common/views.py:113
msgid "Update setting successfully" msgid "Update setting successfully, please restart program"
msgstr "更新设置成功" msgstr "更新设置成功, 请手动重启程序"
#: ops/models.py:32 #: ops/models.py:32
msgid "Interval" msgid "Interval"
msgstr "间隔" msgstr "间隔"
#: ops/models.py:32
msgid "Units: seconds"
msgstr "单位: 秒"
#: ops/models.py:33 #: ops/models.py:33
msgid "Crontab" msgid "Crontab"
msgstr "Crontab" msgstr "Crontab"
@ -1569,7 +1614,7 @@ msgstr "执行历史"
#: ops/templates/ops/adhoc_history.html:52 #: ops/templates/ops/adhoc_history.html:52
#: ops/templates/ops/adhoc_history_detail.html:58 #: 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:124
#: terminal/templates/terminal/session_list.html:77 #: terminal/templates/terminal/session_list.html:77
msgid "Date start" msgid "Date start"
msgstr "开始日期" msgstr "开始日期"
@ -1686,18 +1731,18 @@ msgid "Task run history"
msgstr "执行历史" msgstr "执行历史"
#: perms/forms.py:16 users/forms.py:147 users/forms.py:152 users/forms.py:164 #: perms/forms.py:16 users/forms.py:147 users/forms.py:152 users/forms.py:164
#: users/forms.py:195 #: users/forms.py:194
msgid "Select users" msgid "Select users"
msgstr "选择用户" msgstr "选择用户"
#: perms/forms.py:18 perms/models.py:15 #: perms/forms.py:18 perms/models.py:15
#: perms/templates/perms/asset_permission_create_update.html:36 #: perms/templates/perms/asset_permission_create_update.html:36
#: perms/templates/perms/asset_permission_list.html:26 templates/_nav.html:12 #: perms/templates/perms/asset_permission_list.html:26 templates/_nav.html:12
#: terminal/backends/command/models.py:10 terminal/models.py:92 #: terminal/backends/command/models.py:10 terminal/models.py:115
#: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:32
#: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33 #: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:191 #: terminal/templates/terminal/session_list.html:71 users/forms.py:190
#: users/models/user.py:30 users/templates/users/user_group_detail.html:78 #: users/models/user.py:30 users/templates/users/user_group_detail.html:78
#: users/views/user.py:337 #: users/views/user.py:337
msgid "User" msgid "User"
@ -1842,16 +1887,12 @@ msgstr "资产授权包含用户"
msgid "Asset permission asset list" msgid "Asset permission asset list"
msgstr "资产组授权包含资产" msgstr "资产组授权包含资产"
#: templates/_header_bar.html:14
msgid "Welcome to use Jumpserver system"
msgstr "欢迎使用Jumpserver开源跳板机系统"
#: templates/_header_bar.html:18 #: templates/_header_bar.html:18
msgid "Help" msgid "Help"
msgstr "帮助" msgstr "帮助"
#: templates/_header_bar.html:33 templates/_nav_user.html:9 #: templates/_header_bar.html:32 templates/_nav_user.html:9
#: users/templates/users/_user.html:42 #: users/templates/users/_user.html:36
#: users/templates/users/user_password_update.html:37 #: users/templates/users/user_password_update.html:37
#: users/templates/users/user_profile.html:17 #: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37 #: users/templates/users/user_profile_update.html:37
@ -1860,24 +1901,24 @@ msgstr "帮助"
msgid "Profile" msgid "Profile"
msgstr "个人信息" msgstr "个人信息"
#: templates/_header_bar.html:37 #: templates/_header_bar.html:36
msgid "Admin page" msgid "Admin page"
msgstr "管理页面" msgstr "管理页面"
#: templates/_header_bar.html:39 #: templates/_header_bar.html:38
msgid "User page" msgid "User page"
msgstr "用户页面" msgstr "用户页面"
#: templates/_header_bar.html:42 #: templates/_header_bar.html:41
msgid "Logout" msgid "Logout"
msgstr "注销登录" msgstr "注销登录"
#: templates/_header_bar.html:46 users/templates/users/login.html:42 #: templates/_header_bar.html:45 users/templates/users/login.html:42
#: users/templates/users/login.html:61 #: users/templates/users/login.html:61
msgid "Login" msgid "Login"
msgstr "登录" msgstr "登录"
#: templates/_header_bar.html:59 templates/_nav.html:4 #: templates/_header_bar.html:58 templates/_nav.html:4
msgid "Dashboard" msgid "Dashboard"
msgstr "仪表盘" msgstr "仪表盘"
@ -1912,8 +1953,8 @@ msgid "Close"
msgstr "关闭" msgstr "关闭"
#: templates/_nav.html:9 users/views/group.py:28 users/views/group.py:44 #: 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:194 #: users/views/group.py:62 users/views/group.py:79 users/views/login.py:197
#: users/views/login.py:243 users/views/user.py:57 users/views/user.py:72 #: 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: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 #: users/views/user.py:318 users/views/user.py:355 users/views/user.py:377
msgid "Users" msgid "Users"
@ -1952,7 +1993,7 @@ msgstr "在线会话"
msgid "Session offline" msgid "Session offline"
msgstr "离线会话" msgstr "离线会话"
#: templates/_nav.html:53 terminal/models.py:99 #: templates/_nav.html:53 terminal/models.py:122
#: terminal/templates/terminal/command_list.html:55 #: terminal/templates/terminal/command_list.html:55
#: terminal/templates/terminal/command_list.html:71 #: terminal/templates/terminal/command_list.html:71
#: terminal/templates/terminal/session_detail.html:48 #: terminal/templates/terminal/session_detail.html:48
@ -1993,64 +2034,68 @@ msgstr "输出"
msgid "Session" msgid "Session"
msgstr "会话" msgstr "会话"
#: terminal/forms.py:15 #: terminal/forms.py:27
msgid "Coco ssh listen port" msgid "Coco ssh listen port"
msgstr "SSH 监听端口" msgstr "SSH 监听端口"
#: terminal/forms.py:16 #: terminal/forms.py:28
msgid "Coco http/ws listen port" msgid "Coco http/ws listen port"
msgstr "Http/Websocket 监听端口" msgstr "Http/Websocket 监听端口"
#: terminal/models.py:15 #: terminal/models.py:16
msgid "Remote Address" msgid "Remote Address"
msgstr "远端地址" msgstr "远端地址"
#: terminal/models.py:16 #: terminal/models.py:17
msgid "SSH Port" msgid "SSH Port"
msgstr "SSH端口" msgstr "SSH端口"
#: terminal/models.py:17 #: terminal/models.py:18
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP端口" msgstr "HTTP端口"
#: terminal/models.py:68 #: terminal/models.py:20
msgid "Replay storage"
msgstr "录像存储"
#: terminal/models.py:91
msgid "Session Online" msgid "Session Online"
msgstr "在线会话" msgstr "在线会话"
#: terminal/models.py:69 #: terminal/models.py:92
msgid "CPU Usage" msgid "CPU Usage"
msgstr "CPU使用" msgstr "CPU使用"
#: terminal/models.py:70 #: terminal/models.py:93
msgid "Memory Used" msgid "Memory Used"
msgstr "内存使用" msgstr "内存使用"
#: terminal/models.py:71 #: terminal/models.py:94
msgid "Connections" msgid "Connections"
msgstr "连接数" msgstr "连接数"
#: terminal/models.py:72 #: terminal/models.py:95
msgid "Threads" msgid "Threads"
msgstr "线程数" msgstr "线程数"
#: terminal/models.py:73 #: terminal/models.py:96
msgid "Boot Time" msgid "Boot Time"
msgstr "运行时间" msgstr "运行时间"
#: terminal/models.py:96 terminal/templates/terminal/session_list.html:74 #: terminal/models.py:119 terminal/templates/terminal/session_list.html:74
#: terminal/templates/terminal/terminal_detail.html:47 #: terminal/templates/terminal/terminal_detail.html:47
msgid "Remote addr" msgid "Remote addr"
msgstr "远端地址" msgstr "远端地址"
#: terminal/models.py:98 terminal/templates/terminal/session_list.html:100 #: terminal/models.py:121 terminal/templates/terminal/session_list.html:100
msgid "Replay" msgid "Replay"
msgstr "回放" msgstr "回放"
#: terminal/models.py:102 #: terminal/models.py:125
msgid "Date end" msgid "Date end"
msgstr "结束日期" msgstr "结束日期"
#: terminal/models.py:119 #: terminal/models.py:142
msgid "Args" msgid "Args"
msgstr "参数" msgstr "参数"
@ -2404,7 +2449,7 @@ msgstr "忘记密码"
#: users/templates/users/forgot_password.html:33 #: users/templates/users/forgot_password.html:33
msgid "Input your email, that will send a mail to your" msgid "Input your email, that will send a mail to your"
msgstr "输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中" msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中"
#: users/templates/users/login.html:47 #: users/templates/users/login.html:47
msgid "Captcha invalid" msgid "Captcha invalid"
@ -2473,10 +2518,8 @@ msgid "An e-mail has been sent to the user\\'s mailbox."
msgstr "已发送邮件到用户邮箱" msgstr "已发送邮件到用户邮箱"
#: users/templates/users/user_detail.html:334 #: users/templates/users/user_detail.html:334
msgid "" msgid "This will reset the user password and send a reset mail"
"This will reset the user's password. A password-reset email will be sent to " msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
"the user\\'s mailbox."
msgstr "重设密码邮件将会发送到用户邮箱"
#: users/templates/users/user_detail.html:348 #: users/templates/users/user_detail.html:348
msgid "" msgid ""
@ -2490,8 +2533,8 @@ msgid "Reset SSH public key"
msgstr "重置SSH密钥" msgstr "重置SSH密钥"
#: users/templates/users/user_detail.html:359 #: users/templates/users/user_detail.html:359
msgid "This will reset the user\\" msgid "This will reset the user public key and send a reset mail"
msgstr "重置" msgstr "将会失效用户当前秘钥,并发送重置邮件到用户邮箱"
#: users/templates/users/user_detail.html:376 #: users/templates/users/user_detail.html:376
#: users/templates/users/user_profile.html:170 #: users/templates/users/user_profile.html:170
@ -2740,48 +2783,48 @@ msgstr "编辑用户组"
msgid "Please enable cookies and try again." msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie" msgstr "设置你的浏览器支持cookie"
#: users/views/login.py:84 #: users/views/login.py:87
msgid "Logout success" msgid "Logout success"
msgstr "退出登录成功" msgstr "退出登录成功"
#: users/views/login.py:85 #: users/views/login.py:88
msgid "Logout success, return login page" msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面" msgstr "退出登录成功,返回到登录页面"
#: users/views/login.py:101 #: users/views/login.py:104
msgid "Email address invalid, please input again" msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入" msgstr "邮箱地址错误,重新输入"
#: users/views/login.py:114 #: users/views/login.py:117
msgid "Send reset password message" msgid "Send reset password message"
msgstr "发送重置密码邮件" msgstr "发送重置密码邮件"
#: users/views/login.py:115 #: users/views/login.py:118
msgid "Send reset password mail success, login your mail box and follow it " msgid "Send reset password mail success, login your mail box and follow it "
msgstr "" msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#: users/views/login.py:129 #: users/views/login.py:132
msgid "Reset password success" msgid "Reset password success"
msgstr "重置密码成功" msgstr "重置密码成功"
#: users/views/login.py:130 #: users/views/login.py:133
msgid "Reset password success, return to login page" msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面" msgstr "重置密码成功,返回到登录页面"
#: users/views/login.py:147 users/views/login.py:160 #: users/views/login.py:150 users/views/login.py:163
msgid "Token invalid or expired" msgid "Token invalid or expired"
msgstr "Token错误或失效" msgstr "Token错误或失效"
#: users/views/login.py:156 #: users/views/login.py:159
msgid "Password not same" msgid "Password not same"
msgstr "密码不一致" msgstr "密码不一致"
#: users/views/login.py:194 #: users/views/login.py:197
msgid "First login" msgid "First login"
msgstr "首次登陆" msgstr "首次登陆"
#: users/views/login.py:244 #: users/views/login.py:247
msgid "Login log list" msgid "Login log list"
msgstr "登录日志" msgstr "登录日志"
@ -2813,5 +2856,17 @@ msgstr "密码更新"
msgid "Public key update" msgid "Public key update"
msgstr "秘钥更新" msgstr "秘钥更新"
#~ msgid "Add command storage"
#~ msgstr "添加命令存储"
#~ msgid "Welcome to use Jumpserver system"
#~ msgstr "欢迎使用Jumpserver开源跳板机系统"
#~ msgid "This will reset the user\\"
#~ msgstr "重置"
#~ msgid "Enable LDAP Auth"
#~ msgstr "LDAP认证"
#~ msgid "Connect" #~ msgid "Connect"
#~ msgstr "连接" #~ msgstr "连接"

View File

@ -165,7 +165,7 @@ class AdHoc(models.Model):
if item and isinstance(item, list): if item and isinstance(item, list):
self._tasks = json.dumps(item) self._tasks = json.dumps(item)
else: else:
raise SyntaxError('Tasks should be a list') raise SyntaxError('Tasks should be a list: {}'.format(item))
@property @property
def hosts(self): def hosts(self):

View File

@ -16,6 +16,8 @@ def update_or_create_ansible_task(
run_as_admin=False, run_as="", become_info=None, run_as_admin=False, run_as="", become_info=None,
created_by=None, created_by=None,
): ):
if not hosts or not tasks or not task_name:
return
defaults = { defaults = {
'name': task_name, 'name': task_name,
@ -41,7 +43,7 @@ def update_or_create_ansible_task(
new_adhoc.become = become_info new_adhoc.become = become_info
if not adhoc or adhoc != new_adhoc: 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() new_adhoc.save()
task.latest_adhoc = new_adhoc task.latest_adhoc = new_adhoc
created = True created = True

View File

@ -303,7 +303,7 @@ div.dataTables_wrapper div.dataTables_filter {
.profile-element div:first-child { .profile-element div:first-child {
line-height: 60px; line-height: 60px;
width: 70px; /*width: 70px;*/
float: left; float: left;
text-align: center; text-align: center;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -4,6 +4,6 @@
<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg"> <img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">
</div> </div>
<div> <div>
<strong>Copyright</strong> 北京堆栈科技有限公司 &copy; 2014-2017 <strong>Copyright</strong> 北京堆栈科技有限公司 &copy; 2014-2018
</div> </div>
</div> </div>

View File

@ -10,9 +10,9 @@
<!--</form>--> <!--</form>-->
</div> </div>
<ul class="nav navbar-top-links navbar-right"> <ul class="nav navbar-top-links navbar-right">
<li> {# <li>#}
<span class="m-r-sm text-muted welcome-message">{% trans 'Welcome to use Jumpserver system' %}</span> {# <span class="m-r-sm text-muted welcome-message">{% trans 'Welcome to use Jumpserver system' %}</span>#}
</li> {# </li>#}
<li class="dropdown"> <li class="dropdown">
<a class="dropdown-toggle count-info" data-toggle="dropdown" href="#"> <a class="dropdown-toggle count-info" data-toggle="dropdown" href="#">
<span class="m-r-sm text-muted welcome-message">{% trans 'Help' %}</span> <span class="m-r-sm text-muted welcome-message">{% trans 'Help' %}</span>
@ -22,11 +22,10 @@
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<a data-toggle="dropdown" class="dropdown-toggle" href="#"> <a data-toggle="dropdown" class="dropdown-toggle" href="#">
<span class="m-r-sm text-muted welcome-message"> <span class="m-r-sm text-muted welcome-message">
<img alt="image" class="img-circle" width="40" height="40" src="{{ request.user.avatar_url }}"/> <img alt="image" class="img-circle" width="30" height="30" src="{{ request.user.avatar_url }}"/>
<strong class="font-bold"> {{ request.user.name }} <span style="font-size: 13px;font-weight: 400"> {{ request.user.name }}
<span style="color: #8095a8"></span>
<b class="caret"></b> <b class="caret"></b>
</strong> </span>
</span> </span>
</a> </a>
<ul class="dropdown-menu animated fadeInRight m-t-xs"> <ul class="dropdown-menu animated fadeInRight m-t-xs">

View File

@ -2,11 +2,8 @@
{% load i18n %} {% load i18n %}
<li class="nav-header"> <li class="nav-header">
<div class="dropdown profile-element"> <div class="dropdown profile-element">
<div> <div href="http://www.jumpserver.org" target="_blank">
<img alt="image" height="40" src="/static/img/logo.png"/> <img alt="image" height="55" src="/static/img/logo-text.png" style="margin-left: 10px"/>
</div>
<div>
<a href="http://www.jumpserver.org" target="_blank">Jumpserver</a>
</div> </div>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>

View File

@ -52,7 +52,7 @@
Copyright Jumpserver.org Copyright Jumpserver.org
</div> </div>
<div class="col-md-6 text-right"> <div class="col-md-6 text-right">
<small>2014-2017</small> <small>2014-2018</small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -164,7 +164,7 @@
{% for login in last_login_ten %} {% for login in last_login_ten %}
<div class="feed-element"> <div class="feed-element">
<a href="#" class="pull-left"> <a href="#" class="pull-left">
<img alt="image" class="img-circle" src="/static/img/root.png"> <img alt="image" class="img-circle" src="{% static 'img/avatar/user.png' %}">
</a> </a>
<div class="media-body "> <div class="media-body ">
{% ifequal login.is_finished 0 %} {% ifequal login.is_finished 0 %}

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from collections import OrderedDict from collections import OrderedDict
import copy
import logging import logging
import os import os
import uuid import uuid
@ -21,7 +20,8 @@ from .serializers import TerminalSerializer, StatusSerializer, \
SessionSerializer, TaskSerializer, ReplaySerializer SessionSerializer, TaskSerializer, ReplaySerializer
from .hands import IsSuperUserOrAppUser, IsAppUser, \ from .hands import IsSuperUserOrAppUser, IsAppUser, \
IsSuperUserOrAppUserOrUserReadonly IsSuperUserOrAppUserOrUserReadonly
from .backends import get_command_store, SessionCommandSerializer from .backends import get_command_store, get_multi_command_store, \
SessionCommandSerializer
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
@ -141,7 +141,9 @@ class StatusViewSet(viewsets.ModelViewSet):
session = serializer.save() session = serializer.save()
return session return session
else: else:
msg = "session data is not valid {}".format(serializer.errors) msg = "session data is not valid {}: {}".format(
serializer.errors, str(serializer.data)
)
logger.error(msg) logger.error(msg)
return None return None
@ -195,6 +197,7 @@ class CommandViewSet(viewsets.ViewSet):
""" """
command_store = get_command_store() command_store = get_command_store()
multi_command_storage = get_multi_command_store()
serializer_class = SessionCommandSerializer serializer_class = SessionCommandSerializer
permission_classes = (IsSuperUserOrAppUser,) permission_classes = (IsSuperUserOrAppUser,)
@ -215,7 +218,7 @@ class CommandViewSet(viewsets.ViewSet):
return Response({"msg": msg}, status=401) return Response({"msg": msg}, status=401)
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
queryset = list(self.command_store.all()) queryset = self.multi_command_storage.filter()
serializer = self.serializer_class(queryset, many=True) serializer = self.serializer_class(queryset, many=True)
return Response(serializer.data) return Response(serializer.data)
@ -258,3 +261,13 @@ class SessionReplayViewSet(viewsets.ViewSet):
return redirect(url) return redirect(url)
else: else:
return HttpResponseNotFound() return HttpResponseNotFound()
class TerminalConfig(APIView):
permission_classes = (IsAppUser,)
def get(self, request):
user = request.user
terminal = user.terminal
configs = terminal.config
return Response(configs, status=200)

View File

@ -2,9 +2,39 @@ from importlib import import_module
from django.conf import settings from django.conf import settings
from .command.serializers import SessionCommandSerializer from .command.serializers import SessionCommandSerializer
TYPE_ENGINE_MAPPING = {
'elasticsearch': 'terminal.backends.command.es',
}
def get_command_store(): def get_command_store():
command_engine = import_module(settings.COMMAND_STORAGE_BACKEND) params = settings.COMMAND_STORAGE
command_store = command_engine.CommandStore() engine_class = import_module(params['ENGINE'])
return command_store storage = engine_class.CommandStore(params)
return storage
def get_terminal_command_store():
storage_list = {}
for name, params in settings.TERMINAL_COMMAND_STORAGE.items():
tp = params['TYPE']
if tp == 'server':
storage = get_command_store()
else:
if not TYPE_ENGINE_MAPPING.get(tp):
raise AssertionError("Command storage type should in {}".format(
', '.join(TYPE_ENGINE_MAPPING.keys()))
)
engine_class = import_module(TYPE_ENGINE_MAPPING[tp])
storage = engine_class.CommandStore(params)
storage_list[name] = storage
return storage_list
def get_multi_command_store():
from .command.multi import CommandStore
storage_list = get_terminal_command_store().values()
storage = CommandStore(storage_list)
return storage

View File

@ -19,3 +19,9 @@ class CommandBase(object):
input=None, session=None): input=None, session=None):
pass pass
@abc.abstractmethod
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
pass

View File

@ -8,7 +8,7 @@ from .base import CommandBase
class CommandStore(CommandBase): class CommandStore(CommandBase):
def __init__(self): def __init__(self, params):
from terminal.models import Command from terminal.models import Command
self.model = Command self.model = Command
@ -37,7 +37,9 @@ class CommandStore(CommandBase):
)) ))
return self.model.objects.bulk_create(_commands) return self.model.objects.bulk_create(_commands)
def filter(self, date_from=None, date_to=None, @staticmethod
def make_filter_kwargs(
date_from=None, date_to=None,
user=None, asset=None, system_user=None, user=None, asset=None, system_user=None,
input=None, session=None): input=None, session=None):
filter_kwargs = {} filter_kwargs = {}
@ -60,10 +62,28 @@ class CommandStore(CommandBase):
if session: if session:
filter_kwargs['session'] = session filter_kwargs['session'] = session
return filter_kwargs
def filter(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
filter_kwargs = self.make_filter_kwargs(
date_from=date_from, date_to=date_to, user=user,
asset=asset, system_user=system_user, input=input,
session=session,
)
queryset = self.model.objects.filter(**filter_kwargs) queryset = self.model.objects.filter(**filter_kwargs)
return queryset return [command.to_dict() for command in queryset]
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
filter_kwargs = self.make_filter_kwargs(
date_from=date_from, date_to=date_to, user=user,
asset=asset, system_user=system_user, input=input,
session=session,
)
count = self.model.objects.filter(**filter_kwargs).count()
return count
def all(self):
"""返回所有数据"""
return self.model.objects.iterator()

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
#
from jms_es_sdk import ESStore
from .base import CommandBase
class CommandStore(CommandBase, ESStore):
def __init__(self, params):
hosts = params.get('HOSTS', ['http://localhost'])
ESStore.__init__(self, hosts=hosts)
def save(self, command):
return ESStore.save(self, command)
def bulk_save(self, commands):
return ESStore.bulk_save(self, commands)
def filter(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
data = ESStore.filter(
self, date_from=date_from, date_to=date_to,
user=user, asset=asset, system_user=system_user,
input=input, session=session
)
return [item["_source"] for item in data["hits"] if item]
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
amount = ESStore.count(
self, date_from=date_from, date_to=date_to,
user=user, asset=asset, system_user=system_user,
input=input, session=session
)
return amount

View File

@ -18,5 +18,26 @@ class AbstractSessionCommand(models.Model):
class Meta: class Meta:
abstract = True abstract = True
@classmethod
def from_dict(cls, d):
self = cls()
for k, v in d.items():
setattr(self, k, v)
return self
@classmethod
def from_multi_dict(cls, l):
commands = []
for d in l:
command = cls.from_dict(d)
commands.append(command)
return commands
def to_dict(self):
d = {}
for field in self._meta.fields:
d[field.name] = getattr(self, field.name)
return d
def __str__(self): def __str__(self):
return self.input return self.input

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
from .base import CommandBase
class CommandStore(CommandBase):
def __init__(self, storage_list):
self.storage_list = storage_list
def filter(self, **kwargs):
queryset = []
for storage in self.storage_list:
queryset.extend(storage.filter(**kwargs))
return sorted(queryset, key=lambda command: command["timestamp"], reverse=True)
def count(self, **kwargs):
amount = 0
for storage in self.storage_list:
amount += storage.count(**kwargs)
return amount
def save(self, command):
pass
def bulk_save(self, commands):
pass

View File

@ -2,15 +2,27 @@
# #
from django import forms from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .models import Terminal from .models import Terminal
def get_all_command_storage():
# storage_choices = []
from common.models import Setting
Setting.refresh_all_settings()
for k, v in settings.TERMINAL_COMMAND_STORAGE.items():
yield (k, k)
class TerminalForm(forms.ModelForm): class TerminalForm(forms.ModelForm):
command_storage = forms.ChoiceField(choices=get_all_command_storage(),
label=_("Command storage"))
class Meta: class Meta:
model = Terminal model = Terminal
fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment'] fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment', 'command_storage']
help_texts = { help_texts = {
'ssh_port': _("Coco ssh listen port"), 'ssh_port': _("Coco ssh listen port"),
'http_port': _("Coco http/ws listen port"), 'http_port': _("Coco http/ws listen port"),

View File

@ -4,6 +4,7 @@ import uuid
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from users.models import User from users.models import User
from .backends.command.models import AbstractSessionCommand from .backends.command.models import AbstractSessionCommand
@ -15,6 +16,8 @@ class Terminal(models.Model):
remote_addr = models.CharField(max_length=128, verbose_name=_('Remote Address')) remote_addr = models.CharField(max_length=128, verbose_name=_('Remote Address'))
ssh_port = models.IntegerField(verbose_name=_('SSH Port'), default=2222) ssh_port = models.IntegerField(verbose_name=_('SSH Port'), default=2222)
http_port = models.IntegerField(verbose_name=_('HTTP Port'), default=5000) http_port = models.IntegerField(verbose_name=_('HTTP Port'), default=5000)
command_storage = models.CharField(max_length=128, verbose_name=_("Command storage"), default='default')
replay_storage = models.CharField(max_length=128, verbose_name=_("Replay storage"), default='default')
user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True, on_delete=models.CASCADE) user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True, on_delete=models.CASCADE)
is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted') is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted')
is_deleted = models.BooleanField(default=False) is_deleted = models.BooleanField(default=False)
@ -33,6 +36,26 @@ class Terminal(models.Model):
self.user.is_active = active self.user.is_active = active
self.user.save() self.user.save()
def get_common_storage(self):
storage_all = settings.TERMINAL_COMMAND_STORAGE
if self.command_storage in storage_all:
storage = storage_all.get(self.command_storage)
else:
storage = storage_all.get('default')
return {"TERMINAL_COMMAND_STORAGE": storage}
def get_replay_storage(self):
pass
@property
def config(self):
configs = {}
for k in dir(settings):
if k.startswith('TERMINAL'):
configs[k] = getattr(settings, k)
configs.update(self.get_common_storage())
return configs
def create_app_user(self): def create_app_user(self):
random = uuid.uuid4().hex[:6] random = uuid.uuid4().hex[:6]
user, access_key = User.create_app_user(name="{}-{}".format(self.name, random), comment=self.comment) user, access_key = User.create_app_user(name="{}-{}".format(self.name, random), comment=self.comment)

View File

@ -5,7 +5,7 @@ from django.utils import timezone
from rest_framework import serializers from rest_framework import serializers
from .models import Terminal, Status, Session, Task from .models import Terminal, Status, Session, Task
from .backends import get_command_store from .backends import get_multi_command_store
class TerminalSerializer(serializers.ModelSerializer): class TerminalSerializer(serializers.ModelSerializer):
@ -43,14 +43,14 @@ class TerminalSerializer(serializers.ModelSerializer):
class SessionSerializer(serializers.ModelSerializer): class SessionSerializer(serializers.ModelSerializer):
command_amount = serializers.SerializerMethodField() command_amount = serializers.SerializerMethodField()
command_store = get_command_store() command_store = get_multi_command_store()
class Meta: class Meta:
model = Session model = Session
fields = '__all__' fields = '__all__'
def get_command_amount(self, obj): def get_command_amount(self, obj):
return len(self.command_store.filter(session=obj.session)) return self.command_store.count(session=str(obj.id))
class StatusSerializer(serializers.ModelSerializer): class StatusSerializer(serializers.ModelSerializer):

View File

@ -13,10 +13,6 @@ RUNNING = False
logger = get_logger(__file__) logger = get_logger(__file__)
@shared_task
@register_as_period_task(interval=3600)
@after_app_ready_start
@after_app_shutdown_clean
def set_session_info_cache(): def set_session_info_cache():
logger.debug("") logger.debug("")
from .utils import get_session_asset_list, get_session_user_list, \ from .utils import get_session_asset_list, get_session_user_list, \

View File

@ -12,6 +12,7 @@
{% bootstrap_field form.remote_addr layout="horizontal" %} {% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.ssh_port layout="horizontal" %} {% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.http_port layout="horizontal" %} {% bootstrap_field form.http_port layout="horizontal" %}
{% bootstrap_field form.command_storage layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %} {% bootstrap_field form.comment layout="horizontal" %}
</form> </form>

View File

@ -35,6 +35,7 @@
{% bootstrap_field form.remote_addr layout="horizontal" %} {% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.ssh_port layout="horizontal" %} {% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.http_port layout="horizontal" %} {% bootstrap_field form.http_port layout="horizontal" %}
{% bootstrap_field form.command_storage layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>

View File

@ -1,13 +1,12 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from django import template from django import template
from ..backends import get_command_store from ..backends import get_multi_command_store
register = template.Library() register = template.Library()
command_store = get_command_store() command_store = get_multi_command_store()
@register.filter @register.filter
def get_session_command_amount(session_id): def get_session_command_amount(session_id):
return len(command_store.filter(session=str(session_id))) return command_store.count(session=session_id)

View File

@ -20,7 +20,8 @@ urlpatterns = [
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$', url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$',
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}), api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
name='session-replay'), name='session-replay'),
url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key') url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key'),
url(r'^v1/terminal/config', api.TerminalConfig.as_view(), name='terminal-config'),
] ]
urlpatterns += router.urls urlpatterns += router.urls

View File

@ -9,10 +9,10 @@ from django.utils.translation import ugettext as _
from common.mixins import DatetimeSearchMixin from common.mixins import DatetimeSearchMixin
from ..models import Command from ..models import Command
from .. import utils from .. import utils
from ..backends import get_command_store from ..backends import get_multi_command_store
__all__ = ['CommandListView'] __all__ = ['CommandListView']
command_store = get_command_store() common_storage = get_multi_command_store()
class CommandListView(DatetimeSearchMixin, ListView): class CommandListView(DatetimeSearchMixin, ListView):
@ -39,7 +39,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
filter_kwargs['system_user'] = self.system_user filter_kwargs['system_user'] = self.system_user
if self.command: if self.command:
filter_kwargs['input'] = self.command filter_kwargs['input'] = self.command
queryset = command_store.filter(**filter_kwargs) queryset = common_storage.filter(**filter_kwargs)
return queryset return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

View File

@ -10,7 +10,7 @@ from django.conf import settings
from users.utils import AdminUserRequiredMixin from users.utils import AdminUserRequiredMixin
from common.mixins import DatetimeSearchMixin from common.mixins import DatetimeSearchMixin
from ..models import Session, Command, Terminal from ..models import Session, Command, Terminal
from ..backends import get_command_store from ..backends import get_multi_command_store
from .. import utils from .. import utils
@ -19,7 +19,7 @@ __all__ = [
'SessionDetailView', 'SessionDetailView',
] ]
command_store = get_command_store() command_store = get_multi_command_store()
class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):

View File

@ -1,14 +1,14 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from rest_framework import generics from rest_framework import generics
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from .serializers import UserSerializer, UserGroupSerializer, \ from .serializers import UserSerializer, UserGroupSerializer, \
UserGroupUpdateMemeberSerializer, UserPKUpdateSerializer, \ UserGroupUpdateMemeberSerializer, UserPKUpdateSerializer, \
UserUpdateGroupSerializer UserUpdateGroupSerializer, ChangeUserPasswordSerializer
from .tasks import write_login_log_async from .tasks import write_login_log_async
from .models import User, UserGroup from .models import User, UserGroup
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
@ -24,10 +24,21 @@ class UserViewSet(CustomFilterMixin, BulkModelViewSet):
queryset = User.objects.exclude(role="App") queryset = User.objects.exclude(role="App")
# queryset = User.objects.all().exclude(role="App").order_by("date_joined") # queryset = User.objects.all().exclude(role="App").order_by("date_joined")
serializer_class = UserSerializer serializer_class = UserSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser, IsAuthenticated)
filter_fields = ('username', 'email', 'name', 'id') filter_fields = ('username', 'email', 'name', 'id')
class ChangeUserPasswordApi(generics.RetrieveUpdateAPIView):
permission_classes = (IsSuperUser,)
queryset = User.objects.all()
serializer_class = ChangeUserPasswordSerializer
def perform_update(self, serializer):
user = self.get_object()
user.password_raw = serializer.validated_data["password"]
user.save()
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView): class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserUpdateGroupSerializer serializer_class = UserUpdateGroupSerializer
@ -37,6 +48,7 @@ class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
class UserResetPasswordApi(generics.UpdateAPIView): class UserResetPasswordApi(generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserSerializer serializer_class = UserSerializer
permission_classes = (IsAuthenticated,)
def perform_update(self, serializer): def perform_update(self, serializer):
# Note: we are not updating the user object here. # Note: we are not updating the user object here.
@ -128,7 +140,11 @@ class UserAuthApi(APIView):
user_agent = request.data.get('HTTP_USER_AGENT', '') user_agent = request.data.get('HTTP_USER_AGENT', '')
if not login_ip: if not login_ip:
login_ip = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get("REMOTE_ADDR") x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
if x_forwarded_for:
login_ip = x_forwarded_for[0]
else:
login_ip = request.META.get("REMOTE_ADDR")
user, msg = check_user_valid( user, msg = check_user_valid(
username=username, password=password, username=username, password=password,

View File

@ -172,7 +172,6 @@ class UserBulkUpdateForm(forms.ModelForm):
if self.data.get(field) is not None: if self.data.get(field) is not None:
changed_fields.append(field) changed_fields.append(field)
print(changed_fields)
cleaned_data = {k: v for k, v in self.cleaned_data.items() cleaned_data = {k: v for k, v in self.cleaned_data.items()
if k in changed_fields} if k in changed_fields}
users = cleaned_data.pop('users', '') users = cleaned_data.pop('users', '')

View File

@ -6,7 +6,7 @@
Other module of this app shouldn't connect with other app. Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-2017 by Jumpserver Team. :copyright: (c) 2014-2018 by Jumpserver Team.
:license: GPL v2, see LICENSE for more details. :license: GPL v2, see LICENSE for more details.
""" """

View File

@ -71,3 +71,9 @@ class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer):
model = UserGroup model = UserGroup
fields = ['id', 'users'] fields = ['id', 'users']
class ChangeUserPasswordSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['password']

View File

@ -11,8 +11,8 @@
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data"> <form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<h3>{% trans 'Account' %}</h3> <h3>{% trans 'Account' %}</h3>
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.name layout="horizontal" %} {% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.email layout="horizontal" %} {% bootstrap_field form.email layout="horizontal" %}
{% bootstrap_field form.groups layout="horizontal" %} {% bootstrap_field form.groups layout="horizontal" %}
@ -32,12 +32,6 @@
<span class="help-block ">{{ form.date_expired.errors }}</span> <span class="help-block ">{{ form.date_expired.errors }}</span>
</div> </div>
</div> </div>
{# <div class="form-group">#}
{# <label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>#}
{# <div class="col-sm-8">#}
{# {{ form.enable_otp }}#}
{# </div>#}
{# </div>#}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3> <h3>{% trans 'Profile' %}</h3>
{% bootstrap_field form.phone layout="horizontal" %} {% bootstrap_field form.phone layout="horizontal" %}

View File

@ -55,7 +55,7 @@
Copyright Jumpserver.org Copyright Jumpserver.org
</div> </div>
<div class="col-md-6 text-right"> <div class="col-md-6 text-right">
<small>© 2014-2017</small> <small>© 2014-2018</small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -78,7 +78,7 @@
Copyright Jumpserver.org Copyright Jumpserver.org
</div> </div>
<div class="col-md-6 text-right"> <div class="col-md-6 text-right">
<small>© 2014-2017</small> <small>© 2014-2018</small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -74,7 +74,7 @@
Copyright Jumpserver.org Copyright Jumpserver.org
</div> </div>
<div class="col-md-6 text-right"> <div class="col-md-6 text-right">
<small>© 2014-2017</small> <small>© 2014-2018</small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -24,8 +24,9 @@
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a> <a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-user"> <a class="btn btn-outline {% if request.user != user_object and user_object.username != "admin" %} btn-danger btn-delete-user {% else %} disabled {% endif %}">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %} <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
@ -128,7 +129,7 @@
<td><span class="pull-right"> <td><span class="pull-right">
<div class="switch"> <div class="switch">
<div class="onoffswitch"> <div class="onoffswitch">
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active"> <input type="checkbox" {% if user_object.is_active %} checked {% endif %} {% if request.user == user_object %} disabled {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active"> <label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span> <span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span> <span class="onoffswitch-switch"></span>
@ -156,7 +157,7 @@
<td>{% trans 'Send reset password mail' %}:</td> <td>{% trans 'Send reset password mail' %}:</td>
<td> <td>
<span class="pull-right"> <span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn-reset-password" style="width: 54px">{% trans 'Send' %}</button> <button type="button" class="btn btn-primary btn-xs" {% if request.user == user_object %} disabled="disabled" {% endif %} id="btn-reset-password" style="width: 54px">{% trans 'Send' %}</button>
</span> </span>
</td> </td>
</tr> </tr>
@ -164,7 +165,7 @@
<td>{% trans 'Send reset ssh key mail' %}:</td> <td>{% trans 'Send reset ssh key mail' %}:</td>
<td> <td>
<span class="pull-right"> <span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn-reset-pk" style="width: 54px;">{% trans 'Send' %}</button> <button type="button" class="btn btn-primary btn-xs" {% if request.user == user_object %} disabled="disabled" {% endif %} id="btn-reset-pk" style="width: 54px;">{% trans 'Send' %}</button>
</span> </span>
</td> </td>
</tr> </tr>
@ -331,7 +332,7 @@ $(document).ready(function() {
} }
swal({ swal({
title: "{% trans 'Are you sure?' %}", title: "{% trans 'Are you sure?' %}",
text: "{% trans "This will reset the user's password. A password-reset email will be sent to the user\'s mailbox." %}", text: "{% trans "This will reset the user password and send a reset mail"%}",
type: "warning", type: "warning",
showCancelButton: true, showCancelButton: true,
confirmButtonColor: "#DD6B55", confirmButtonColor: "#DD6B55",
@ -356,7 +357,7 @@ $(document).ready(function() {
} }
swal({ swal({
title: "{% trans 'Are you sure?' %}", title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will reset the user\'s public key.' %}", text: "{% trans 'This will reset the user public key and send a reset mail' %}",
type: "warning", type: "warning",
showCancelButton: true, showCancelButton: true,
confirmButtonColor: "#DD6B55", confirmButtonColor: "#DD6B55",

View File

@ -76,7 +76,7 @@ function initTable() {
var update_btn = '<a href="{% url "users:user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('00000000-0000-0000-0000-000000000000', cellData); var update_btn = '<a href="{% url "users:user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('00000000-0000-0000-0000-000000000000', cellData);
var del_btn = ""; var del_btn = "";
if (rowData.id === 1 || rowData.username === "admin" || rowData.username === "{{ user.username }}") { if (rowData.id === 1 || rowData.username === "admin" || rowData.username === "{{ request.user.username }}") {
del_btn = '<a class="btn btn-xs btn-danger m-l-xs" disabled>{% trans "Delete" %}</a>' del_btn = '<a class="btn btn-xs btn-danger m-l-xs" disabled>{% trans "Delete" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData) .replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', rowData.name); .replace('99991938', rowData.name);

View File

@ -19,6 +19,8 @@ urlpatterns = [
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'), url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'), 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/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/$',
api.ChangeUserPasswordApi.as_view(), name='change-user-password'),
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/reset/$', url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/reset/$',
api.UserResetPasswordApi.as_view(), name='user-reset-password'), api.UserResetPasswordApi.as_view(), name='user-reset-password'),
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/pubkey/reset/$', url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/pubkey/reset/$',

View File

@ -180,7 +180,9 @@ def validate_ip(ip):
def write_login_log(username, type='', ip='', user_agent=''): def write_login_log(username, type='', ip='', user_agent=''):
if not (ip and validate_ip(ip)): if not (ip and validate_ip(ip)):
ip = '0.0.0.0' ip = ip[:15]
city = "Unknown"
else:
city = get_ip_city(ip) city = get_ip_city(ip)
LoginLog.objects.create( LoginLog.objects.create(
username=username, type=type, username=username, type=type,

View File

@ -53,8 +53,11 @@ class UserLoginView(FormView):
if not self.request.session.test_cookie_worked(): if not self.request.session.test_cookie_worked():
return HttpResponse(_("Please enable cookies and try again.")) return HttpResponse(_("Please enable cookies and try again."))
auth_login(self.request, form.get_user()) auth_login(self.request, form.get_user())
login_ip = self.request.META.get('HTTP_X_FORWARDED_FOR') or \ x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
self.request.META.get('REMOTE_ADDR', '') if x_forwarded_for and x_forwarded_for[0]:
login_ip = x_forwarded_for[0]
else:
login_ip = self.request.META.get('REMOTE_ADDR', '')
user_agent = self.request.META.get('HTTP_USER_AGENT', '') user_agent = self.request.META.get('HTTP_USER_AGENT', '')
write_login_log_async.delay( write_login_log_async.delay(
self.request.user.username, type='W', self.request.user.username, type='W',

View File

@ -1,129 +1 @@
## Jumpserver v0.4.0 版本安装详细过程 More see [安装文档](https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7)
### 环境
- 系统: 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

View File

@ -56,6 +56,8 @@ uritemplate==3.0.0
urllib3==1.22 urllib3==1.22
vine==1.1.4 vine==1.1.4
gunicorn==19.7.1 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.0
ephem==3.7.6.0 ephem==3.7.6.0
python-gssapi==0.6.4 python-gssapi==0.6.4
jms-es-sdk