Commit 76df1de6 authored by ibuler's avatar ibuler

[Bugfix] 修复一些明显的bug

parent 5a929721
......@@ -44,9 +44,11 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet):
else:
assets_granted = get_user_granted_assets(self.request.user)
queryset = self.queryset.filter(id__in=[asset.id for asset in assets_granted])
cluster_id = self.request.query_params.get('cluster_id')
asset_group_id = self.request.query_params.get('asset_group_id')
admin_user_id = self.request.query_params.get('admin_user_id')
if cluster_id:
queryset = queryset.filter(cluster__id=cluster_id)
if asset_group_id:
......
# coding:utf-8
import uuid
from django import forms
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,
logger = get_logger(__file__)
from rest_framework import serializers
class AssetCreateForm(forms.ModelForm):
......@@ -57,11 +55,11 @@ class AssetUpdateForm(forms.ModelForm):
class AssetBulkUpdateForm(forms.ModelForm):
assets = forms.MultipleChoiceField(
assets = forms.ModelMultipleChoiceField(
required=True,
help_text='* required',
label=_('Select assets'),
choices=[(asset.id, asset.hostname) for asset in Asset.objects.all()],
queryset=Asset.objects.all(),
widget=forms.SelectMultiple(
attrs={
'class': 'select2',
......@@ -94,10 +92,9 @@ class AssetBulkUpdateForm(forms.ModelForm):
cleaned_data = {k: v for k, v in self.cleaned_data.items()
if k in changed_fields}
print(cleaned_data)
assets_id = cleaned_data.pop('assets')
assets = cleaned_data.pop('assets')
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)
if groups:
for asset in assets:
......@@ -175,16 +172,18 @@ class AdminUserForm(forms.ModelForm):
password = None
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)
return admin_user
def clean_private_key_file(self):
private_key_file = self.cleaned_data['private_key_file']
password = self.cleaned_data['password']
if private_key_file:
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'))
return private_key
return private_key_file
......
......@@ -18,10 +18,6 @@ __all__ = ['Asset']
logger = logging.getLogger(__name__)
def get_default_cluster():
return Cluster.initial()
class Asset(models.Model):
# Todo: Move them to settings
STATUS_CHOICES = (
......@@ -48,7 +44,7 @@ class Asset(models.Model):
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
port = models.IntegerField(default=22, verbose_name=_('Port'))
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'))
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'),)
......
......@@ -10,7 +10,6 @@ from django.db import models
import logging
from django.utils.translation import ugettext_lazy as _
from .user import SystemUser
__all__ = ['AssetGroup']
logger = logging.getLogger(__name__)
......
......@@ -66,16 +66,15 @@ class AssetUser(models.Model):
@property
def private_key_file(self):
if not self.private_key:
if not self.private_key_obj:
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
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)
if not os.path.exists(key_path):
with open(key_path, 'w') as f:
f.write(key_str)
self.private_key_obj.write_private_key_file(key_path)
os.chmod(key_path, 0o400)
return key_path
......@@ -105,7 +104,6 @@ class AssetUser(models.Model):
update_fields.append('_public_key')
if update_fields:
print(update_fields)
self.save(update_fields=update_fields)
def auto_gen_auth(self):
......@@ -149,7 +147,11 @@ class AdminUser(AssetUser):
@property
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
def become_pass(self, password):
......@@ -199,7 +201,7 @@ class SystemUser(AssetUser):
('K', 'Public key'),
)
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'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
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, *
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(
{
'update_assets_hardware_period': {
......
......@@ -63,7 +63,7 @@
</tr>
<tr>
<td>{% trans 'Public IP' %}:</td>
<td><b>{{ asset.public_ip }}</b></td>
<td><b>{{ asset.public_ip|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Port' %}:</td>
......@@ -74,12 +74,12 @@
{% if asset.admin_user_avail %}
<td><b>{{ asset.admin_user_avail.name }}</b></td>
{% else %}
<td><b>None</b></td>
<td></td>
{% endif %}
</tr>
<tr>
<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>
<td>{% trans 'Cluster' %}:</td>
......@@ -87,39 +87,39 @@
</tr>
<tr>
<td>{% trans 'Cabinet number' %}:</td>
<td><b>{{ asset.cabinet_no }}</b></td>
<td><b>{{ asset.cabinet_no|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Cabinet position' %}:</td>
<td><b>{{ asset.cabinet_pos }}</b></td>
<td><b>{{ asset.cabinet_pos|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Vendor' %}:</td>
<td><b>{{ asset.vendor }}</b></td>
<td><b>{{ asset.vendor|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Model' %}:</td>
<td><b>{{ asset.model }}</b></td>
<td><b>{{ asset.model|default:"" }}</b></td>
</tr>
<tr>
<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>
<td>{% trans 'Memory' %}:</td>
<td><b>{{ asset.memory }}</b></td>
<td><b>{{ asset.memory|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Disk' %}:</td>
<td><b>{{ asset.disk_total }}</b></td>
<td><b>{{ asset.disk_total|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Platform' %}:</td>
<td><b>{{ asset.platform }}</b></td>
<td><b>{{ asset.platform|default:"" }}</b></td>
</tr>
<tr>
<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>
<td>{% trans 'Asset status' %}:</td>
......@@ -127,7 +127,7 @@
</tr>
<tr>
<td>{% trans 'Is active' %}:</td>
<td><b>{{ asset.is_active }}</b></td>
<td><b>{{ asset.is_active|yesno:"Yes,No" }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset type' %}:</td>
......@@ -139,11 +139,11 @@
</tr>
<tr>
<td>{% trans 'Serial number' %}:</td>
<td><b>{{ asset.sn }}</b></td>
<td><b>{{ asset.sn|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset number' %}:</td>
<td><b>{{ asset.number }}</b></td>
<td><b>{{ asset.number|default:"" }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
......
......@@ -64,8 +64,7 @@
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script type="text/javascript">
$(document).ready(function(){
function initTable() {
var options = {
ele: $('#asset_list_table'),
columnDefs: [
......@@ -103,48 +102,51 @@ $(document).ready(function(){
{data: "is_active" }, {data: "is_connective"}, {data: "id" }],
op_html: $('#actions').html()
};
var table = jumpserver.initDataTable(options);
return 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');
}
})
$(document).ready(function(){
initTable();
})
.on('click', '#btn_export', function () {
var $data_table = $('#asset_list_table').DataTable();
var rows = $data_table.rows('.selected').data();
var assets = [];
$.each(rows, function (index, obj) {
assets.push(obj.id)
});
$('#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();
}
$.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');
}
$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 () {
var $this = $(this);
......
......@@ -4,6 +4,7 @@
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
......
{% extends '_base_list.html' %}
{% load i18n %}
{% block help_message %}
<div class="alert alert-info help-message">
系统用户是 用户登录资产(服务器)时使用的用户,如 web, sa, dba等具有特殊功能的用户。系统用户创建时,如果选择了自动推送
Jumpserver会使用ansible自动推送到系统用户所在集群的资产中,如果资产(交换机)不支持ansible, 请手动填写账号密码。
</div>
{% endblock %}
{% block table_search %}
{% endblock %}
......
# ~*~ coding: utf-8 ~*~
#
from collections import defaultdict
from common.utils import get_object_or_none
from .models import Asset, SystemUser
......@@ -17,3 +18,12 @@ def get_system_user_by_name(name):
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
# Create your models here.
......@@ -195,11 +195,11 @@ def ssh_key_string_to_obj(text, password=None):
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):
private_key = private_key.decode("utf-8")
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)):
raise IOError('Invalid private key')
......@@ -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.')
def validate_ssh_private_key(text):
def validate_ssh_private_key(text, password=None):
if isinstance(text, bytes):
try:
text = text.decode("utf-8")
except UnicodeDecodeError:
return False
key = ssh_key_string_to_obj(text)
key = ssh_key_string_to_obj(text, password=password)
if key is None:
return False
else:
......
......@@ -59,7 +59,6 @@ INSTALLED_APPS = [
'assets.apps.AssetsConfig',
'perms.apps.PermsConfig',
'ops.apps.OpsConfig',
# 'audits.apps.AuditsConfig',
'common.apps.CommonConfig',
'terminal.apps.TerminalConfig',
'rest_framework',
......
# ~*~ coding: utf-8 ~*~
from collections import defaultdict
from ansible.plugins.callback import CallbackBase
from ansible.plugins.callback.default import CallbackModule
......@@ -21,9 +19,8 @@ class AdHocResultCallback(CallbackModule):
# "contacted": {"hostname",...},
# "dark": {"hostname": {"task_name": {}, "task_name": {}},...,},
# }
self.results_raw = dict(ok=defaultdict(dict), failed=defaultdict(dict),
unreachable=defaultdict(dict), skipped=defaultdict(dict))
self.results_summary = dict(contacted=[], dark=defaultdict(dict))
self.results_raw = dict(ok={}, failed={}, unreachable={}, skipped={})
self.results_summary = dict(contacted=[], dark={})
super().__init__()
def gather_result(self, t, res):
......@@ -34,8 +31,8 @@ class AdHocResultCallback(CallbackModule):
if self.results_raw[t].get(host):
self.results_raw[t][host][task_name] = task_result
# else:
# self.results_raw[t][host] = {task_name: task_result}
else:
self.results_raw[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):
......@@ -45,10 +42,10 @@ class AdHocResultCallback(CallbackModule):
if host not in contacted:
contacted.append(host)
else:
# if dark.get(host):
dark[host][task_name] = task_result.values
# else:
# dark[host] = {task_name: task_result}
if dark.get(host):
dark[host][task_name] = task_result.values
else:
dark[host] = {task_name: task_result}
if host in contacted:
contacted.remove(host)
......
......@@ -32,6 +32,7 @@ class BaseHost(Host):
}
"""
self.host_data = host_data
print(host_data)
hostname = host_data.get('hostname') or host_data.get('ip')
port = host_data.get('port') or 22
super().__init__(hostname, port)
......
......@@ -27,7 +27,7 @@
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
{% trans "Search" %}
</button>
</div>
</div>
......@@ -37,7 +37,7 @@
{% block table_head %}
<th class="text-center"></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 'Hosts' %}</th>
<th class="text-center">{% trans 'Success' %}</th>
......
# ~*~ coding: utf-8 ~*~
import re
import time
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 .ansible import AdHocRunner, CommandResultCallback
......@@ -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):
inventory = get_inventory(
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
return runner
@record_adhoc
def run_adhoc_object(adhoc, **options):
"""
:param adhoc: Instance of AdHoc
......@@ -109,6 +109,8 @@ def create_or_update_task(
run_as_admin=False, run_as="", become_info=None,
created_by=None
):
print(options)
print(task_name)
task = get_object_or_none(Task, name=task_name)
if task is None:
task = Task(name=task_name, created_by=created_by)
......@@ -125,6 +127,7 @@ def create_or_update_task(
if not adhoc or adhoc != new_adhoc:
new_adhoc.save()
task.latest_adhoc = new_adhoc
print("Return task")
return task
......
......@@ -38,3 +38,34 @@ class AssetPermissionForm(forms.ModelForm):
'user_groups': _('User or user 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 @@
<div class="col-sm-9">
<div class="input-group date">
<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>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
......
......@@ -4,6 +4,12 @@
{% block table_search %}
{% endblock %}
{% block help_message %}
<div class="alert alert-info help-message">
提前规划好集群中的系统用户,授权时选择的资产(组内资产)必须存在该系统用户,否则可能无法成功登录
</div>
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary ">
......
......@@ -66,9 +66,7 @@ class MessageMixin:
return success_message
class AssetPermissionCreateView(AdminUserRequiredMixin,
MessageMixin,
CreateView):
class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
......@@ -83,8 +81,19 @@ class AssetPermissionCreateView(AdminUserRequiredMixin,
kwargs.update(context)
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
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
......@@ -98,6 +107,17 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, MessageMixin, UpdateView
kwargs.update(context)
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):
template_name = 'perms/asset_permission_detail.html'
......
......@@ -156,8 +156,8 @@ function activeNav() {
function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,}
props = props || {};
var success_message = props.success_message || 'Update successfully!';
var fail_message = props.fail_message || 'Error occurred while updating.';
var success_message = props.success_message || '更新成功!';
var fail_message = props.fail_message || '更新时发生未知错误.';
$.ajax({
url: props.url,
type: props.method || "PATCH",
......@@ -183,7 +183,7 @@ function objectDelete(obj, name, url, redirectTo) {
function doDelete() {
var body = {};
var success = function() {
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
// swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
if (!redirectTo) {
$(obj).parent().parent().remove();
} else {
......@@ -191,7 +191,7 @@ function objectDelete(obj, name, url, redirectTo) {
}
};
var fail = function() {
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error");
};
APIUpdateAttr({
url: url,
......@@ -202,14 +202,14 @@ function objectDelete(obj, name, url, redirectTo) {
});
}
swal({
title: 'Are you sure delete ?',
title: '你确定删除吗 ?',
text: " [" + name + "] ",
type: "warning",
showCancelButton: true,
cancelButtonText: 'Cancel',
confirmButtonColor: "#DD6B55",
confirmButtonText: 'Confirm',
closeOnConfirm: false
cancelButtonText: '取消',
confirmButtonColor: "#ed5565",
confirmButtonText: '确认',
closeOnConfirm: true,
}, function () {
doDelete()
});
......@@ -334,3 +334,9 @@ String.prototype.format = function(args) {
}
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 @@
<div class="sidebar-collapse">
<ul class="nav" id="side-menu">
{% include '_user_profile.html' %}
{% if request.user.is_superuser %}
{% if request.user.is_superuser and request.COOKIES.admin == "Yes" %}
{% include '_nav.html' %}
{% else %}
{% include '_nav_user.html' %}
......
......@@ -34,16 +34,14 @@
</li>
</ul>
</li>
<li id="ops">
<a>
<i class="fa fa-coffee"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
</a>
<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>
</li>
<li id="terminal">
<a>
<i class="fa fa-rocket"></i> <span class="nav-label">{% trans 'Terminal' %}</span><span class="fa arrow"></span>
......@@ -65,11 +63,11 @@
{# <li id="download"><a href="">{% trans 'File download' %}</a></li>#}
{# </ul>#}
{#</li>#}
<li id="">
<a href="">
<i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
</a>
</li>
{#<li id="">#}
{# <a href="">#}
{# <i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>#}
{# </a>#}
{#</li>#}
<li class="special_link">
<a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i>
<span class="nav-label">{% trans 'Visit us' %}</span>
......
......@@ -19,6 +19,13 @@
<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 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>
</ul>
</div>
......@@ -26,3 +33,15 @@
JMS
</div>
</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 -*-
#
import time
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
......
......@@ -13,7 +13,6 @@
{% endblock %}
{% block content_left_head %}
123
{% endblock %}
{% block table_search %}
......@@ -51,12 +50,12 @@
</select>
</div>
<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 class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
{% trans 'Search' %}
</button>
</div>
</div>
......
......@@ -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>'
.replace('{{ DEFAULT_PK }}', cellData)
.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 user.is_superuser %}
$(td).html(connect_btn + update_btn + delete_btn);
{% else %}
$(td).html(connect_btn);
{% endif %}
$(td).html(update_btn + delete_btn);
} else {
{% if user.is_superuser %}
$(td).html(accept_btn + reject_btn);
......
......@@ -67,15 +67,23 @@ class UserProfileForm(forms.ModelForm):
class UserPasswordForm(forms.Form):
old_password = forms.CharField(
max_length=128, widget=forms.PasswordInput)
max_length=128, widget=forms.PasswordInput,
label=_("Old password")
)
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(
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):
self.instance = kwargs.pop('instance')
super(UserPasswordForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def clean_old_password(self):
old_password = self.cleaned_data['old_password']
......@@ -102,20 +110,21 @@ class UserPublicKeyForm(forms.Form):
public_key = forms.CharField(
label=_('ssh public key'), max_length=5000,
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):
if 'instance' in kwargs:
self.instance = kwargs.pop('instance')
else:
self.instance = None
super(UserPublicKeyForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def clean_public_key(self):
public_key = self.cleaned_data['public_key']
if self.instance.public_key and public_key == self.instance.public_key:
raise forms.ValidationError(_('Public key should not be the '
'same as your old one.'))
msg = _('Public key should not be the same as your old one.')
raise forms.ValidationError(msg)
if not validate_ssh_public_key(public_key):
raise forms.ValidationError(_('Not a valid ssh public key'))
......@@ -129,11 +138,11 @@ class UserPublicKeyForm(forms.Form):
class UserBulkUpdateForm(forms.ModelForm):
users = forms.MultipleChoiceField(
users = forms.ModelMultipleChoiceField(
required=True,
help_text='* required',
label=_('Select users'),
choices=[(user.id, user.name) for user in User.objects.all()],
queryset=User.objects.all(),
widget=forms.SelectMultiple(
attrs={
'class': 'select2',
......@@ -162,9 +171,9 @@ class UserBulkUpdateForm(forms.ModelForm):
cleaned_data = {k: v for k, v in self.cleaned_data.items()
if k in changed_fields}
users_id = cleaned_data.pop('users', '')
users = cleaned_data.pop('users', '')
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)
if groups:
for user in users:
......
......@@ -146,7 +146,7 @@ class User(AbstractUser):
if not self.name:
self.name = self.username
super(User, self).save(*args, **kwargs)
super().save(*args, **kwargs)
# Add the current user to the default group.
if not self.groups.count():
group = UserGroup.initial()
......@@ -180,13 +180,14 @@ class User(AbstractUser):
return False
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:
return self.avatar.url
if self.is_superuser:
return admin_default
else:
avatar_dir = os.path.join(settings.MEDIA_ROOT, 'avatar')
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'
return user_default
def generate_reset_token(self):
return signer.sign_t({'reset': str(self.id), 'email': self.email}, expires_in=3600)
......
......@@ -31,12 +31,12 @@
</select>
</div>
<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 class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
{% trans "Search" %}
</button>
</div>
</div>
......@@ -85,7 +85,9 @@
forceParse: false,
autoclose: true
});
$('.select2').select2();
$('.select2').select2({
dropdownAutoWidth: true
});
})
</script>
{% endblock %}
......
......@@ -73,7 +73,7 @@ function initTable() {
}
}},
{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 = "";
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