Commit 76df1de6 authored by ibuler's avatar ibuler

[Bugfix] 修复一些明显的bug

parent 5a929721
...@@ -44,9 +44,11 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet): ...@@ -44,9 +44,11 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet):
else: else:
assets_granted = get_user_granted_assets(self.request.user) assets_granted = get_user_granted_assets(self.request.user)
queryset = self.queryset.filter(id__in=[asset.id for asset in assets_granted]) queryset = self.queryset.filter(id__in=[asset.id for asset in assets_granted])
cluster_id = self.request.query_params.get('cluster_id') cluster_id = self.request.query_params.get('cluster_id')
asset_group_id = self.request.query_params.get('asset_group_id') asset_group_id = self.request.query_params.get('asset_group_id')
admin_user_id = self.request.query_params.get('admin_user_id') admin_user_id = self.request.query_params.get('admin_user_id')
if cluster_id: if cluster_id:
queryset = queryset.filter(cluster__id=cluster_id) queryset = queryset.filter(cluster__id=cluster_id)
if asset_group_id: if asset_group_id:
......
# coding:utf-8 # coding:utf-8
import uuid
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
...@@ -9,7 +8,6 @@ from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, ...@@ -9,7 +8,6 @@ from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen,
logger = get_logger(__file__) logger = get_logger(__file__)
from rest_framework import serializers
class AssetCreateForm(forms.ModelForm): class AssetCreateForm(forms.ModelForm):
...@@ -57,11 +55,11 @@ class AssetUpdateForm(forms.ModelForm): ...@@ -57,11 +55,11 @@ class AssetUpdateForm(forms.ModelForm):
class AssetBulkUpdateForm(forms.ModelForm): class AssetBulkUpdateForm(forms.ModelForm):
assets = forms.MultipleChoiceField( assets = forms.ModelMultipleChoiceField(
required=True, required=True,
help_text='* required', help_text='* required',
label=_('Select assets'), label=_('Select assets'),
choices=[(asset.id, asset.hostname) for asset in Asset.objects.all()], queryset=Asset.objects.all(),
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={ attrs={
'class': 'select2', 'class': 'select2',
...@@ -94,10 +92,9 @@ class AssetBulkUpdateForm(forms.ModelForm): ...@@ -94,10 +92,9 @@ class AssetBulkUpdateForm(forms.ModelForm):
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}
print(cleaned_data) assets = cleaned_data.pop('assets')
assets_id = cleaned_data.pop('assets')
groups = cleaned_data.pop('groups', []) groups = cleaned_data.pop('groups', [])
assets = Asset.objects.filter(id__in=assets_id) assets = Asset.objects.filter(id__in=[asset.id for asset in assets])
assets.update(**cleaned_data) assets.update(**cleaned_data)
if groups: if groups:
for asset in assets: for asset in assets:
...@@ -175,16 +172,18 @@ class AdminUserForm(forms.ModelForm): ...@@ -175,16 +172,18 @@ class AdminUserForm(forms.ModelForm):
password = None password = None
if private_key: if private_key:
public_key = ssh_pubkey_gen(private_key) public_key = ssh_pubkey_gen(private_key, password=password)
admin_user.set_auth(password=password, public_key=public_key, private_key=private_key) admin_user.set_auth(password=password, public_key=public_key, private_key=private_key)
return admin_user return admin_user
def clean_private_key_file(self): def clean_private_key_file(self):
private_key_file = self.cleaned_data['private_key_file'] private_key_file = self.cleaned_data['private_key_file']
password = self.cleaned_data['password']
if private_key_file: if private_key_file:
private_key = private_key_file.read() private_key = private_key_file.read()
if not validate_ssh_private_key(private_key): if not validate_ssh_private_key(private_key, password):
raise forms.ValidationError(_('Invalid private key')) raise forms.ValidationError(_('Invalid private key'))
return private_key return private_key
return private_key_file return private_key_file
......
...@@ -18,10 +18,6 @@ __all__ = ['Asset'] ...@@ -18,10 +18,6 @@ __all__ = ['Asset']
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_default_cluster():
return Cluster.initial()
class Asset(models.Model): class Asset(models.Model):
# Todo: Move them to settings # Todo: Move them to settings
STATUS_CHOICES = ( STATUS_CHOICES = (
...@@ -48,7 +44,7 @@ class Asset(models.Model): ...@@ -48,7 +44,7 @@ class Asset(models.Model):
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
port = models.IntegerField(default=22, verbose_name=_('Port')) port = models.IntegerField(default=22, verbose_name=_('Port'))
groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups'))
cluster = models.ForeignKey(Cluster, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('Cluster'),) cluster = models.ForeignKey(Cluster, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('Cluster'))
is_active = models.BooleanField(default=True, verbose_name=_('Is active')) is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, default='Server', verbose_name=_('Asset type'),) type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, default='Server', verbose_name=_('Asset type'),)
env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),) env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),)
......
...@@ -10,7 +10,6 @@ from django.db import models ...@@ -10,7 +10,6 @@ from django.db import models
import logging import logging
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .user import SystemUser
__all__ = ['AssetGroup'] __all__ = ['AssetGroup']
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
......
...@@ -66,16 +66,15 @@ class AssetUser(models.Model): ...@@ -66,16 +66,15 @@ class AssetUser(models.Model):
@property @property
def private_key_file(self): def private_key_file(self):
if not self.private_key: if not self.private_key_obj:
return None return None
project_dir = settings.PROJECT_DIR project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp') tmp_dir = os.path.join(project_dir, 'tmp')
key_str = signer.unsign(self._private_key) key_str = signer.unsign(self._private_key)
key_name = md5(key_str.encode('utf-8')).hexdigest() key_name = '.' + md5(key_str.encode('utf-8')).hexdigest()
key_path = os.path.join(tmp_dir, key_name) key_path = os.path.join(tmp_dir, key_name)
if not os.path.exists(key_path): if not os.path.exists(key_path):
with open(key_path, 'w') as f: self.private_key_obj.write_private_key_file(key_path)
f.write(key_str)
os.chmod(key_path, 0o400) os.chmod(key_path, 0o400)
return key_path return key_path
...@@ -105,7 +104,6 @@ class AssetUser(models.Model): ...@@ -105,7 +104,6 @@ class AssetUser(models.Model):
update_fields.append('_public_key') update_fields.append('_public_key')
if update_fields: if update_fields:
print(update_fields)
self.save(update_fields=update_fields) self.save(update_fields=update_fields)
def auto_gen_auth(self): def auto_gen_auth(self):
...@@ -149,7 +147,11 @@ class AdminUser(AssetUser): ...@@ -149,7 +147,11 @@ class AdminUser(AssetUser):
@property @property
def become_pass(self): def become_pass(self):
return signer.unsign(self._become_pass) password = signer.unsign(self._become_pass)
if password:
return password
else:
return ""
@become_pass.setter @become_pass.setter
def become_pass(self, password): def become_pass(self, password):
...@@ -199,7 +201,7 @@ class SystemUser(AssetUser): ...@@ -199,7 +201,7 @@ class SystemUser(AssetUser):
('K', 'Public key'), ('K', 'Public key'),
) )
cluster = models.ManyToManyField('assets.Cluster', blank=True, verbose_name=_("Cluster")) cluster = models.ManyToManyField('assets.Cluster', blank=True, verbose_name=_("Cluster"))
priority = models.IntegerField(default=10, verbose_name=_("Priority")) # Todo: If user granted more priority user, default will be login as the hign priority = models.IntegerField(default=10, verbose_name=_("Priority"))
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol')) protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo')) sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo'))
......
...@@ -402,12 +402,6 @@ def push_system_user_on_auth_change(sender, instance=None, update_fields=None, * ...@@ -402,12 +402,6 @@ def push_system_user_on_auth_change(sender, instance=None, update_fields=None, *
push_system_user_to_cluster_assets.delay(instance, task_name) push_system_user_to_cluster_assets.delay(instance, task_name)
@receiver(on_app_ready, dispatch_uid="my_unique_identifier")
def test_admin_user_on_app_ready(sender, **kwargs):
logger.debug("Receive app ready signal, test admin connectability")
test_admin_user_connectability_period.delay()
celery_app.conf['CELERYBEAT_SCHEDULE'].update( celery_app.conf['CELERYBEAT_SCHEDULE'].update(
{ {
'update_assets_hardware_period': { 'update_assets_hardware_period': {
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'Public IP' %}:</td> <td>{% trans 'Public IP' %}:</td>
<td><b>{{ asset.public_ip }}</b></td> <td><b>{{ asset.public_ip|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Port' %}:</td> <td>{% trans 'Port' %}:</td>
...@@ -74,12 +74,12 @@ ...@@ -74,12 +74,12 @@
{% if asset.admin_user_avail %} {% if asset.admin_user_avail %}
<td><b>{{ asset.admin_user_avail.name }}</b></td> <td><b>{{ asset.admin_user_avail.name }}</b></td>
{% else %} {% else %}
<td><b>None</b></td> <td></td>
{% endif %} {% endif %}
</tr> </tr>
<tr> <tr>
<td>{% trans 'Remote card IP' %}:</td> <td>{% trans 'Remote card IP' %}:</td>
<td><b>{{ asset.remote_card_ip }}</b></td> <td><b>{{ asset.remote_card_ip|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Cluster' %}:</td> <td>{% trans 'Cluster' %}:</td>
...@@ -87,39 +87,39 @@ ...@@ -87,39 +87,39 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'Cabinet number' %}:</td> <td>{% trans 'Cabinet number' %}:</td>
<td><b>{{ asset.cabinet_no }}</b></td> <td><b>{{ asset.cabinet_no|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Cabinet position' %}:</td> <td>{% trans 'Cabinet position' %}:</td>
<td><b>{{ asset.cabinet_pos }}</b></td> <td><b>{{ asset.cabinet_pos|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Vendor' %}:</td> <td>{% trans 'Vendor' %}:</td>
<td><b>{{ asset.vendor }}</b></td> <td><b>{{ asset.vendor|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Model' %}:</td> <td>{% trans 'Model' %}:</td>
<td><b>{{ asset.model }}</b></td> <td><b>{{ asset.model|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'CPU' %}:</td> <td>{% trans 'CPU' %}:</td>
<td><b>{{ asset.cpu_model }} {{ asset.cpu_count }}*{{ asset.cpu_cores }}</b></td> <td><b>{{ asset.cpu_model|default:"" }} {{ asset.cpu_count|default:"" }}*{{ asset.cpu_cores|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Memory' %}:</td> <td>{% trans 'Memory' %}:</td>
<td><b>{{ asset.memory }}</b></td> <td><b>{{ asset.memory|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Disk' %}:</td> <td>{% trans 'Disk' %}:</td>
<td><b>{{ asset.disk_total }}</b></td> <td><b>{{ asset.disk_total|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Platform' %}:</td> <td>{% trans 'Platform' %}:</td>
<td><b>{{ asset.platform }}</b></td> <td><b>{{ asset.platform|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'OS' %}:</td> <td>{% trans 'OS' %}:</td>
<td><b>{{ asset.os }} {{ asset.os_version }} {{ asset.os_arch }}</b></td> <td><b>{{ asset.os|default:"" }} {{ asset.os_version|default:"" }} {{ asset.os_arch|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Asset status' %}:</td> <td>{% trans 'Asset status' %}:</td>
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'Is active' %}:</td> <td>{% trans 'Is active' %}:</td>
<td><b>{{ asset.is_active }}</b></td> <td><b>{{ asset.is_active|yesno:"Yes,No" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Asset type' %}:</td> <td>{% trans 'Asset type' %}:</td>
...@@ -139,11 +139,11 @@ ...@@ -139,11 +139,11 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'Serial number' %}:</td> <td>{% trans 'Serial number' %}:</td>
<td><b>{{ asset.sn }}</b></td> <td><b>{{ asset.sn|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Asset number' %}:</td> <td>{% trans 'Asset number' %}:</td>
<td><b>{{ asset.number }}</b></td> <td><b>{{ asset.number|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Created by' %}:</td> <td>{% trans 'Created by' %}:</td>
......
...@@ -64,8 +64,7 @@ ...@@ -64,8 +64,7 @@
<script src="{% static 'js/jquery.form.min.js' %}"></script> <script src="{% static 'js/jquery.form.min.js' %}"></script>
<script type="text/javascript"> <script type="text/javascript">
function initTable() {
$(document).ready(function(){
var options = { var options = {
ele: $('#asset_list_table'), ele: $('#asset_list_table'),
columnDefs: [ columnDefs: [
...@@ -103,48 +102,51 @@ $(document).ready(function(){ ...@@ -103,48 +102,51 @@ $(document).ready(function(){
{data: "is_active" }, {data: "is_connective"}, {data: "id" }], {data: "is_active" }, {data: "is_connective"}, {data: "id" }],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
var table = jumpserver.initDataTable(options); return jumpserver.initDataTable(options);
}
$('.btn_export').click(function () { $(document).ready(function(){
var assets = []; initTable();
var rows = table.rows('.selected').data(); })
$.each(rows, function (index, obj) { .on('click', '#btn_export', function () {
assets.push(obj.id) var $data_table = $('#asset_list_table').DataTable();
}); var rows = $data_table.rows('.selected').data();
console.log(assets); var assets = [];
$.ajax({ $.each(rows, function (index, obj) {
url: "{% url "assets:asset-export" %}", assets.push(obj.id)
method: 'POST',
data: JSON.stringify({assets_id: assets}),
dataType: "json",
success: function (data, textStatus) {
window.open(data.redirect)
},
error: function () {
toastr.error('Export failed');
}
})
}); });
$('#btn_asset_import').click(function() { $.ajax({
var $form = $('#fm_asset_import'); url: "{% url "assets:asset-export" %}",
$form.find('.help-block').remove(); method: 'POST',
function success (data) { data: JSON.stringify({assets_id: assets}),
if (data.valid === false) { dataType: "json",
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_assets')); success: function (data, textStatus) {
} else { window.open(data.redirect)
$('#id_created').html(data.created_info); },
$('#id_created_detail').html(data.created.join(', ')); error: function () {
$('#id_updated').html(data.updated_info); toastr.error('Export failed');
$('#id_updated_detail').html(data.updated.join(', '));
$('#id_failed').html(data.failed_info);
$('#id_failed_detail').html(data.failed.join(', '));
var $data_table = $('#asset_list_table').DataTable();
$data_table.ajax.reload();
}
} }
$form.ajaxSubmit({success: success});
}) })
}) })
.on('click', '#btn_import', function () {
var $form = $('#fm_asset_import');
$form.find('.help-block').remove();
function success (data) {
if (data.valid === false) {
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_assets'));
} else {
$('#id_created').html(data.created_info);
$('#id_created_detail').html(data.created.join(', '));
$('#id_updated').html(data.updated_info);
$('#id_updated_detail').html(data.updated.join(', '));
$('#id_failed').html(data.failed_info);
$('#id_failed_detail').html(data.failed.join(', '));
var $data_table = $('#asset_list_table').DataTable();
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
.on('click', '.btn_asset_delete', function () { .on('click', '.btn_asset_delete', function () {
var $this = $(this); var $this = $(this);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block table_search %}{% endblock %} {% block table_search %}{% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-r-5"> <div class="uc pull-left m-r-5">
......
{% extends '_base_list.html' %} {% extends '_base_list.html' %}
{% load i18n %} {% load i18n %}
{% block help_message %}
<div class="alert alert-info help-message">
系统用户是 用户登录资产(服务器)时使用的用户,如 web, sa, dba等具有特殊功能的用户。系统用户创建时,如果选择了自动推送
Jumpserver会使用ansible自动推送到系统用户所在集群的资产中,如果资产(交换机)不支持ansible, 请手动填写账号密码。
</div>
{% endblock %}
{% block table_search %} {% block table_search %}
{% endblock %} {% endblock %}
......
...@@ -5,26 +5,22 @@ ...@@ -5,26 +5,22 @@
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style>
.custom{
margin-right:5px;
}
#modal .modal-body { max-height: 200px; }
</style>
{% endblock %} {% endblock %}
{% block content_left_head %}{% endblock %} {% block content_left_head %}{% endblock %}
{% block table_search %} {% block table_search %}
{# <div class="html5buttons">#} <div class="html5buttons">
{# <div class="dt-buttons btn-group">#} <div class="dt-buttons btn-group">
{# <a class="btn btn-default btn_export" tabindex="0">#} <a class="btn btn-default btn_import" data-toggle="modal" data-target="#asset_import_modal" tabindex="0">
{# <span>{% trans "Export" %}</span>#} <span>{% trans "Import" %}</span>
{# </a>#} </a>
{# </div>#} <a class="btn btn-default btn_export" tabindex="0">
{# </div>#} <span>{% trans "Export" %}</span>
</a>
</div>
</div>
{% endblock %} {% endblock %}
{% block table_container %} {% block table_container %}
<table class="table table-striped table-bordered table-hover " id="asset_list_table" > <table class="table table-striped table-bordered table-hover " id="asset_list_table" >
<thead> <thead>
...@@ -36,8 +32,8 @@ ...@@ -36,8 +32,8 @@
<th class="text-center">{% trans 'Type' %}</th> <th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'Env' %}</th> <th class="text-center">{% trans 'Env' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th> <th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Valid' %}</th> <th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Alive' %}</th> <th class="text-center">{% trans 'Connective' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
...@@ -48,216 +44,45 @@ ...@@ -48,216 +44,45 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script> <script src="{% static 'js/jquery.form.min.js' %}"></script>
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function(){
var options = {
ele: $('#asset_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 7, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 8, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}}
],
ajax_url: '{% url "api-assets:asset-list" %}',
columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware"},
{data: "is_active" }, {data: "is_active"}],
};
var table = jumpserver.initDataTable(options);
$('.btn_export').click(function () {
var assets = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
assets.push(obj.id)
});
console.log(assets);
$.ajax({
url: "{% url "assets:asset-export" %}",
method: 'POST',
data: JSON.stringify({assets_id: assets}),
dataType: "json",
success: function (data, textStatus) {
window.open(data.redirect)
},
error: function () {
toastr.error('Export failed');
}
})
});
$('#btn_asset_import').click(function() {
var $form = $('#fm_asset_import');
$form.find('.help-block').remove();
function success (data) {
if (data.valid === false) {
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_assets'));
} else {
$('#id_created').html(data.created_info);
$('#id_created_detail').html(data.created.join(', '));
$('#id_updated').html(data.updated_info);
$('#id_updated_detail').html(data.updated.join(', '));
$('#id_failed').html(data.failed_info);
$('#id_failed_detail').html(data.failed.join(', '));
var $data_table = $('#asset_list_table').DataTable();
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
})
.on('click', '.btn_asset_delete', function () {
var $this = $(this);
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-assets:asset-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
})
.on('click', '#btn_bulk_update', function () {
var action = $('#slct_bulk_update').val();
var $data_table = $('#asset_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (plain_id_list.length == 0) {
return false;
}
var the_url = "{% url 'api-assets:asset-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, asset_object) {
asset_object['is_active'] = false;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doActive() {
var body = $.each(id_list, function(index, asset_object) {
asset_object['is_active'] = true;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected assets !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success");
$('#asset_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'Asset Deleting failed.' %}";
swal("{% trans 'Asset Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
$data_table.ajax.reload();
jumpserver.checked = false;
});
}
function doUpdate() {
$('#asset_bulk_update_modal').modal('show');
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
case 'active':
doActive();
break;
default:
break;
}
})
.on('click', '#btn_asset_bulk_update', function () {
var json_data = $("#fm_asset_bulk_update").serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.type != '') {
body.type = json_data.type;
}
if (json_data.groups != undefined) {
body.groups = json_data.groups;
}
if (typeof body.groups === 'string') {
body.groups = [parseInt(body.groups)]
} else if(typeof body.groups === 'array') {
var new_groups = body.groups.map(Number);
body.groups = new_groups;
}
if (json_data.system_users != undefined) { function initTable() {
body.system_users = json_data.system_users; var options = {
} ele: $('#asset_list_table'),
if (typeof body.system_users === 'string') { columnDefs: [
body.system_users = [parseInt(body.system_users)] {targets: 1, createdCell: function (td, cellData, rowData) {
} else if(typeof body.system_users === 'array') { {% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
var new_users = body.system_users.map(Number); console.log('{{ the_url }}');
body.system_users = new_users; var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
} $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
if (json_data.tags != undefined) { {targets: 7, createdCell: function (td, cellData) {
body.tags = json_data.tags; if (!cellData) {
} $(td).html('<i class="fa fa-times text-danger"></i>')
if (typeof body.tags == 'string') { } else {
body.tags = [parseInt(body.tags)]; $(td).html('<i class="fa fa-check text-navy"></i>')
} else if (typeof body.tags === 'array') { }
var new_tags = body.tags.map(Number); }},
body.tags = new_tags; {targets: 8, createdCell: function (td, cellData) {
} if (cellData == 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
var $data_table = $('#asset_list_table').DataTable(); } else if (!cellData) {
var post_list = []; $(td).html('<i class="fa fa-circle text-danger"></i>')
$data_table.rows({selected: true}).every(function(){ } else {
var content = Object.assign({id: this.data().id}, body); $(td).html('<i class="fa fa-circle text-navy"></i>')
post_list.push(content); }
}); }}
if (post_list === []) { ],
return false ajax_url: '{% url "api-assets:asset-list" %}',
} columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
var the_url = "{% url 'api-assets:asset-list' %}"; {data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"},
var success = function() { {data: "is_active" }, {data: "is_connective"}],
var msg = "{% trans 'The selected assets has been updated successfully.' %}"; op_html: $('#actions').html()
swal("{% trans 'Asset Updated' %}", msg, "success");
$('#asset_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
}; };
console.log(JSON.stringify(post_list)); return jumpserver.initDataTable(options);
console.log(the_url); }
{# APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#}
$('#asset_bulk_update_modal').modal('hide'); $(document).ready(function(){
initTable();
}); });
</script> </script>
......
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
# #
from collections import defaultdict
from common.utils import get_object_or_none from common.utils import get_object_or_none
from .models import Asset, SystemUser from .models import Asset, SystemUser
...@@ -17,3 +18,12 @@ def get_system_user_by_name(name): ...@@ -17,3 +18,12 @@ def get_system_user_by_name(name):
return system_user return system_user
def check_assets_have_system_user(assets, system_users):
errors = defaultdict(list)
for system_user in system_users:
clusters = system_user.cluster.all()
for asset in assets:
if asset.cluster not in clusters:
errors[asset].append(system_user)
return errors
from __future__ import unicode_literals
from django.db import models from django.db import models
# Create your models here.
...@@ -195,11 +195,11 @@ def ssh_key_string_to_obj(text, password=None): ...@@ -195,11 +195,11 @@ def ssh_key_string_to_obj(text, password=None):
return key return key
def ssh_pubkey_gen(private_key=None, username='jumpserver', hostname='localhost'): def ssh_pubkey_gen(private_key=None, username='jumpserver', hostname='localhost', password=None):
if isinstance(private_key, bytes): if isinstance(private_key, bytes):
private_key = private_key.decode("utf-8") private_key = private_key.decode("utf-8")
if isinstance(private_key, string_types): if isinstance(private_key, string_types):
private_key = ssh_key_string_to_obj(private_key) private_key = ssh_key_string_to_obj(private_key, password=password)
if not isinstance(private_key, (paramiko.RSAKey, paramiko.DSSKey)): if not isinstance(private_key, (paramiko.RSAKey, paramiko.DSSKey)):
raise IOError('Invalid private key') raise IOError('Invalid private key')
...@@ -238,14 +238,14 @@ def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', h ...@@ -238,14 +238,14 @@ def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', h
raise IOError('These is error when generate ssh key.') raise IOError('These is error when generate ssh key.')
def validate_ssh_private_key(text): def validate_ssh_private_key(text, password=None):
if isinstance(text, bytes): if isinstance(text, bytes):
try: try:
text = text.decode("utf-8") text = text.decode("utf-8")
except UnicodeDecodeError: except UnicodeDecodeError:
return False return False
key = ssh_key_string_to_obj(text) key = ssh_key_string_to_obj(text, password=password)
if key is None: if key is None:
return False return False
else: else:
......
...@@ -59,7 +59,6 @@ INSTALLED_APPS = [ ...@@ -59,7 +59,6 @@ INSTALLED_APPS = [
'assets.apps.AssetsConfig', 'assets.apps.AssetsConfig',
'perms.apps.PermsConfig', 'perms.apps.PermsConfig',
'ops.apps.OpsConfig', 'ops.apps.OpsConfig',
# 'audits.apps.AuditsConfig',
'common.apps.CommonConfig', 'common.apps.CommonConfig',
'terminal.apps.TerminalConfig', 'terminal.apps.TerminalConfig',
'rest_framework', 'rest_framework',
......
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from collections import defaultdict
from ansible.plugins.callback import CallbackBase from ansible.plugins.callback import CallbackBase
from ansible.plugins.callback.default import CallbackModule from ansible.plugins.callback.default import CallbackModule
...@@ -21,9 +19,8 @@ class AdHocResultCallback(CallbackModule): ...@@ -21,9 +19,8 @@ class AdHocResultCallback(CallbackModule):
# "contacted": {"hostname",...}, # "contacted": {"hostname",...},
# "dark": {"hostname": {"task_name": {}, "task_name": {}},...,}, # "dark": {"hostname": {"task_name": {}, "task_name": {}},...,},
# } # }
self.results_raw = dict(ok=defaultdict(dict), failed=defaultdict(dict), self.results_raw = dict(ok={}, failed={}, unreachable={}, skipped={})
unreachable=defaultdict(dict), skipped=defaultdict(dict)) self.results_summary = dict(contacted=[], dark={})
self.results_summary = dict(contacted=[], dark=defaultdict(dict))
super().__init__() super().__init__()
def gather_result(self, t, res): def gather_result(self, t, res):
...@@ -34,8 +31,8 @@ class AdHocResultCallback(CallbackModule): ...@@ -34,8 +31,8 @@ class AdHocResultCallback(CallbackModule):
if self.results_raw[t].get(host): if self.results_raw[t].get(host):
self.results_raw[t][host][task_name] = task_result self.results_raw[t][host][task_name] = task_result
# else: else:
# self.results_raw[t][host] = {task_name: task_result} self.results_raw[t][host] = {task_name: task_result}
self.clean_result(t, host, task_name, task_result) self.clean_result(t, host, task_name, task_result)
def clean_result(self, t, host, task_name, task_result): def clean_result(self, t, host, task_name, task_result):
...@@ -45,10 +42,10 @@ class AdHocResultCallback(CallbackModule): ...@@ -45,10 +42,10 @@ class AdHocResultCallback(CallbackModule):
if host not in contacted: if host not in contacted:
contacted.append(host) contacted.append(host)
else: else:
# if dark.get(host): if dark.get(host):
dark[host][task_name] = task_result.values dark[host][task_name] = task_result.values
# else: else:
# dark[host] = {task_name: task_result} dark[host] = {task_name: task_result}
if host in contacted: if host in contacted:
contacted.remove(host) contacted.remove(host)
......
...@@ -32,6 +32,7 @@ class BaseHost(Host): ...@@ -32,6 +32,7 @@ class BaseHost(Host):
} }
""" """
self.host_data = host_data self.host_data = host_data
print(host_data)
hostname = host_data.get('hostname') or host_data.get('ip') hostname = host_data.get('hostname') or host_data.get('ip')
port = host_data.get('port') or 22 port = host_data.get('port') or 22
super().__init__(hostname, port) super().__init__(hostname, port)
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
<div class="input-group"> <div class="input-group">
<div class="input-group-btn"> <div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary"> <button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索 {% trans "Search" %}
</button> </button>
</div> </div>
</div> </div>
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
{% block table_head %} {% block table_head %}
<th class="text-center"></th> <th class="text-center"></th>
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'F/S/T' %}</th> <th class="text-center">{% trans 'Run times' %}</th>
<th class="text-center">{% trans 'Versions' %}</th> <th class="text-center">{% trans 'Versions' %}</th>
<th class="text-center">{% trans 'Hosts' %}</th> <th class="text-center">{% trans 'Hosts' %}</th>
<th class="text-center">{% trans 'Success' %}</th> <th class="text-center">{% trans 'Success' %}</th>
......
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
import re
import time import time
from django.utils import timezone from django.utils import timezone
from django.db import transaction
from common.utils import get_logger, get_object_or_none, get_short_uuid_str from common.utils import get_logger, get_object_or_none, get_short_uuid_str
from .ansible import AdHocRunner, CommandResultCallback from .ansible import AdHocRunner, CommandResultCallback
...@@ -58,6 +58,7 @@ def get_inventory(hostname_list, run_as_admin=False, run_as=None, become_info=No ...@@ -58,6 +58,7 @@ def get_inventory(hostname_list, run_as_admin=False, run_as=None, become_info=No
) )
@record_adhoc
def get_adhoc_runner(hostname_list, run_as_admin=False, run_as=None, become_info=None): def get_adhoc_runner(hostname_list, run_as_admin=False, run_as=None, become_info=None):
inventory = get_inventory( inventory = get_inventory(
hostname_list, run_as_admin=run_as_admin, hostname_list, run_as_admin=run_as_admin,
...@@ -67,7 +68,6 @@ def get_adhoc_runner(hostname_list, run_as_admin=False, run_as=None, become_info ...@@ -67,7 +68,6 @@ def get_adhoc_runner(hostname_list, run_as_admin=False, run_as=None, become_info
return runner return runner
@record_adhoc
def run_adhoc_object(adhoc, **options): def run_adhoc_object(adhoc, **options):
""" """
:param adhoc: Instance of AdHoc :param adhoc: Instance of AdHoc
...@@ -109,6 +109,8 @@ def create_or_update_task( ...@@ -109,6 +109,8 @@ def create_or_update_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
): ):
print(options)
print(task_name)
task = get_object_or_none(Task, name=task_name) task = get_object_or_none(Task, name=task_name)
if task is None: if task is None:
task = Task(name=task_name, created_by=created_by) task = Task(name=task_name, created_by=created_by)
...@@ -125,6 +127,7 @@ def create_or_update_task( ...@@ -125,6 +127,7 @@ def create_or_update_task(
if not adhoc or adhoc != new_adhoc: if not adhoc or adhoc != new_adhoc:
new_adhoc.save() new_adhoc.save()
task.latest_adhoc = new_adhoc task.latest_adhoc = new_adhoc
print("Return task")
return task return task
......
...@@ -38,3 +38,34 @@ class AssetPermissionForm(forms.ModelForm): ...@@ -38,3 +38,34 @@ class AssetPermissionForm(forms.ModelForm):
'user_groups': _('User or user group at least one required'), 'user_groups': _('User or user group at least one required'),
'asset_groups': _('Asset or Asset group at least one required'), 'asset_groups': _('Asset or Asset group at least one required'),
} }
def clean_system_users(self):
from assets.utils import check_assets_have_system_user
errors = []
assets = self.cleaned_data['assets']
asset_groups = self.cleaned_data['asset_groups']
system_users = self.cleaned_data['system_users']
error_data = check_assets_have_system_user(assets, system_users)
if error_data:
for asset, system_users in error_data.items():
msg = _("Asset {} not have [{}] system users, please check \n")
error = forms.ValidationError(msg.format(
asset.hostname,
", ".join(system_user.name for system_user in system_users)
))
errors.append(error)
for group in asset_groups:
msg = _("Asset {}: {} not have [{}] system users, please check")
assets = group.assets.all()
error_data = check_assets_have_system_user(assets, system_users)
for asset, system_users in error_data.items():
errors.append(msg.format(
group.name, asset.hostname,
", ".join(system_user.name for system_user in system_users)
))
if errors:
raise forms.ValidationError(errors)
return self.cleaned_data['system_users']
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
<div class="col-sm-9"> <div class="col-sm-9">
<div class="input-group date"> <div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span> <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}"> <input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d'|default:form.date_expired.value }}">
</div> </div>
<span class="help-block ">{{ form.date_expired.errors }}</span> <span class="help-block ">{{ form.date_expired.errors }}</span>
</div> </div>
......
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
{% block table_search %} {% block table_search %}
{% endblock %} {% endblock %}
{% block help_message %}
<div class="alert alert-info help-message">
提前规划好集群中的系统用户,授权时选择的资产(组内资产)必须存在该系统用户,否则可能无法成功登录
</div>
{% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-r-5"> <div class="uc pull-left m-r-5">
<a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary "> <a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary ">
......
...@@ -66,9 +66,7 @@ class MessageMixin: ...@@ -66,9 +66,7 @@ class MessageMixin:
return success_message return success_message
class AssetPermissionCreateView(AdminUserRequiredMixin, class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
MessageMixin,
CreateView):
model = AssetPermission model = AssetPermission
form_class = AssetPermissionForm form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html' template_name = 'perms/asset_permission_create_update.html'
...@@ -83,8 +81,19 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, ...@@ -83,8 +81,19 @@ class AssetPermissionCreateView(AdminUserRequiredMixin,
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'perms:asset-permission-detail',
kwargs={'pk': self.object.pk}
)
success_message = _(
'Create asset permission <a href="{url}"> {name} </a> '
'success.'.format(url=url, name=self.object.name)
)
return success_message
class AssetPermissionUpdateView(AdminUserRequiredMixin, MessageMixin, UpdateView): class AssetPermissionUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = AssetPermission model = AssetPermission
form_class = AssetPermissionForm form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html' template_name = 'perms/asset_permission_create_update.html'
...@@ -98,6 +107,17 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, MessageMixin, UpdateView ...@@ -98,6 +107,17 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, MessageMixin, UpdateView
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'perms:asset-permission-detail',
kwargs={'pk': self.object.pk}
)
success_message = _(
'Update asset permission <a href="{url}"> {name} </a> '
'success.'.format(url=url, name=self.object.name)
)
return success_message
class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView): class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'perms/asset_permission_detail.html' template_name = 'perms/asset_permission_detail.html'
......
...@@ -156,8 +156,8 @@ function activeNav() { ...@@ -156,8 +156,8 @@ function activeNav() {
function APIUpdateAttr(props) { function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,} // props = {url: .., body: , success: , error: , method: ,}
props = props || {}; props = props || {};
var success_message = props.success_message || 'Update successfully!'; var success_message = props.success_message || '更新成功!';
var fail_message = props.fail_message || 'Error occurred while updating.'; var fail_message = props.fail_message || '更新时发生未知错误.';
$.ajax({ $.ajax({
url: props.url, url: props.url,
type: props.method || "PATCH", type: props.method || "PATCH",
...@@ -183,7 +183,7 @@ function objectDelete(obj, name, url, redirectTo) { ...@@ -183,7 +183,7 @@ function objectDelete(obj, name, url, redirectTo) {
function doDelete() { function doDelete() {
var body = {}; var body = {};
var success = function() { var success = function() {
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success"); // swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
if (!redirectTo) { if (!redirectTo) {
$(obj).parent().parent().remove(); $(obj).parent().parent().remove();
} else { } else {
...@@ -191,7 +191,7 @@ function objectDelete(obj, name, url, redirectTo) { ...@@ -191,7 +191,7 @@ function objectDelete(obj, name, url, redirectTo) {
} }
}; };
var fail = function() { var fail = function() {
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error"); swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error");
}; };
APIUpdateAttr({ APIUpdateAttr({
url: url, url: url,
...@@ -202,14 +202,14 @@ function objectDelete(obj, name, url, redirectTo) { ...@@ -202,14 +202,14 @@ function objectDelete(obj, name, url, redirectTo) {
}); });
} }
swal({ swal({
title: 'Are you sure delete ?', title: '你确定删除吗 ?',
text: " [" + name + "] ", text: " [" + name + "] ",
type: "warning", type: "warning",
showCancelButton: true, showCancelButton: true,
cancelButtonText: 'Cancel', cancelButtonText: '取消',
confirmButtonColor: "#DD6B55", confirmButtonColor: "#ed5565",
confirmButtonText: 'Confirm', confirmButtonText: '确认',
closeOnConfirm: false closeOnConfirm: true,
}, function () { }, function () {
doDelete() doDelete()
}); });
...@@ -334,3 +334,9 @@ String.prototype.format = function(args) { ...@@ -334,3 +334,9 @@ String.prototype.format = function(args) {
} }
return result; return result;
}; };
function setCookie(key, value) {
var expires = new Date();
expires.setTime(expires.getTime() + (24 * 60 * 60 * 1000));
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString();
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="sidebar-collapse"> <div class="sidebar-collapse">
<ul class="nav" id="side-menu"> <ul class="nav" id="side-menu">
{% include '_user_profile.html' %} {% include '_user_profile.html' %}
{% if request.user.is_superuser %} {% if request.user.is_superuser and request.COOKIES.admin == "Yes" %}
{% include '_nav.html' %} {% include '_nav.html' %}
{% else %} {% else %}
{% include '_nav_user.html' %} {% include '_nav_user.html' %}
......
...@@ -34,16 +34,14 @@ ...@@ -34,16 +34,14 @@
</li> </li>
</ul> </ul>
</li> </li>
<li id="ops"> <li id="ops">
<a> <a>
<i class="fa fa-coffee"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span> <i class="fa fa-coffee"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
</a> </a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Tasks' %}</a></li> <li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task' %}</a></li>
</ul> </ul>
</li> </li>
<li id="terminal"> <li id="terminal">
<a> <a>
<i class="fa fa-rocket"></i> <span class="nav-label">{% trans 'Terminal' %}</span><span class="fa arrow"></span> <i class="fa fa-rocket"></i> <span class="nav-label">{% trans 'Terminal' %}</span><span class="fa arrow"></span>
...@@ -65,11 +63,11 @@ ...@@ -65,11 +63,11 @@
{# <li id="download"><a href="">{% trans 'File download' %}</a></li>#} {# <li id="download"><a href="">{% trans 'File download' %}</a></li>#}
{# </ul>#} {# </ul>#}
{#</li>#} {#</li>#}
<li id=""> {#<li id="">#}
<a href=""> {# <a href="">#}
<i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span> {# <i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>#}
</a> {# </a>#}
</li> {#</li>#}
<li class="special_link"> <li class="special_link">
<a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i> <a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i>
<span class="nav-label">{% trans 'Visit us' %}</span> <span class="nav-label">{% trans 'Visit us' %}</span>
......
...@@ -19,6 +19,13 @@ ...@@ -19,6 +19,13 @@
<li><a href="{% url 'users:user-profile' %}">{% trans 'Profile' %}</a></li> <li><a href="{% url 'users:user-profile' %}">{% trans 'Profile' %}</a></li>
<li><a href="{% url 'users:user-profile-update' %}">{% trans 'Profile settings' %}</a></li> <li><a href="{% url 'users:user-profile-update' %}">{% trans 'Profile settings' %}</a></li>
<li class="divider"></li> <li class="divider"></li>
{% if request.user.is_superuser %}
{% if request.COOKIES.admin == 'No' %}
<li><a id="switch_admin">{% trans 'Admin page' %}</a></li>
{% else %}
<li><a id="switch_user">{% trans 'User page' %}</a></li>
{% endif %}
{% endif %}
<li><a href="{% url 'users:logout' %}">{% trans 'Logout' %}</a></li> <li><a href="{% url 'users:logout' %}">{% trans 'Logout' %}</a></li>
</ul> </ul>
</div> </div>
...@@ -26,3 +33,15 @@ ...@@ -26,3 +33,15 @@
JMS JMS
</div> </div>
</li> </li>
<script>
$(document).ready(function () {
})
.on('click', '#switch_admin', function () {
setCookie("admin", "Yes");
window.location = "/"
})
.on('click', '#switch_user', function () {
setCookie("admin", "No");
window.location = "/"
})
</script>
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import time
from celery import shared_task from celery import shared_task
from django.core.cache import cache
from django.db.utils import ProgrammingError, OperationalError
from .models import Session
CACHE_REFRESH_INTERVAL = 10 CACHE_REFRESH_INTERVAL = 10
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
{% endblock %} {% endblock %}
{% block content_left_head %} {% block content_left_head %}
123
{% endblock %} {% endblock %}
{% block table_search %} {% block table_search %}
...@@ -51,12 +50,12 @@ ...@@ -51,12 +50,12 @@
</select> </select>
</div> </div>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control input-sm" name="command" placeholder="Command" value="{{ command }}"> <input type="text" class="form-control input-sm" name="command" placeholder="{% trans 'Command' %}" value="{{ command }}">
</div> </div>
<div class="input-group"> <div class="input-group">
<div class="input-group-btn"> <div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary"> <button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索 {% trans 'Search' %}
</button> </button>
</div> </div>
</div> </div>
......
...@@ -78,14 +78,8 @@ function initTable() { ...@@ -78,14 +78,8 @@ function initTable() {
var reject_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-id="{{ DEFAULT_PK }}" data-name="99991938">{% trans "Reject" %}</a>' var reject_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-id="{{ DEFAULT_PK }}" data-name="99991938">{% trans "Reject" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData) .replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', rowData.name); .replace('99991938', rowData.name);
var connect_btn = '<a href="" class="btn btn-xs btn-warning btn-connect" >{% trans "Connect" %}</a> '
.replace('{{ DEFAULT_PK }}', cellData);
if (rowData.is_accepted) { if (rowData.is_accepted) {
{% if user.is_superuser %} $(td).html(update_btn + delete_btn);
$(td).html(connect_btn + update_btn + delete_btn);
{% else %}
$(td).html(connect_btn);
{% endif %}
} else { } else {
{% if user.is_superuser %} {% if user.is_superuser %}
$(td).html(accept_btn + reject_btn); $(td).html(accept_btn + reject_btn);
......
...@@ -67,15 +67,23 @@ class UserProfileForm(forms.ModelForm): ...@@ -67,15 +67,23 @@ class UserProfileForm(forms.ModelForm):
class UserPasswordForm(forms.Form): class UserPasswordForm(forms.Form):
old_password = forms.CharField( old_password = forms.CharField(
max_length=128, widget=forms.PasswordInput) max_length=128, widget=forms.PasswordInput,
label=_("Old password")
)
new_password = forms.CharField( new_password = forms.CharField(
min_length=5, max_length=128, widget=forms.PasswordInput) min_length=5, max_length=128,
widget=forms.PasswordInput,
label=_("New password")
)
confirm_password = forms.CharField( confirm_password = forms.CharField(
min_length=5, max_length=128, widget=forms.PasswordInput) min_length=5, max_length=128,
widget=forms.PasswordInput,
label=_("Confirm password")
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.instance = kwargs.pop('instance') self.instance = kwargs.pop('instance')
super(UserPasswordForm, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def clean_old_password(self): def clean_old_password(self):
old_password = self.cleaned_data['old_password'] old_password = self.cleaned_data['old_password']
...@@ -102,20 +110,21 @@ class UserPublicKeyForm(forms.Form): ...@@ -102,20 +110,21 @@ class UserPublicKeyForm(forms.Form):
public_key = forms.CharField( public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, label=_('ssh public key'), max_length=5000,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
help_text=_('Paste your id_rsa.pub here.')) help_text=_('Paste your id_rsa.pub here.')
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if 'instance' in kwargs: if 'instance' in kwargs:
self.instance = kwargs.pop('instance') self.instance = kwargs.pop('instance')
else: else:
self.instance = None self.instance = None
super(UserPublicKeyForm, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def clean_public_key(self): def clean_public_key(self):
public_key = self.cleaned_data['public_key'] public_key = self.cleaned_data['public_key']
if self.instance.public_key and public_key == self.instance.public_key: if self.instance.public_key and public_key == self.instance.public_key:
raise forms.ValidationError(_('Public key should not be the ' msg = _('Public key should not be the same as your old one.')
'same as your old one.')) raise forms.ValidationError(msg)
if not validate_ssh_public_key(public_key): if not validate_ssh_public_key(public_key):
raise forms.ValidationError(_('Not a valid ssh public key')) raise forms.ValidationError(_('Not a valid ssh public key'))
...@@ -129,11 +138,11 @@ class UserPublicKeyForm(forms.Form): ...@@ -129,11 +138,11 @@ class UserPublicKeyForm(forms.Form):
class UserBulkUpdateForm(forms.ModelForm): class UserBulkUpdateForm(forms.ModelForm):
users = forms.MultipleChoiceField( users = forms.ModelMultipleChoiceField(
required=True, required=True,
help_text='* required', help_text='* required',
label=_('Select users'), label=_('Select users'),
choices=[(user.id, user.name) for user in User.objects.all()], queryset=User.objects.all(),
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={ attrs={
'class': 'select2', 'class': 'select2',
...@@ -162,9 +171,9 @@ class UserBulkUpdateForm(forms.ModelForm): ...@@ -162,9 +171,9 @@ class UserBulkUpdateForm(forms.ModelForm):
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_id = cleaned_data.pop('users', '') users = cleaned_data.pop('users', '')
groups = cleaned_data.pop('groups', []) groups = cleaned_data.pop('groups', [])
users = User.objects.filter(id__in=users_id) users = User.objects.filter(id__in=[user.id for user in users])
users.update(**cleaned_data) users.update(**cleaned_data)
if groups: if groups:
for user in users: for user in users:
......
...@@ -146,7 +146,7 @@ class User(AbstractUser): ...@@ -146,7 +146,7 @@ class User(AbstractUser):
if not self.name: if not self.name:
self.name = self.username self.name = self.username
super(User, self).save(*args, **kwargs) super().save(*args, **kwargs)
# Add the current user to the default group. # Add the current user to the default group.
if not self.groups.count(): if not self.groups.count():
group = UserGroup.initial() group = UserGroup.initial()
...@@ -180,13 +180,14 @@ class User(AbstractUser): ...@@ -180,13 +180,14 @@ class User(AbstractUser):
return False return False
def avatar_url(self): def avatar_url(self):
admin_default = settings.STATIC_URL + "img/avatar/admin.png"
user_default = settings.STATIC_URL + "img/avatar/user.png"
if self.avatar: if self.avatar:
return self.avatar.url return self.avatar.url
if self.is_superuser:
return admin_default
else: else:
avatar_dir = os.path.join(settings.MEDIA_ROOT, 'avatar') return user_default
if os.path.isdir(avatar_dir):
return os.path.join(settings.MEDIA_URL, 'avatar', 'default.png')
return 'https://www.gravatar.com/avatar/c6812ab450230979465d7bf288eadce2a?s=120&d=identicon'
def generate_reset_token(self): def generate_reset_token(self):
return signer.sign_t({'reset': str(self.id), 'email': self.email}, expires_in=3600) return signer.sign_t({'reset': str(self.id), 'email': self.email}, expires_in=3600)
......
...@@ -31,12 +31,12 @@ ...@@ -31,12 +31,12 @@
</select> </select>
</div> </div>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}"> <input type="text" class="form-control input-sm" name="keyword" placeholder="{% trans "Search" %}" value="{{ keyword }}">
</div> </div>
<div class="input-group"> <div class="input-group">
<div class="input-group-btn"> <div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary"> <button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索 {% trans "Search" %}
</button> </button>
</div> </div>
</div> </div>
...@@ -85,7 +85,9 @@ ...@@ -85,7 +85,9 @@
forceParse: false, forceParse: false,
autoclose: true autoclose: true
}); });
$('.select2').select2(); $('.select2').select2({
dropdownAutoWidth: true
});
}) })
</script> </script>
{% endblock %} {% endblock %}
......
...@@ -73,7 +73,7 @@ function initTable() { ...@@ -73,7 +73,7 @@ function initTable() {
} }
}}, }},
{targets: 6, createdCell: function (td, cellData, rowData) { {targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "users:user-update" pk='00000000-0000-0000-0000-000000000000' %}" 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 === "{{ user.username }}") {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment