Commit 17ddb3bb authored by ibuler's avatar ibuler

[Feture] 使用信号解耦

parent 18fbfb44
......@@ -22,7 +22,7 @@ from django.shortcuts import get_object_or_404
from common.mixins import IDInFilterMixin
from common.utils import get_object_or_none
from .hands import IsSuperUser, IsAppUser, IsValidUser, \
from .hands import IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser, \
get_user_granted_assets, push_users
from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser
from . import serializers
......@@ -156,7 +156,7 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
class SystemUserAuthInfoApi(generics.RetrieveAPIView):
"""Get system user auth info"""
queryset = SystemUser.objects.all()
permission_classes = (IsAppUser,)
permission_classes = (IsSuperUserOrAppUser,)
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
......@@ -166,7 +166,6 @@ class SystemUserAuthInfoApi(generics.RetrieveAPIView):
'username': system_user.username,
'password': system_user.password,
'private_key': system_user.private_key,
'auth_method': system_user.auth_method,
}
return Response(data)
......
......@@ -8,5 +8,6 @@ class AssetsConfig(AppConfig):
def ready(self):
from .signals import on_app_ready
from . import tasks
on_app_ready.send(self.__class__)
super().ready()
......@@ -2,4 +2,8 @@
#
ADMIN_USER_CONN_CACHE_KEY_PREFIX = "ADMIN_USER_CONN_"
SYSTEM_USER_CONN_CACHE_KEY_PREFIX = 'SYSTEM_USER_CONN_'
SYSTEM_USER_CONN_CACHE_KEY_PREFIX = "SYSTEM_USER_CONN_"
UPDATE_ASSETS_HARDWARE_PERIOD_LOCK_KEY = "UPDATE_ASSETS_HARDWARE_PERIOD_LOCK_KEY"
TEST_ADMIN_USER_CONNECTABILITY_PEROID_KEY = "TEST_ADMIN_USER_CONNECTABILITY_KEY"
TEST_SYSTEM_USER_CONNECTABILITY_PEROID_KEY = "TEST_SYSTEM_USER_CONNECTABILITY_PEROID_KEY"
PUSH_SYSTEM_USER_PERIOD_KEY = "PUSH_SYSTEM_USER_PERIOD_KEY"
# coding:utf-8
import uuid
from django import forms
from django.utils.translation import gettext_lazy as _
......@@ -142,7 +143,7 @@ class ClusterForm(forms.ModelForm):
class AdminUserForm(forms.ModelForm):
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(
widget=forms.PasswordInput, max_length=100,
widget=forms.PasswordInput, max_length=128,
strip=True, required=False,
help_text=_('If also set private key, use that first'),
)
......@@ -199,57 +200,46 @@ class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=True, required=False)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, required=False, max_length=100, strip=True)
password = forms.CharField(widget=forms.PasswordInput, required=False, max_length=128, strip=True)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
def __init__(self, *args, **kwargs):
super(SystemUserForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
system_user = super(SystemUserForm, self).save(commit=commit)
password = self.cleaned_data['password']
system_user = super().save()
password = self.cleaned_data.get('password', None)
private_key_file = self.cleaned_data.get('private_key_file')
auto_generate_key = self.cleaned_data.get('auto_generate_key')
if system_user.auth_method == 'P':
if password:
system_user.password = password
elif system_user.auth_method == 'K':
if self.cleaned_data['auto_generate_key']:
private_key, public_key = ssh_key_gen(username=system_user.name)
logger.info('Generate private key and public key')
else:
private_key = private_key_file.read().strip()
public_key = ssh_pubkey_gen(private_key=private_key)
system_user.private_key = private_key
system_user.public_key = public_key
system_user.save()
return self.instance
if auto_generate_key:
logger.info('Auto set system user auth')
system_user.auto_gen_auth()
else:
private_key = private_key_file.read().strip().decode('utf-8')
public_key = ssh_pubkey_gen(private_key=private_key)
system_user.set_auth(password=password, private_key=private_key, public_key=public_key)
return system_user
def clean_private_key_file(self):
if self.data['auth_method'] == 'K' and \
not self.cleaned_data['auto_generate_key']:
if not self.cleaned_data['private_key_file']:
raise forms.ValidationError(_('Private key required'))
else:
key_string = self.cleaned_data['private_key_file'].read()
self.cleaned_data['private_key_file'].seek(0)
if not validate_ssh_private_key(key_string):
raise forms.ValidationError(_('Invalid private key'))
if self.cleaned_data.get('private_key_file'):
key_string = self.cleaned_data['private_key_file'].read()
self.cleaned_data['private_key_file'].seek(0)
if not validate_ssh_private_key(key_string):
raise forms.ValidationError(_('Invalid private key'))
return self.cleaned_data['private_key_file']
def clean_password(self):
if self.data['auth_method'] == 'P':
if not self.cleaned_data.get('password'):
raise forms.ValidationError(_('Password required'))
if not self.cleaned_data.get('password') and \
not self.cleaned_data.get('private_key_file') and \
not self.cleaned_data.get('auto_generate_key'):
raise forms.ValidationError(_('Auth info required, private_key or password'))
return self.cleaned_data['password']
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol', 'auto_generate_key', 'password',
'private_key_file', 'auth_method', 'auto_push', 'sudo',
'name', 'username', 'protocol', 'auto_generate_key',
'password', 'private_key_file', 'auto_push', 'sudo',
'comment', 'shell', 'cluster'
]
widgets = {
......@@ -262,52 +252,17 @@ class SystemUserForm(forms.ModelForm):
help_texts = {
'name': '* required',
'username': '* required',
'cluster': 'If auto push checked, then push system user to that cluster assets',
'cluster': 'If auto push checked, system user will be create at cluster assets',
'auto_push': 'Auto push system user to asset',
}
class SystemUserUpdateForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=False, required=False)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, required=False, max_length=100, strip=True)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
def __init__(self, *args, **kwargs):
super(SystemUserUpdateForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
system_user = super(SystemUserUpdateForm, self).save(commit=commit)
password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file')
if system_user.auth_method == 'P' and password:
system_user.password = password
elif system_user.auth_method == 'K' and private_key_file:
private_key = private_key_file.read().strip()
public_key = ssh_pubkey_gen(private_key=private_key)
system_user.private_key = private_key
system_user.public_key = public_key
system_user.save()
return self.instance
def clean_private_key_file(self):
if self.data['auth_method'] == 'K' and self.cleaned_data['private_key_file']:
key_string = self.cleaned_data['private_key_file'].read()
self.cleaned_data['private_key_file'].seek(0)
if not validate_ssh_private_key(key_string):
raise forms.ValidationError(_('Invalid private key'))
return self.cleaned_data['private_key_file']
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol',
'auth_method', 'auto_push', 'sudo',
'comment', 'shell', 'cluster'
'sudo', 'comment', 'shell', 'cluster'
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
......@@ -320,9 +275,41 @@ class SystemUserUpdateForm(forms.ModelForm):
'name': '* required',
'username': '* required',
'cluster': 'If auto push checked, then push system user to that cluster assets',
'auto_push': 'Auto push system user to asset',
}
class SystemUserAuthForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput, required=False, max_length=128, strip=True)
private_key_file = forms.FileField(required=False)
def clean_private_key_file(self):
if self.cleaned_data.get('private_key_file'):
key_string = self.cleaned_data['private_key_file'].read()
self.cleaned_data['private_key_file'].seek(0)
if not validate_ssh_private_key(key_string):
raise forms.ValidationError(_('Invalid private key'))
return self.cleaned_data['private_key_file']
def clean_password(self):
if not self.cleaned_data.get('password') and \
not self.cleaned_data.get('private_key_file'):
msg = _('Auth info required, private_key or password')
raise forms.ValidationError(msg)
return self.cleaned_data['password']
def update(self, system_user):
password = self.cleaned_data.get('password')
private_key_file = self.cleaned_data.get('private_key_file')
if private_key_file:
private_key = private_key_file.read().strip()
public_key = ssh_pubkey_gen(private_key=private_key)
else:
private_key = None
public_key = None
system_user.set_auth(password=password, private_key=private_key, public_key=public_key)
return system_user
class FileForm(forms.Form):
file = forms.FileField()
......@@ -12,7 +12,7 @@
from users.utils import AdminUserRequiredMixin
from users.permissions import IsAppUser, IsSuperUser, IsValidUser
from users.permissions import IsAppUser, IsSuperUser, IsValidUser, IsSuperUserOrAppUser
from users.models import User, UserGroup
from perms.utils import get_user_granted_assets
from perms.tasks import push_users
\ No newline at end of file
......@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
class Cluster(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=32, verbose_name=_('Name'))
admin_user = models.ForeignKey('assets.AdminUser', null=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
admin_user = models.ForeignKey('assets.AdminUser', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth'))
contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact'))
phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))
......
......@@ -2,7 +2,6 @@
# -*- coding: utf-8 -*-
#
from __future__ import unicode_literals
import os
import logging
import uuid
......@@ -12,7 +11,7 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from common.utils import signer, ssh_key_string_to_obj
from common.utils import signer, ssh_key_string_to_obj, ssh_key_gen
from .utils import private_key_validator
......@@ -20,53 +19,41 @@ __all__ = ['AdminUser', 'SystemUser',]
logger = logging.getLogger(__name__)
class AdminUser(models.Model):
"""
A privileged user that ansible can use it to push system user and so on
"""
BECOME_METHOD_CHOICES = (
('sudo', 'sudo'),
('su', 'su'),
)
class AssetUser(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
_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,])
become = models.BooleanField(default=True)
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
become_user = models.CharField(default='root', max_length=64)
_become_pass = models.CharField(default='', max_length=128)
_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'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
def __str__(self):
return self.name
@property
def password(self):
if self._password:
return signer.unsign(self._password)
else:
return ''
return None
@password.setter
def password(self, password_raw):
self._password = signer.sign(password_raw)
raise AttributeError("Using set_auth do that")
# self._password = signer.sign(password_raw)
@property
def private_key(self):
if self._private_key:
key_str = signer.unsign(self._private_key)
return ssh_key_string_to_obj(key_str)
return ssh_key_string_to_obj(key_str, password=self.password)
else:
return None
@private_key.setter
def private_key(self, private_key_raw):
self._private_key = signer.sign(private_key_raw)
raise AttributeError("Using set_auth do that")
# self._private_key = signer.sign(private_key_raw)
@property
def private_key_file(self):
......@@ -74,19 +61,62 @@ class AdminUser(models.Model):
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
key_name = md5(self._private_key.encode()).hexdigest()
key_str = signer.unsign(self._private_key)
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):
self.private_key.write_private_key_file(key_path)
with open(key_path, 'w') as f:
f.write(key_str)
os.chmod(key_path, 0o400)
return key_path
@property
def public_key(self):
return signer.unsign(self._public_key)
@public_key.setter
def public_key(self, public_key_raw):
self._public_key = signer.sign(public_key_raw)
def set_auth(self, password=None, private_key=None, public_key=None):
update_fields = []
if password:
self._password = signer.sign(password)
update_fields.append('_password')
if private_key:
self._private_key = signer.sign(private_key)
update_fields.append('_private_key')
if public_key:
self._public_key = signer.sign(public_key)
update_fields.append('_public_key')
if update_fields:
self.save(update_fields=update_fields)
def auto_gen_auth(self):
password = str(uuid.uuid4())
private_key, public_key = ssh_key_gen(
username=self.name, password=password
)
self.set_auth(password=password,
private_key=private_key,
public_key=public_key)
class Meta:
abstract = True
class AdminUser(AssetUser):
"""
A privileged user that ansible can use it to push system user and so on
"""
BECOME_METHOD_CHOICES = (
('sudo', 'sudo'),
('su', 'su'),
)
become = models.BooleanField(default=True)
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
become_user = models.CharField(default='root', max_length=64)
_become_pass = models.CharField(default='', max_length=128)
def __str__(self):
return self.name
@property
def become_pass(self):
......@@ -131,7 +161,7 @@ class AdminUser(models.Model):
continue
class SystemUser(models.Model):
class SystemUser(AssetUser):
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
)
......@@ -139,68 +169,15 @@ class SystemUser(models.Model):
('P', 'Password'),
('K', 'Public key'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
cluster = models.ManyToManyField('assets.Cluster', verbose_name=_("Cluster"))
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
_private_key = models.TextField(max_length=8192, blank=True, verbose_name=_('SSH private key'))
_public_key = models.TextField(max_length=8192, blank=True, verbose_name=_('SSH public key'))
auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K', max_length=1, verbose_name=_('Auth method'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
date_created = models.DateTimeField(auto_now_add=True)
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.name
@property
def password(self):
if self._password:
return signer.unsign(self._password)
return None
@password.setter
def password(self, password_raw):
self._password = signer.sign(password_raw)
@property
def private_key(self):
if self._private_key:
return signer.unsign(self._private_key)
return None
@private_key.setter
def private_key(self, private_key_raw):
self._private_key = signer.sign(private_key_raw)
@property
def private_key_file(self):
if not self.private_key:
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
key_name = md5(self._private_key.encode()).hexdigest()
key_path = os.path.join(tmp_dir, key_name)
if not os.path.exists(key_path):
self.private_key.write_private_key_file(key_path)
return key_path
@property
def public_key(self):
if self._public_key:
return signer.unsign(self._public_key)
else:
return None
@public_key.setter
def public_key(self, public_key_raw):
self._public_key = signer.sign(public_key_raw)
def _to_secret_json(self):
"""Push system user use it"""
return {
......@@ -228,7 +205,6 @@ class SystemUser(models.Model):
'name': self.name,
'username': self.username,
'protocol': self.protocol,
'auth_method': self.auth_method,
'auto_push': self.auto_push,
}
......
......@@ -115,7 +115,7 @@ class SystemUserSerializer(serializers.ModelSerializer):
class AssetSystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
fields = ('id', 'name', 'username', 'protocol', 'auth_method', 'comment')
fields = ('id', 'name', 'username', 'protocol', 'comment')
class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer):
......@@ -202,7 +202,10 @@ class ClusterSerializer(BulkSerializerMixin, serializers.ModelSerializer):
@staticmethod
def get_admin_user_name(obj):
return obj.admin_user.name
try:
return obj.admin_user.name
except AttributeError:
return ''
class AssetGroupGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer):
......
# -*- coding: utf-8 -*-
#
from django.dispatch import Signal
from django.dispatch import Signal, receiver
from common.utils import get_logger
logger = get_logger(__file__)
on_asset_created = Signal(providing_args=['asset'])
on_app_ready = Signal()
@receiver(on_asset_created)
def update_asset_info(sender, asset=None, **kwargs):
from .tasks import update_assets_hardware_info
logger.debug("Receive asset create signal, update asset hardware info")
update_assets_hardware_info.delay([asset])
@receiver(on_asset_created)
def test_admin_user_connective(sender, asset=None, **kwargs):
from .tasks import test_admin_user_connectability_manual
logger.debug("Receive asset create signal, test admin user connectability")
test_admin_user_connectability_manual.delay(asset)
@receiver(on_app_ready)
def test_admin_user_on_app_ready(sender, **kwargs):
from .tasks import test_admin_user_connectability_period
logger.debug("Receive app ready signal, test admin connectability")
test_admin_user_connectability_period.delay()
on_system_user_auth_changed = Signal(providing_args=['system_user'])
This diff is collapsed.
......@@ -39,30 +39,29 @@
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.cluster layout="horizontal" %}
<h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.auth_method layout="horizontal" %}
{% block auth %}
<div class="password-auth hidden">
{% bootstrap_field form.password layout="horizontal" %}
</div>
<div class="public-key-auth">
<h3>{% trans 'Auth' %}</h3>
<div class="auto-generate">
<div class="form-group">
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
<div class="col-sm-8">
{{ form.auto_generate_key}}
</div>
</div>
<div>
{% bootstrap_field form.private_key_file layout="horizontal" %}
</div>
</div>
<div class="auth-fields">
{% bootstrap_field form.private_key_file layout="horizontal" %}
{% bootstrap_field form.password layout="horizontal" %}
</div>
{% endblock %}
<div class="form-group">
<label for="{{ form.as_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label>
<div class="col-sm-8">
{{ form.auto_push}}
</div>
</div>
{% endblock %}
<h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.sudo layout="horizontal" %}
{% bootstrap_field form.shell layout="horizontal" %}
......@@ -82,41 +81,20 @@
{% endblock %}
{% block custom_foot_js %}
<script>
var auth_method = '#'+'{{ form.auth_method.id_for_label }}';
var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}';
function authMethodDisplay() {
if ($(auth_method).val() == 'P') {
$('.password-auth').removeClass('hidden');
$('.public-key-auth').addClass('hidden');
$('#'+'{{ form.password.id_for_label }}').attr('required', 'required');
$('#'+'{{ form.password.id_for_label }}').removeAttr('disabled');
$('#'+'{{ form.private_key_file.id_for_label }}').removeAttr('required');
} else if ($(auth_method).val() == 'K') {
$('.password-auth').addClass('hidden');
$('.public-key-auth').removeClass('hidden');
$('#'+'{{ form.password.id_for_label }}').removeAttr('required');
$('#'+'{{ form.password.id_for_label }}').attr('disabled', 'disabled');
if ($(auto_generate_key).prop('checked')){
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').addClass('hidden');
} else {
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').removeClass('hidden');
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group input').attr('required', 'required');
}
function authFieldsDisplay() {
if ($(auto_generate_key).prop('checked')) {
$('.auth-fields').addClass('hidden');
} else {
$('.auth-fields').removeClass('hidden');
}
}
$(document).ready(function () {
$('.select2').select2();
authMethodDisplay();
$(auth_method).change(function () {
authMethodDisplay();
});
authFieldsDisplay();
$(auto_generate_key).change(function () {
authMethodDisplay();
authFieldsDisplay();
});
})
</script>
{% endblock %}
\ No newline at end of file
......@@ -86,10 +86,10 @@
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Reset auth' %}:</td>
<td width="50%">{% trans 'Test auth all assets manual' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Run' %}</button>
</span>
</td>
</tr>
......@@ -97,6 +97,42 @@
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Using this as cluster admin user' %}
</div>
<div class="panel-body">
<table class="table group_edit" id="add-asset2group">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Select cluster' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for cluster in cluster_remain %}
<option value="{{ cluster.id }}" id="opt_{{ cluster.id }}" >{{ cluster.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-sm" id="btn_add_user_group">{% trans 'Confirm' %}</button>
</td>
</tr>
</form>
{% for cluster in admin_user.cluster_set.all %}
<tr>
<td ><b class="bdg_group" data-gid={{ cluster.id }}>{{ cluster.name }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn-leave-group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
......@@ -192,6 +228,7 @@ function adminUserDelete(name, url) {
jumpserver.assets_selected = {};
jumpserver.asset_groups_selected = {};
$(document).ready(function () {
$('.select2').select2();
})
.on('click', '.btn-delete-admin-user', function () {
var $this = $(this);
......
......@@ -236,7 +236,7 @@
</table>
</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
</div>
......
......@@ -16,8 +16,18 @@
<li>
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="active"><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Attached assets' %}</a>
<li>
<a href="{% url 'assets:system-user-auth' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Auth' %}
</a>
</li>
<li class="active">
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets' %}
</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
</ul>
</div>
......@@ -131,7 +141,7 @@ function initAssetsTable() {
}}
],
ajax_url: '{% url "api-assets:asset-list" %}?system_user_id={{ system_user.id }}',
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "is_online" }],
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "is_connective" }],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
......
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% load bootstrap3 %}
{% 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 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="active">
<a href="{% url 'assets:system-user-auth' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Auth' %}
</a>
</li>
<li>
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Attached assets' %}
</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-12" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ system_user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %}
{% bootstrap_field form.password layout="horizontal" %}
<div class="hr-line-dashed"></div>
{% bootstrap_field form.private_key_file layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" 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 %}
......@@ -17,6 +17,11 @@
<li class="active">
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'assets:system-user-auth' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Auth' %}
</a>
</li>
<li>
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Attached assets' %}
......@@ -61,10 +66,6 @@
<td>{% trans 'Protocol' %}:</td>
<td><b>{{ system_user.protocol }}</b></td>
</tr>
<tr>
<td>{% trans 'Auto push' %}:</td>
<td><b>{{ system_user.auto_push|yesno:"Yes,No,Unknown" }}</b></td>
</tr>
<tr>
<td>{% trans 'Sudo' %}:</td>
<td><b>{{ system_user.sudo }}</b></td>
......@@ -129,14 +130,50 @@
</span>
</td>
</tr>
{# <tr>#}
{# <td width="50%">{% trans 'Change auth period' %}:</td>#}
{# <td>#}
{# <span style="float: right">#}
{# <button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>#}
{# </span>#}
{# </td>#}
{# </tr>#}
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Clusters' %}
</div>
<div class="panel-body">
<table class="table group_edit" id="add-asset2group">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Add to cluster' %}" id="" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for cluster in cluster_remain %}
<option value="{{ cluster.id }}" id="opt_{{ cluster.id }}" >{{ cluster.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-sm" id="btn_add_user_group">{% trans 'Confirm' %}</button>
</td>
</tr>
</form>
{% for cluster in system_user.cluster.all %}
<tr>
<td width="50%">{% trans 'Reset auth' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
<td ><b class="bdg_group" data-gid={{ cluster.id }}>{{ cluster.name }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn-leave-group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
......@@ -150,6 +187,7 @@
{% endblock %}
{% block custom_foot_js %}
<script>
// Todo: 添加自动推送的js
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
......
......@@ -50,6 +50,7 @@ urlpatterns = [
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.SystemUserUpdateView.as_view(), name='system-user-update'),
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/asset/$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/auth/$', views.SystemUserAuthView.as_view(), name='system-user-auth'),
# url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/asset-group$', views.SystemUserAssetGroupView.as_view(),
# name='system-user-asset-group'),
......
......@@ -28,7 +28,7 @@ class AdminUserListView(AdminUserRequiredMixin, TemplateView):
'action': _('Admin user list'),
}
kwargs.update(context)
return super(AdminUserListView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
class AdminUserCreateView(AdminUserRequiredMixin,
......@@ -45,7 +45,7 @@ class AdminUserCreateView(AdminUserRequiredMixin,
'action': 'Create admin user'
}
kwargs.update(context)
return super(AdminUserCreateView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _(
......@@ -56,9 +56,6 @@ class AdminUserCreateView(AdminUserRequiredMixin,
))
return success_message
def form_invalid(self, form):
return super(AdminUserCreateView, self).form_invalid(form)
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser
......@@ -71,7 +68,7 @@ class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
'action': 'Update admin user'
}
kwargs.update(context)
return super(AdminUserUpdateView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:admin-user-detail',
......@@ -79,38 +76,28 @@ class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
return success_url
class AdminUserDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
model = AdminUser
template_name = 'assets/admin_user_detail.html'
context_object_name = 'admin_user'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AdminUser.objects.all())
return super(AdminUserDetailView, self).get(request, *args, **kwargs)
def get_queryset(self):
queryset = []
for cluster in self.object.cluster_set.all():
queryset.extend(list(cluster.assets.all()))
return queryset
object = None
def get_context_data(self, **kwargs):
asset_groups = AssetGroup.objects.all()
assets = self.get_queryset()
cluster_remain = Cluster.objects.exclude(admin_user=self.object)
context = {
'app': 'assets',
'action': 'Admin user detail',
'assets_remain': [asset for asset in Asset.objects.all() if asset not in assets],
'asset_groups': asset_groups,
'cluster_remain': cluster_remain,
}
kwargs.update(context)
return super(AdminUserDetailView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
template_name = 'assets/admin_user_assets.html'
context_object_name = 'admin_user'
object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AdminUser.objects.all())
......
......@@ -28,7 +28,6 @@ from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin
from ..tasks import update_assets_hardware_info
from ..signals import on_asset_created
__all__ = [
......@@ -79,7 +78,6 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
asset.created_by = self.request.user.username or 'Admin'
asset.date_created = timezone.now()
asset.save()
on_asset_created.send(sender=self.__class__, asset=asset)
return super().form_valid(form)
def get_context_data(self, **kwargs):
......@@ -90,10 +88,6 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_url(self):
# update_assets_hardware_info.delay([self.asset._to_secret_json()])
return super().get_success_url()
class AssetModalListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
......
# ~*~ coding: utf-8 ~*~
from __future__ import absolute_import, unicode_literals
from django.contrib import messages
from django.shortcuts import redirect, reverse
from django.utils.translation import ugettext as _
from django.conf import settings
from django.db import transaction
from django.views.generic import TemplateView, ListView
from django.views.generic import TemplateView, ListView, FormView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from ..forms import SystemUserForm, SystemUserUpdateForm
from ..models import Asset, AssetGroup, SystemUser
from ..forms import SystemUserForm, SystemUserUpdateForm, SystemUserAuthForm
from ..models import SystemUser, Cluster
from ..hands import AdminUserRequiredMixin
from perms.utils import associate_system_users_and_assets
__all__ = ['SystemUserCreateView', 'SystemUserUpdateView',
'SystemUserDetailView', 'SystemUserDeleteView',
'SystemUserAssetView', 'SystemUserListView',
'SystemUserAuthView',
]
......@@ -31,7 +31,7 @@ class SystemUserListView(AdminUserRequiredMixin, TemplateView):
'action': _('System user list'),
}
kwargs.update(context)
return super(SystemUserListView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
......@@ -50,11 +50,10 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
'action': _('Create system user'),
}
kwargs.update(context)
return super(SystemUserCreateView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy('assets:system-user-detail',
kwargs={'pk': self.object.pk}),
url = reverse('assets:system-user-detail', kwargs={'pk': self.object.pk})
success_message = _(
'Create system user <a href="{url}">{name}</a> '
'successfully.'.format(url=url, name=self.object.name)
......@@ -74,15 +73,7 @@ class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
'action': _('Update system user')
}
kwargs.update(context)
return super(SystemUserUpdateView, self).get_context_data(**kwargs)
def form_valid(self, form):
response = super(SystemUserUpdateView, self).form_valid(form)
system_user = self.object
assets = system_user.assets.all()
asset_groups = system_user.asset_groups.all()
associate_system_users_and_assets([system_user], assets, asset_groups, force=True)
return response
return super().get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:system-user-detail',
......@@ -96,12 +87,14 @@ class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
model = SystemUser
def get_context_data(self, **kwargs):
cluster_remain = Cluster.objects.exclude(systemuser=self.object)
context = {
'app': _('Assets'),
'action': _('System user detail')
'action': _('System user detail'),
'cluster_remain': cluster_remain,
}
kwargs.update(context)
return super(SystemUserDetailView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
class SystemUserDeleteView(AdminUserRequiredMixin, DeleteView):
......@@ -121,5 +114,36 @@ class SystemUserAssetView(AdminUserRequiredMixin, DetailView):
'action': 'System user asset',
}
kwargs.update(context)
return super(SystemUserAssetView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
class SystemUserAuthView(AdminUserRequiredMixin, SingleObjectMixin,
SuccessMessageMixin, FormView):
model = SystemUser
template_name = 'assets/system_user_auth.html'
context_object_name = 'system_user'
form_class = SystemUserAuthForm
success_message = _("Update auth info success")
object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
def form_valid(self, form):
instance = form.update(self.object)
success_url = reverse('assets:system-user-detail', kwargs={"pk": instance.id})
messages.success(self.request, self.success_message)
return redirect(success_url)
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'System user auth',
}
kwargs.update(context)
return super().get_context_data(**kwargs)
# ~*~ coding: utf-8 ~*~
from __future__ import absolute_import, unicode_literals
import os
from datetime import timedelta
from celery import Celery
from celery.schedules import crontab
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings')
......@@ -21,20 +18,5 @@ app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in setti
app.conf.update(
CELERYBEAT_SCHEDULE={
'refresh-asset-hardware-info': {
'task': 'assets.tasks.update_assets_hardware_period',
'schedule': 60*60*60*24,
'args': (),
},
'test-admin-user-connectability_periode': {
'task': 'assets.tasks.test_admin_user_connectability_period',
'schedule': 60*60*60,
'args': (),
},
'clean_terminal_history': {
'task': 'terminal.tasks.clean_terminal_history',
'schedule': 60*60*60,
'args': (),
}
}
)
......@@ -14,7 +14,7 @@ import hashlib
from email.utils import formatdate
import calendar
import threading
from six import StringIO
from io import StringIO
import uuid
import paramiko
......@@ -180,15 +180,15 @@ def timesince(dt, since='', default="just now"):
return default
def ssh_key_string_to_obj(text):
def ssh_key_string_to_obj(text, password=None):
key = None
try:
key = paramiko.RSAKey.from_private_key( StringIO(text) )
key = paramiko.RSAKey.from_private_key(StringIO(text), password=password)
except paramiko.SSHException:
pass
try:
key = paramiko.DSSKey.from_private_key( StringIO(text) )
key = paramiko.DSSKey.from_private_key(StringIO(text), password=password)
except paramiko.SSHException:
pass
return key
......@@ -222,7 +222,6 @@ def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', h
hostname = os.uname()[1]
f = StringIO()
try:
if type == 'rsa':
private_key_obj = paramiko.RSAKey.generate(length)
......
[{"model": "users.usergroup", "pk": "2e0b2a25-b32f-44a0-b087-f0f17de66e26", "fields": {"is_discard": false, "discard_time": null, "name": "Default", "comment": "Default user group", "date_created": "2017-12-07T08:20:09.593Z", "created_by": "System"}}, {"model": "assets.cluster", "pk": "c0df1aa0-bde7-4226-a69a-d02976888456", "fields": {"name": "Default", "bandwidth": "", "contact": "", "phone": "", "address": "", "intranet": "", "extranet": "", "date_created": "2017-12-07T08:20:09.606Z", "operator": "", "created_by": "System", "comment": "Default Cluster"}}, {"model": "assets.assetgroup", "pk": "881a0460-7989-42af-a588-957efe57fb8e", "fields": {"name": "Default", "created_by": "", "date_created": "2017-12-07T08:20:09.610Z", "comment": "Default asset group", "system_users": []}}, {"model": "users.user", "pk": "edc529d5-0377-4456-831f-ec42cf5b34d2", "fields": {"password": "pbkdf2_sha256$36000$3VcSL8Ap6zHd$14Y4+uZHRU8gwFgEXdIEZZ2+NWV15sRBV2YWgWbdyhY=", "last_login": null, "first_name": "", "last_name": "", "is_active": true, "date_joined": "2017-12-07T08:20:09.494Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "role": "Admin", "avatar": "", "wechat": "", "phone": null, "enable_otp": false, "secret_key_otp": "", "_private_key": "", "_public_key": "", "comment": "Administrator is the super user of system", "is_first_login": false, "date_expired": "2087-11-20T08:20:09.494Z", "created_by": "System", "user_permissions": [], "groups": ["2e0b2a25-b32f-44a0-b087-f0f17de66e26"]}}]
\ No newline at end of file
[{"model": "users.usergroup", "pk": "f3c6b021-59a9-43e7-b022-c2e9bfac84d7", "fields": {"is_discard": false, "discard_time": null, "name": "Default", "comment": "Default user group", "date_created": "2017-12-12T08:13:20.906Z", "created_by": "System"}}, {"model": "users.loginlog", "pk": "328c7d0f-214d-4665-8c7b-f3063718530e", "fields": {"username": "admin", "type": "ST", "ip": "127.0.0.1", "city": "Unknown", "user_agent": "", "datetime": "2017-12-12T08:10:28.973Z"}}, {"model": "users.loginlog", "pk": "a72fa02e-3b2c-40a0-8cb1-0c2f98d8f248", "fields": {"username": "admin", "type": "ST", "ip": "127.0.0.1", "city": "Unknown", "user_agent": "", "datetime": "2017-12-12T08:10:28.980Z"}}, {"model": "assets.cluster", "pk": "a950b8aa-073b-45ab-b72e-5bdfbb614653", "fields": {"name": "Default", "admin_user": null, "bandwidth": "", "contact": "", "phone": "", "address": "", "intranet": "", "extranet": "", "date_created": "2017-12-12T08:13:20.919Z", "operator": "", "created_by": "System", "comment": "Default Cluster"}}, {"model": "assets.assetgroup", "pk": "d742a7be-faf1-4c29-ae0a-e6aa640ab395", "fields": {"name": "Default", "created_by": "", "date_created": "2017-12-12T08:13:20.923Z", "comment": "Default asset group"}}, {"model": "captcha.captchastore", "pk": 1, "fields": {"challenge": "EQEI", "response": "eqei", "hashkey": "c993e8e245252eb8ca40a57c67a63ee9c61dce5c", "expiration": "2017-12-12T08:17:50.235Z"}}, {"model": "users.user", "pk": "61c39c1f-5b57-4268-8180-b6dda235aadd", "fields": {"password": "pbkdf2_sha256$36000$yhYWUEo4DNqj$SpxtdIOm9nwRG+X76jUUlGvdDcLaMBl7Z+rJ8sfSMcU=", "last_login": null, "first_name": "", "last_name": "", "is_active": true, "date_joined": "2017-12-12T08:13:20.827Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "role": "Admin", "avatar": "", "wechat": "", "phone": null, "enable_otp": false, "secret_key_otp": "", "_private_key": "", "_public_key": "", "comment": "Administrator is the super user of system", "is_first_login": false, "date_expired": "2087-11-25T08:13:20.827Z", "created_by": "System", "user_permissions": [], "groups": ["f3c6b021-59a9-43e7-b022-c2e9bfac84d7"]}}]
\ No newline at end of file
......@@ -34,8 +34,8 @@ th a {
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #1ab394 !important;
color: white;
background-color: #d2d2d2 !important;
color: #333 !important;
}
.select2-selection--single,
......
......@@ -3,7 +3,6 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/dataTables/datatables.min.css" %}" rel="stylesheet">
{# <link href="{% static "css/plugins/awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.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/dataTables/dataTables.min.js" %}"></script>
......@@ -48,7 +47,6 @@
{% endblock %}
</form>
{% endblock %}
{# {% block tags_list %}{% endblock %}#}
</div>
{% block table_container %}
<table class="table table-striped table-bordered table-hover" id="editable" >
......@@ -64,7 +62,6 @@
{% endblock %}
<div class="row">
<div class="col-sm-4">
{# Update batch #}
{% block content_bottom_left %} {% endblock %}
</div>
{% block table_pagination %}
......
{% load i18n %}
{% block first_login_message %}
{% if user.is_authenticated and user.is_first_login %}
<div class="alert alert-danger help-message">
{% url 'users:user-first-login' as first_login_url %}
{% blocktrans %}
Your information was incomplete. Please click <a href="{{ first_login_url }}"> this link </a>to complete your information.
{% endblocktrans %}
</div>
{% endif %}
{% endblock %}
{% block update_public_key_message %}
{% if user.is_authenticated and not user.is_public_key_valid %}
<div class="alert alert-danger help-message">
{% url 'users:user-pubkey-update' as user_pubkey_update %}
{% blocktrans %}
Your ssh public key not set or expired. Please click <a href="{{ user_pubkey_update }}"> this link </a>to update your
{% endblocktrans %}
</div>
{% endif %}
{% endblock %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} help-message" >
......
......@@ -5,9 +5,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit">
<title>Jumpserver</title>
<link rel="shortcut icon" href="{% static 'img/facio.ico' %}" type="image/x-icon">
{% include '_head_css_js.html' %}
<link href="{% static 'css/jumpserver.css' %}" rel="stylesheet">
......@@ -20,27 +18,7 @@
<div id="page-wrapper" class="gray-bg">
{% include '_header_bar.html' %}
{% include '_message.html' %}
{% block first_login_message %}
{% if user.is_authenticated and user.is_first_login %}
<div class="alert alert-danger help-message">
{% url 'users:user-first-login' as first_login_url %}
{% blocktrans %}
Your information was incomplete. Please click <a href="{{ first_login_url }}"> this link </a>to complete your information.
{% endblocktrans %}
</div>
{% endif %}
{% endblock %}
{% block update_public_key_message %}
{% if user.is_authenticated and not user.is_public_key_valid %}
<div class="alert alert-danger help-message">
{% url 'users:user-pubkey-update' as user_pubkey_update %}
{% blocktrans %}
Your ssh public key not set or expired. Please click <a href="{{ user_pubkey_update }}"> this link </a>to update your
{% endblocktrans %}
</div>
{% endif %}
{% endblock %}
{% block help_message %}{% endblock %}
{% block help_message %} {% endblock %}
{% block content %}{% endblock %}
{% include '_footer.html' %}
</div>
......
......@@ -124,7 +124,7 @@ class SessionDetailView(SingleObjectMixin, ListView):
model = Session
def get_queryset(self):
self.object = self.get_object(self.model.objects.all())
self.object = self.get_object()
return command_store.filter(session=self.object.id)
def get_context_data(self, **kwargs):
......
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