[Feture] 使用信号解耦

pull/828/merge
ibuler 2017-12-13 17:21:08 +08:00
parent 18fbfb449d
commit 17ddb3bbbd
29 changed files with 603 additions and 416 deletions

View File

@ -1 +0,0 @@
from . import signals

View File

@ -22,7 +22,7 @@ from django.shortcuts import get_object_or_404
from common.mixins import IDInFilterMixin from common.mixins import IDInFilterMixin
from common.utils import get_object_or_none 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 get_user_granted_assets, push_users
from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser
from . import serializers from . import serializers
@ -156,7 +156,7 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
class SystemUserAuthInfoApi(generics.RetrieveAPIView): class SystemUserAuthInfoApi(generics.RetrieveAPIView):
"""Get system user auth info""" """Get system user auth info"""
queryset = SystemUser.objects.all() queryset = SystemUser.objects.all()
permission_classes = (IsAppUser,) permission_classes = (IsSuperUserOrAppUser,)
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
system_user = self.get_object() system_user = self.get_object()
@ -166,7 +166,6 @@ class SystemUserAuthInfoApi(generics.RetrieveAPIView):
'username': system_user.username, 'username': system_user.username,
'password': system_user.password, 'password': system_user.password,
'private_key': system_user.private_key, 'private_key': system_user.private_key,
'auth_method': system_user.auth_method,
} }
return Response(data) return Response(data)

View File

@ -8,5 +8,6 @@ class AssetsConfig(AppConfig):
def ready(self): def ready(self):
from .signals import on_app_ready from .signals import on_app_ready
from . import tasks
on_app_ready.send(self.__class__) on_app_ready.send(self.__class__)
super().ready() super().ready()

View File

@ -2,4 +2,8 @@
# #
ADMIN_USER_CONN_CACHE_KEY_PREFIX = "ADMIN_USER_CONN_" 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"

View File

@ -1,4 +1,5 @@
# 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 _
@ -142,7 +143,7 @@ class ClusterForm(forms.ModelForm):
class AdminUserForm(forms.ModelForm): class AdminUserForm(forms.ModelForm):
# Form field name can not start with `_`, so redefine it, # Form field name can not start with `_`, so redefine it,
password = forms.CharField( password = forms.CharField(
widget=forms.PasswordInput, max_length=100, widget=forms.PasswordInput, max_length=128,
strip=True, required=False, strip=True, required=False,
help_text=_('If also set private key, use that first'), help_text=_('If also set private key, use that first'),
) )
@ -199,114 +200,46 @@ class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view # Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=True, required=False) auto_generate_key = forms.BooleanField(initial=True, required=False)
# Form field name can not start with `_`, so redefine it, # 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 # Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False) private_key_file = forms.FileField(required=False)
def __init__(self, *args, **kwargs):
super(SystemUserForm, self).__init__(*args, **kwargs)
def save(self, commit=True): def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save` # Because we define custom field, so we need rewrite :method: `save`
system_user = super(SystemUserForm, self).save(commit=commit) system_user = super().save()
password = self.cleaned_data['password'] password = self.cleaned_data.get('password', None)
private_key_file = self.cleaned_data.get('private_key_file') 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 auto_generate_key:
if password: logger.info('Auto set system user auth')
system_user.password = password system_user.auto_gen_auth()
elif system_user.auth_method == 'K': else:
if self.cleaned_data['auto_generate_key']: private_key = private_key_file.read().strip().decode('utf-8')
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
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'))
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'))
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',
'comment', 'shell', 'cluster'
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
'cluster': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _(' Select clusters')}),
}
help_texts = {
'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 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) public_key = ssh_pubkey_gen(private_key=private_key)
system_user.private_key = private_key system_user.set_auth(password=password, private_key=private_key, public_key=public_key)
system_user.public_key = public_key return system_user
system_user.save()
return self.instance
def clean_private_key_file(self): def clean_private_key_file(self):
if self.data['auth_method'] == 'K' and self.cleaned_data['private_key_file']: if self.cleaned_data.get('private_key_file'):
key_string = self.cleaned_data['private_key_file'].read() key_string = self.cleaned_data['private_key_file'].read()
self.cleaned_data['private_key_file'].seek(0) self.cleaned_data['private_key_file'].seek(0)
if not validate_ssh_private_key(key_string): if not validate_ssh_private_key(key_string):
raise forms.ValidationError(_('Invalid private key')) raise forms.ValidationError(_('Invalid private key'))
return self.cleaned_data['private_key_file'] 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') 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: class Meta:
model = SystemUser model = SystemUser
fields = [ fields = [
'name', 'username', 'protocol', 'name', 'username', 'protocol', 'auto_generate_key',
'auth_method', 'auto_push', 'sudo', 'password', 'private_key_file', 'auto_push', 'sudo',
'comment', 'shell', 'cluster' 'comment', 'shell', 'cluster'
] ]
widgets = { widgets = {
@ -319,10 +252,64 @@ class SystemUserUpdateForm(forms.ModelForm):
help_texts = { help_texts = {
'name': '* required', 'name': '* required',
'username': '* 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', 'auto_push': 'Auto push system user to asset',
} }
class SystemUserUpdateForm(forms.ModelForm):
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol',
'sudo', 'comment', 'shell', 'cluster'
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
'cluster': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _(' Select clusters')}),
}
help_texts = {
'name': '* required',
'username': '* required',
'cluster': 'If auto push checked, then push system user to that cluster assets',
}
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): class FileForm(forms.Form):
file = forms.FileField() file = forms.FileField()

View File

@ -12,7 +12,7 @@
from users.utils import AdminUserRequiredMixin 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 users.models import User, UserGroup
from perms.utils import get_user_granted_assets from perms.utils import get_user_granted_assets
from perms.tasks import push_users from perms.tasks import push_users

View File

@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
class Cluster(models.Model): class Cluster(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=32, verbose_name=_('Name')) 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')) bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth'))
contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact')) contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact'))
phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone')) phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))

View File

@ -2,7 +2,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from __future__ import unicode_literals
import os import os
import logging import logging
import uuid import uuid
@ -12,7 +11,7 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from 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 from .utils import private_key_validator
@ -20,53 +19,41 @@ __all__ = ['AdminUser', 'SystemUser',]
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class AdminUser(models.Model): class AssetUser(models.Model):
"""
A privileged user that ansible can use it to push system user and so on
"""
BECOME_METHOD_CHOICES = (
('sudo', 'sudo'),
('su', 'su'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username')) username = models.CharField(max_length=16, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator,]) _private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
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)
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key')) _public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment')) comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True) date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by')) created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
def __str__(self):
return self.name
@property @property
def password(self): def password(self):
if self._password: if self._password:
return signer.unsign(self._password) return signer.unsign(self._password)
else: else:
return '' return None
@password.setter @password.setter
def password(self, password_raw): 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 @property
def private_key(self): def private_key(self):
if self._private_key: if self._private_key:
key_str = signer.unsign(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: else:
return None return None
@private_key.setter @private_key.setter
def private_key(self, private_key_raw): 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 @property
def private_key_file(self): def private_key_file(self):
@ -74,19 +61,62 @@ class AdminUser(models.Model):
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_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) key_path = os.path.join(tmp_dir, key_name)
if not os.path.exists(key_path): 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 return key_path
@property @property
def public_key(self): def public_key(self):
return signer.unsign(self._public_key) return signer.unsign(self._public_key)
@public_key.setter def set_auth(self, password=None, private_key=None, public_key=None):
def public_key(self, public_key_raw): update_fields = []
self._public_key = signer.sign(public_key_raw) 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 @property
def become_pass(self): def become_pass(self):
@ -131,7 +161,7 @@ class AdminUser(models.Model):
continue continue
class SystemUser(models.Model): class SystemUser(AssetUser):
PROTOCOL_CHOICES = ( PROTOCOL_CHOICES = (
('ssh', 'ssh'), ('ssh', 'ssh'),
) )
@ -139,68 +169,15 @@ class SystemUser(models.Model):
('P', 'Password'), ('P', 'Password'),
('K', 'Public key'), ('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")) 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')) 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')) 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'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell')) 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): def __str__(self):
return self.name 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): def _to_secret_json(self):
"""Push system user use it""" """Push system user use it"""
return { return {
@ -228,7 +205,6 @@ class SystemUser(models.Model):
'name': self.name, 'name': self.name,
'username': self.username, 'username': self.username,
'protocol': self.protocol, 'protocol': self.protocol,
'auth_method': self.auth_method,
'auto_push': self.auto_push, 'auto_push': self.auto_push,
} }

View File

@ -115,7 +115,7 @@ class SystemUserSerializer(serializers.ModelSerializer):
class AssetSystemUserSerializer(serializers.ModelSerializer): class AssetSystemUserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = SystemUser model = SystemUser
fields = ('id', 'name', 'username', 'protocol', 'auth_method', 'comment') fields = ('id', 'name', 'username', 'protocol', 'comment')
class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer): class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer):
@ -202,7 +202,10 @@ class ClusterSerializer(BulkSerializerMixin, serializers.ModelSerializer):
@staticmethod @staticmethod
def get_admin_user_name(obj): 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): class AssetGroupGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer):

View File

@ -1,31 +1,6 @@
# -*- coding: utf-8 -*- # -*- 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() on_app_ready = Signal()
on_system_user_auth_changed = Signal(providing_args=['system_user'])
@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()

View File

@ -3,15 +3,23 @@ import json
from celery import shared_task from celery import shared_task
from django.core.cache import cache from django.core.cache import cache
from django.dispatch import receiver
from django.db.models.signals import post_save
from common.utils import get_object_or_none, capacity_convert, sum_capacity, encrypt_password, get_logger from common.utils import get_object_or_none, capacity_convert, \
sum_capacity, encrypt_password, get_logger
from common.celery import app as celery_app
from .models import SystemUser, AdminUser, Asset from .models import SystemUser, AdminUser, Asset
from .const import ADMIN_USER_CONN_CACHE_KEY_PREFIX, SYSTEM_USER_CONN_CACHE_KEY_PREFIX from .const import ADMIN_USER_CONN_CACHE_KEY_PREFIX, SYSTEM_USER_CONN_CACHE_KEY_PREFIX, \
UPDATE_ASSETS_HARDWARE_PERIOD_LOCK_KEY, TEST_ADMIN_USER_CONNECTABILITY_PEROID_KEY, \
TEST_SYSTEM_USER_CONNECTABILITY_PEROID_KEY, PUSH_SYSTEM_USER_PERIOD_KEY
from .signals import on_app_ready
FORKS = 10 FORKS = 10
TIMEOUT = 60 TIMEOUT = 60
logger = get_logger(__file__) logger = get_logger(__file__)
CACHE_MAX_TIME = 60*60*60
@shared_task @shared_task
@ -75,7 +83,7 @@ def update_assets_hardware_info(assets):
asset.save() asset.save()
for hostname, task in summary['dark'].items(): for hostname, task in summary['dark'].items():
logger.warn("Update {} hardware info error: {}".format( logger.error("Update {} hardware info error: {}".format(
hostname, task[name], hostname, task[name],
)) ))
@ -88,8 +96,15 @@ def update_assets_hardware_period():
Update asset hardware period task Update asset hardware period task
:return: :return:
""" """
assets = Asset.objects.filter(type__in=['Server', 'VM']) if cache.get(UPDATE_ASSETS_HARDWARE_PERIOD_LOCK_KEY) == 1:
update_assets_hardware_info(assets) logger.debug("Update asset hardware period task is running, passed")
return {}
try:
cache.set(UPDATE_ASSETS_HARDWARE_PERIOD_LOCK_KEY, 1, CACHE_MAX_TIME)
assets = Asset.objects.filter(type__in=['Server', 'VM'])
return update_assets_hardware_info(assets)
finally:
cache.set(UPDATE_ASSETS_HARDWARE_PERIOD_LOCK_KEY, 0)
@shared_task @shared_task
@ -100,6 +115,7 @@ def test_admin_user_connectability(admin_user):
:return: :return:
""" """
from ops.utils import run_adhoc from ops.utils import run_adhoc
assets = admin_user.get_related_assets() assets = admin_user.get_related_assets()
hosts = [asset.hostname for asset in assets] hosts = [asset.hostname for asset in assets]
tasks = [ tasks = [
@ -117,16 +133,26 @@ def test_admin_user_connectability(admin_user):
@shared_task @shared_task
def test_admin_user_connectability_period(): def test_admin_user_connectability_period():
# assets = Asset.objects.filter(type__in=['Server', 'VM']) # assets = Asset.objects.filter(type__in=['Server', 'VM'])
admin_users = AdminUser.objects.all() if cache.get(TEST_ADMIN_USER_CONNECTABILITY_PEROID_KEY) == 1:
for admin_user in admin_users: logger.debug("Test admin user connectablity period task is running, passed")
summary = test_admin_user_connectability(admin_user) return
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + admin_user.name, summary, 60*60*60) logger.debug("Test admin user connectablity period task start")
for i in summary['contacted']: try:
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 1, 60*60*60) cache.set(TEST_ADMIN_USER_CONNECTABILITY_PEROID_KEY, 1, CACHE_MAX_TIME)
admin_users = AdminUser.objects.all()
for admin_user in admin_users:
summary = test_admin_user_connectability(admin_user)
for i in summary['dark']: cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + admin_user.name, summary, 60*60*60)
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 0, 60*60*60) for i in summary['contacted']:
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 1, 60*60*60)
for i, error in summary['dark'].items():
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 0, 60*60*60)
logger.error(error)
finally:
cache.set(TEST_ADMIN_USER_CONNECTABILITY_PEROID_KEY, 0)
@shared_task @shared_task
@ -175,9 +201,18 @@ def test_system_user_connectability(system_user):
@shared_task @shared_task
def test_system_user_connectability_period(): def test_system_user_connectability_period():
for system_user in SystemUser.objects.all(): if cache.get(TEST_SYSTEM_USER_CONNECTABILITY_PEROID_KEY) == 1:
summary = test_system_user_connectability(system_user) logger.debug("Test admin user connectablity period task is running, passed")
cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name, summary, 60*60*60) return
logger.debug("Test system user connectablity period task start")
try:
cache.set(TEST_SYSTEM_USER_CONNECTABILITY_PEROID_KEY, 1, CACHE_MAX_TIME)
for system_user in SystemUser.objects.all():
summary = test_system_user_connectability(system_user)
cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name, summary, 60*60*60)
finally:
cache.set(TEST_SYSTEM_USER_CONNECTABILITY_PEROID_KEY, 0)
def get_push_system_user_tasks(system_user): def get_push_system_user_tasks(system_user):
@ -217,18 +252,18 @@ def get_push_system_user_tasks(system_user):
return tasks return tasks
PUSH_SYSTEM_USER_PERIOD_TASK_NAME = 'PUSH SYSTEM USER [{}] PERIOD...' def push_system_user(system_user, assets, task_name=None):
PUSH_SYSTEM_USER_TASK_NAME = 'PUSH SYSTEM USER [{}] ASSETS'
def push_system_user(system_user, assets, name):
from ops.utils import get_task_by_name, run_adhoc_object, \ from ops.utils import get_task_by_name, run_adhoc_object, \
create_task, create_adhoc create_task, create_adhoc
if system_user.auto_push and assets: if system_user.auto_push and assets:
task = get_task_by_name(name) if task_name is None:
task_name = 'PUSH-SYSTEM-USER-{}'.format(system_user.name)
task = get_task_by_name(task_name)
if not task: if not task:
task = create_task(name, created_by="System") logger.debug("Doesn't get task {}, create it".format(task_name))
task = create_task(task_name, created_by="System")
task.save() task.save()
tasks = get_push_system_user_tasks(system_user) tasks = get_push_system_user_tasks(system_user)
hosts = [asset.hostname for asset in assets] hosts = [asset.hostname for asset in assets]
@ -236,36 +271,124 @@ def push_system_user(system_user, assets, name):
adhoc = task.get_latest_adhoc() adhoc = task.get_latest_adhoc()
if not adhoc or adhoc.task != tasks or adhoc.hosts != hosts: if not adhoc or adhoc.task != tasks or adhoc.hosts != hosts:
logger.debug("Task {} not exit or changed, create new version".format(task_name))
adhoc = create_adhoc(task=task, tasks=tasks, pattern='all', adhoc = create_adhoc(task=task, tasks=tasks, pattern='all',
options=options, hosts=hosts, run_as_admin=True) options=options, hosts=hosts, run_as_admin=True)
return run_adhoc_object(adhoc) logger.debug("Task {} start execute".format(task_name))
result = run_adhoc_object(adhoc)
return result.results_summary
else:
msg = "Task {} does'nt execute, because not auto_push " \
"is not True, or not assets".format(task_name)
logger.debug(msg)
return {}
@shared_task
def push_system_user_to_cluster_assets(system_user, task_name=None):
logger.debug("{} task start".format(task_name))
assets = system_user.get_clusters_assets()
summary = push_system_user(system_user, assets, task_name)
for h in summary.get("contacted", []):
logger.debug("Push system user {} to {} success".format(system_user.name, h))
for h, msg in summary.get('dark', {}).items():
logger.error('Push system user {} to {} failed: {}'.format(
system_user.name, h, msg
))
return summary
@shared_task @shared_task
def push_system_user_period(): def push_system_user_period():
logger.debug("Push system user period") if cache.get(PUSH_SYSTEM_USER_PERIOD_KEY) == 1:
for s in SystemUser.objects.filter(auto_push=True): logger.debug("push system user period task is running, passed")
assets = s.get_clusters_assets()
name = PUSH_SYSTEM_USER_PERIOD_TASK_NAME.format(s.name)
push_system_user(s, assets, name)
def push_system_user_to_assets_if_need(system_user, assets=None, asset_groups=None):
assets_to_push = []
system_user_assets = system_user.assets.all()
if assets:
assets_to_push.extend(assets)
if asset_groups:
for group in asset_groups:
assets_to_push.extend(group.assets.all())
assets_need_push = set(assets_to_push) - set(system_user_assets)
if not assets_need_push:
return return
logger.debug("Push system user {} to {} assets".format(
system_user.name, ', '.join([asset.hostname for asset in assets_need_push]) logger.debug("Push system user period task start")
)) try:
result = push_system_user(system_user, assets_need_push, PUSH_SYSTEM_USER_TASK_NAME) cache.set(PUSH_SYSTEM_USER_PERIOD_KEY, 1, timeout=CACHE_MAX_TIME)
system_user.assets.add(*tuple(assets_need_push)) for system_user in SystemUser.objects.filter(auto_push=True):
return result task_name = 'PUSH-SYSTEM-USER-PERIOD'
push_system_user_to_cluster_assets(system_user, task_name)
finally:
cache.set(PUSH_SYSTEM_USER_PERIOD_KEY, 0)
# def push_system_user_to_assets_if_need(system_user, assets=None, asset_groups=None):
# assets_to_push = []
# system_user_assets = system_user.assets.all()
# if assets:
# assets_to_push.extend(assets)
# if asset_groups:
# for group in asset_groups:
# assets_to_push.extend(group.assets.all())
#
# assets_need_push = set(assets_to_push) - set(system_user_assets)
# if not assets_need_push:
# return
# logger.debug("Push system user {} to {} assets".format(
# system_user.name, ', '.join([asset.hostname for asset in assets_need_push])
# ))
# result = push_system_user(system_user, assets_need_push, PUSH_SYSTEM_USER_TASK_NAME)
# system_user.assets.add(*tuple(assets_need_push))
# return result
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
def update_asset_info(sender, instance=None, created=False, **kwargs):
if instance and created:
logger.debug("Receive asset create signal, update asset hardware info")
update_assets_hardware_info.delay([instance])
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
def test_admin_user_connective(sender, instance=None, created=False, **kwargs):
if instance and created:
logger.debug("Receive asset create signal, test admin user connectability")
test_admin_user_connectability_manual.delay(instance)
@receiver(post_save, sender=SystemUser)
def push_system_user_on_change(sender, instance=None, created=False, **kwargs):
if instance and instance.auto_push:
logger.debug("System user `{}` auth changed, push it".format(instance.name))
task_name = "PUSH-SYSTEM-USER-ON-CREATED-{}".format(instance.name)
push_system_user_to_cluster_assets.delay(instance, task_name)
@receiver(post_save, sender=SystemUser)
def push_system_user_on_change(sender, instance=None, update_fields=None, **kwargs):
fields_check = {'_password', '_private_key', '_public_key'}
auth_changed = update_fields & fields_check if update_fields else None
if instance and instance.auto_push and auth_changed:
logger.debug("System user `{}` auth changed, push it".format(instance.name))
task_name = "PUSH-SYSTEM-USER-ON-CREATED-{}".format(instance.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(
{
'update_assets_hardware_period': {
'task': 'assets.tasks.update_assets_hardware_period',
'schedule': 60*60*24,
'args': (),
},
'test-admin-user-connectability_period': {
'task': 'assets.tasks.test_admin_user_connectability_period',
'schedule': 60*60,
'args': (),
},
'push_system_user_period': {
'task': 'assets.tasks.push_system_user_period',
'schedule': 60*60,
'args': (),
}
}
)

View File

@ -39,30 +39,29 @@
{% bootstrap_field form.username layout="horizontal" %} {% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %} {% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.cluster layout="horizontal" %} {% bootstrap_field form.cluster layout="horizontal" %}
<h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.auth_method layout="horizontal" %}
{% block auth %} {% block auth %}
<div class="password-auth hidden"> <h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.password layout="horizontal" %} <div class="auto-generate">
</div>
<div class="public-key-auth">
<div class="form-group"> <div class="form-group">
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label> <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"> <div class="col-sm-8">
{{ form.auto_generate_key}} {{ form.auto_generate_key}}
</div> </div>
</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> </div>
{% endblock %}
<div class="form-group"> <div class="form-group">
<label for="{{ form.as_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label> <label for="{{ form.as_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label>
<div class="col-sm-8"> <div class="col-sm-8">
{{ form.auto_push}} {{ form.auto_push}}
</div> </div>
</div> </div>
{% endblock %}
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.sudo layout="horizontal" %} {% bootstrap_field form.sudo layout="horizontal" %}
{% bootstrap_field form.shell layout="horizontal" %} {% bootstrap_field form.shell layout="horizontal" %}
@ -82,41 +81,20 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var auth_method = '#'+'{{ form.auth_method.id_for_label }}';
var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}'; var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}';
function authMethodDisplay() { function authFieldsDisplay() {
if ($(auth_method).val() == 'P') { if ($(auto_generate_key).prop('checked')) {
$('.password-auth').removeClass('hidden'); $('.auth-fields').addClass('hidden');
$('.public-key-auth').addClass('hidden'); } else {
$('#'+'{{ form.password.id_for_label }}').attr('required', 'required'); $('.auth-fields').removeClass('hidden');
$('#'+'{{ 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');
}
} }
} }
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
authMethodDisplay(); authFieldsDisplay();
$(auth_method).change(function () {
authMethodDisplay();
});
$(auto_generate_key).change(function () { $(auto_generate_key).change(function () {
authMethodDisplay(); authFieldsDisplay();
}); });
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -86,10 +86,10 @@
<table class="table"> <table class="table">
<tbody> <tbody>
<tr class="no-borders-tr"> <tr class="no-borders-tr">
<td width="50%">{% trans 'Reset auth' %}:</td> <td width="50%">{% trans 'Test auth all assets manual' %}:</td>
<td> <td>
<span style="float: right"> <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> </span>
</td> </td>
</tr> </tr>
@ -97,6 +97,42 @@
</table> </table>
</div> </div>
</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> </div>
</div> </div>
@ -192,6 +228,7 @@ function adminUserDelete(name, url) {
jumpserver.assets_selected = {}; jumpserver.assets_selected = {};
jumpserver.asset_groups_selected = {}; jumpserver.asset_groups_selected = {};
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2();
}) })
.on('click', '.btn-delete-admin-user', function () { .on('click', '.btn-delete-admin-user', function () {
var $this = $(this); var $this = $(this);

View File

@ -236,7 +236,7 @@
</table> </table>
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -16,8 +16,18 @@
<li> <li>
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a> <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>
<li class="active"><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center"> <li>
<i class="fa fa-bar-chart-o"></i> {% trans 'Attached assets' %}</a> <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> </li>
</ul> </ul>
</div> </div>
@ -131,7 +141,7 @@ function initAssetsTable() {
}} }}
], ],
ajax_url: '{% url "api-assets:asset-list" %}?system_user_id={{ system_user.id }}', 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() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initDataTable(options);

View File

@ -0,0 +1,73 @@
{% 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 %}

View File

@ -17,6 +17,11 @@
<li class="active"> <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> <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>
<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> <li>
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center"> <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' %} <i class="fa fa-bar-chart-o"></i> {% trans 'Attached assets' %}
@ -61,10 +66,6 @@
<td>{% trans 'Protocol' %}:</td> <td>{% trans 'Protocol' %}:</td>
<td><b>{{ system_user.protocol }}</b></td> <td><b>{{ system_user.protocol }}</b></td>
</tr> </tr>
<tr>
<td>{% trans 'Auto push' %}:</td>
<td><b>{{ system_user.auto_push|yesno:"Yes,No,Unknown" }}</b></td>
</tr>
<tr> <tr>
<td>{% trans 'Sudo' %}:</td> <td>{% trans 'Sudo' %}:</td>
<td><b>{{ system_user.sudo }}</b></td> <td><b>{{ system_user.sudo }}</b></td>
@ -129,14 +130,50 @@
</span> </span>
</td> </td>
</tr> </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> <tr>
<td width="50%">{% trans 'Reset auth' %}:</td> <td ><b class="bdg_group" data-gid={{ cluster.id }}>{{ cluster.name }}</b></td>
<td> <td>
<span style="float: right"> <button class="btn btn-danger pull-right btn-xs btn-leave-group" type="button"><i class="fa fa-minus"></i></button>
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button> </td>
</span>
</td>
</tr> </tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -150,6 +187,7 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
// Todo: 添加自动推送的js
{# function switch_user_status(obj) {#} {# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#} {# var status = $(obj).prop('checked');#}
{##} {##}

View File

@ -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})/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})/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})/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(), # url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/asset-group$', views.SystemUserAssetGroupView.as_view(),
# name='system-user-asset-group'), # name='system-user-asset-group'),

View File

@ -28,7 +28,7 @@ class AdminUserListView(AdminUserRequiredMixin, TemplateView):
'action': _('Admin user list'), 'action': _('Admin user list'),
} }
kwargs.update(context) kwargs.update(context)
return super(AdminUserListView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AdminUserCreateView(AdminUserRequiredMixin, class AdminUserCreateView(AdminUserRequiredMixin,
@ -45,7 +45,7 @@ class AdminUserCreateView(AdminUserRequiredMixin,
'action': 'Create admin user' 'action': 'Create admin user'
} }
kwargs.update(context) kwargs.update(context)
return super(AdminUserCreateView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data): def get_success_message(self, cleaned_data):
success_message = _( success_message = _(
@ -56,9 +56,6 @@ class AdminUserCreateView(AdminUserRequiredMixin,
)) ))
return success_message return success_message
def form_invalid(self, form):
return super(AdminUserCreateView, self).form_invalid(form)
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView): class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser model = AdminUser
@ -71,7 +68,7 @@ class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
'action': 'Update admin user' 'action': 'Update admin user'
} }
kwargs.update(context) kwargs.update(context)
return super(AdminUserUpdateView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_url(self): def get_success_url(self):
success_url = reverse_lazy('assets:admin-user-detail', success_url = reverse_lazy('assets:admin-user-detail',
@ -79,38 +76,28 @@ class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
return success_url return success_url
class AdminUserDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView): class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE model = AdminUser
template_name = 'assets/admin_user_detail.html' template_name = 'assets/admin_user_detail.html'
context_object_name = 'admin_user' context_object_name = 'admin_user'
object = None
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
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
asset_groups = AssetGroup.objects.all() cluster_remain = Cluster.objects.exclude(admin_user=self.object)
assets = self.get_queryset()
context = { context = {
'app': 'assets', 'app': 'assets',
'action': 'Admin user detail', 'action': 'Admin user detail',
'assets_remain': [asset for asset in Asset.objects.all() if asset not in assets], 'cluster_remain': cluster_remain,
'asset_groups': asset_groups,
} }
kwargs.update(context) kwargs.update(context)
return super(AdminUserDetailView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView): class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
template_name = 'assets/admin_user_assets.html' template_name = 'assets/admin_user_assets.html'
context_object_name = 'admin_user' context_object_name = 'admin_user'
object = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AdminUser.objects.all()) self.object = self.get_object(queryset=AdminUser.objects.all())

View File

@ -28,7 +28,6 @@ from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
from ..tasks import update_assets_hardware_info from ..tasks import update_assets_hardware_info
from ..signals import on_asset_created
__all__ = [ __all__ = [
@ -79,7 +78,6 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
asset.created_by = self.request.user.username or 'Admin' asset.created_by = self.request.user.username or 'Admin'
asset.date_created = timezone.now() asset.date_created = timezone.now()
asset.save() asset.save()
on_asset_created.send(sender=self.__class__, asset=asset)
return super().form_valid(form) return super().form_valid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -90,10 +88,6 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) 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): class AssetModalListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE

View File

@ -1,24 +1,24 @@
# ~*~ coding: utf-8 ~*~ # ~*~ 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.utils.translation import ugettext as _
from django.conf import settings
from django.db import transaction 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.views.generic.edit import CreateView, DeleteView, UpdateView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.detail import DetailView, SingleObjectMixin
from ..forms import SystemUserForm, SystemUserUpdateForm from ..forms import SystemUserForm, SystemUserUpdateForm, SystemUserAuthForm
from ..models import Asset, AssetGroup, SystemUser from ..models import SystemUser, Cluster
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
from perms.utils import associate_system_users_and_assets
__all__ = ['SystemUserCreateView', 'SystemUserUpdateView', __all__ = ['SystemUserCreateView', 'SystemUserUpdateView',
'SystemUserDetailView', 'SystemUserDeleteView', 'SystemUserDetailView', 'SystemUserDeleteView',
'SystemUserAssetView', 'SystemUserListView', 'SystemUserAssetView', 'SystemUserListView',
'SystemUserAuthView',
] ]
@ -31,7 +31,7 @@ class SystemUserListView(AdminUserRequiredMixin, TemplateView):
'action': _('System user list'), 'action': _('System user list'),
} }
kwargs.update(context) kwargs.update(context)
return super(SystemUserListView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
@ -50,11 +50,10 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
'action': _('Create system user'), 'action': _('Create system user'),
} }
kwargs.update(context) kwargs.update(context)
return super(SystemUserCreateView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data): def get_success_message(self, cleaned_data):
url = reverse_lazy('assets:system-user-detail', url = reverse('assets:system-user-detail', kwargs={'pk': self.object.pk})
kwargs={'pk': self.object.pk}),
success_message = _( success_message = _(
'Create system user <a href="{url}">{name}</a> ' 'Create system user <a href="{url}">{name}</a> '
'successfully.'.format(url=url, name=self.object.name) 'successfully.'.format(url=url, name=self.object.name)
@ -74,15 +73,7 @@ class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
'action': _('Update system user') 'action': _('Update system user')
} }
kwargs.update(context) kwargs.update(context)
return super(SystemUserUpdateView, self).get_context_data(**kwargs) return super().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
def get_success_url(self): def get_success_url(self):
success_url = reverse_lazy('assets:system-user-detail', success_url = reverse_lazy('assets:system-user-detail',
@ -96,12 +87,14 @@ class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
model = SystemUser model = SystemUser
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
cluster_remain = Cluster.objects.exclude(systemuser=self.object)
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('System user detail') 'action': _('System user detail'),
'cluster_remain': cluster_remain,
} }
kwargs.update(context) kwargs.update(context)
return super(SystemUserDetailView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
class SystemUserDeleteView(AdminUserRequiredMixin, DeleteView): class SystemUserDeleteView(AdminUserRequiredMixin, DeleteView):
@ -121,5 +114,36 @@ class SystemUserAssetView(AdminUserRequiredMixin, DetailView):
'action': 'System user asset', 'action': 'System user asset',
} }
kwargs.update(context) 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)

View File

@ -1,11 +1,8 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from __future__ import absolute_import, unicode_literals
import os import os
from datetime import timedelta
from celery import Celery from celery import Celery
from celery.schedules import crontab
# set the default Django settings module for the 'celery' program. # set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') 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( app.conf.update(
CELERYBEAT_SCHEDULE={ 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': (),
}
} }
) )

View File

@ -14,7 +14,7 @@ import hashlib
from email.utils import formatdate from email.utils import formatdate
import calendar import calendar
import threading import threading
from six import StringIO from io import StringIO
import uuid import uuid
import paramiko import paramiko
@ -180,15 +180,15 @@ def timesince(dt, since='', default="just now"):
return default return default
def ssh_key_string_to_obj(text): def ssh_key_string_to_obj(text, password=None):
key = None key = None
try: try:
key = paramiko.RSAKey.from_private_key( StringIO(text) ) key = paramiko.RSAKey.from_private_key(StringIO(text), password=password)
except paramiko.SSHException: except paramiko.SSHException:
pass pass
try: try:
key = paramiko.DSSKey.from_private_key( StringIO(text) ) key = paramiko.DSSKey.from_private_key(StringIO(text), password=password)
except paramiko.SSHException: except paramiko.SSHException:
pass pass
return key return key
@ -222,7 +222,6 @@ def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', h
hostname = os.uname()[1] hostname = os.uname()[1]
f = StringIO() f = StringIO()
try: try:
if type == 'rsa': if type == 'rsa':
private_key_obj = paramiko.RSAKey.generate(length) private_key_obj = paramiko.RSAKey.generate(length)

View File

@ -1 +1 @@
[{"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"]}}] [{"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"]}}]

View File

@ -34,8 +34,8 @@ th a {
} }
.select2-container--default .select2-results__option--highlighted[aria-selected] { .select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #1ab394 !important; background-color: #d2d2d2 !important;
color: white; color: #333 !important;
} }
.select2-selection--single, .select2-selection--single,

View File

@ -3,7 +3,6 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/dataTables/datatables.min.css" %}" rel="stylesheet"> <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"> <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>
<script src="{% static "js/plugins/dataTables/dataTables.min.js" %}"></script> <script src="{% static "js/plugins/dataTables/dataTables.min.js" %}"></script>
@ -48,7 +47,6 @@
{% endblock %} {% endblock %}
</form> </form>
{% endblock %} {% endblock %}
{# {% block tags_list %}{% endblock %}#}
</div> </div>
{% block table_container %} {% block table_container %}
<table class="table table-striped table-bordered table-hover" id="editable" > <table class="table table-striped table-bordered table-hover" id="editable" >
@ -64,7 +62,6 @@
{% endblock %} {% endblock %}
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
{# Update batch #}
{% block content_bottom_left %} {% endblock %} {% block content_bottom_left %} {% endblock %}
</div> </div>
{% block table_pagination %} {% block table_pagination %}

View File

@ -1,3 +1,25 @@
{% 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 %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="alert alert-{{ message.tags }} help-message" > <div class="alert alert-{{ message.tags }} help-message" >

View File

@ -5,9 +5,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<title>Jumpserver</title> <title>Jumpserver</title>
<link rel="shortcut icon" href="{% static 'img/facio.ico' %}" type="image/x-icon"> <link rel="shortcut icon" href="{% static 'img/facio.ico' %}" type="image/x-icon">
{% include '_head_css_js.html' %} {% include '_head_css_js.html' %}
<link href="{% static 'css/jumpserver.css' %}" rel="stylesheet"> <link href="{% static 'css/jumpserver.css' %}" rel="stylesheet">
@ -20,27 +18,7 @@
<div id="page-wrapper" class="gray-bg"> <div id="page-wrapper" class="gray-bg">
{% include '_header_bar.html' %} {% include '_header_bar.html' %}
{% include '_message.html' %} {% include '_message.html' %}
{% block first_login_message %} {% block help_message %} {% endblock %}
{% 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 content %}{% endblock %} {% block content %}{% endblock %}
{% include '_footer.html' %} {% include '_footer.html' %}
</div> </div>

View File

@ -124,7 +124,7 @@ class SessionDetailView(SingleObjectMixin, ListView):
model = Session model = Session
def get_queryset(self): 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) return command_store.filter(session=self.object.id)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):