Commit 17ddb3bb authored by ibuler's avatar ibuler

[Feture] 使用信号解耦

parent 18fbfb44
...@@ -22,7 +22,7 @@ from django.shortcuts import get_object_or_404 ...@@ -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): ...@@ -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): ...@@ -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)
......
...@@ -8,5 +8,6 @@ class AssetsConfig(AppConfig): ...@@ -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()
...@@ -2,4 +2,8 @@ ...@@ -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"
# 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): ...@@ -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,57 +200,46 @@ class SystemUserForm(forms.ModelForm): ...@@ -199,57 +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) public_key = ssh_pubkey_gen(private_key=private_key)
logger.info('Generate private key and public key') system_user.set_auth(password=password, private_key=private_key, public_key=public_key)
else: return system_user
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): def clean_private_key_file(self):
if self.data['auth_method'] == 'K' and \ if self.cleaned_data.get('private_key_file'):
not self.cleaned_data['auto_generate_key']: key_string = self.cleaned_data['private_key_file'].read()
if not self.cleaned_data['private_key_file']: self.cleaned_data['private_key_file'].seek(0)
raise forms.ValidationError(_('Private key required')) if not validate_ssh_private_key(key_string):
else: raise forms.ValidationError(_('Invalid private key'))
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'] return self.cleaned_data['private_key_file']
def clean_password(self): def clean_password(self):
if self.data['auth_method'] == 'P': if not self.cleaned_data.get('password') and \
if not self.cleaned_data.get('password'): not self.cleaned_data.get('private_key_file') and \
raise forms.ValidationError(_('Password required')) not self.cleaned_data.get('auto_generate_key'):
raise forms.ValidationError(_('Auth info required, private_key or password'))
return self.cleaned_data['password'] return self.cleaned_data['password']
class Meta: class Meta:
model = SystemUser model = SystemUser
fields = [ fields = [
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'name', 'username', 'protocol', 'auto_generate_key',
'private_key_file', 'auth_method', 'auto_push', 'sudo', 'password', 'private_key_file', 'auto_push', 'sudo',
'comment', 'shell', 'cluster' 'comment', 'shell', 'cluster'
] ]
widgets = { widgets = {
...@@ -262,52 +252,17 @@ class SystemUserForm(forms.ModelForm): ...@@ -262,52 +252,17 @@ class SystemUserForm(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 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: class Meta:
model = SystemUser model = SystemUser
fields = [ fields = [
'name', 'username', 'protocol', 'name', 'username', 'protocol',
'auth_method', 'auto_push', 'sudo', 'sudo', 'comment', 'shell', 'cluster'
'comment', 'shell', 'cluster'
] ]
widgets = { widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
...@@ -320,9 +275,41 @@ class SystemUserUpdateForm(forms.ModelForm): ...@@ -320,9 +275,41 @@ class SystemUserUpdateForm(forms.ModelForm):
'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, 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): class FileForm(forms.Form):
file = forms.FileField() file = forms.FileField()
...@@ -12,7 +12,7 @@ ...@@ -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
\ No newline at end of file
...@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__) ...@@ -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'))
......
...@@ -2,7 +2,6 @@ ...@@ -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 ...@@ -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',] ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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,
} }
......
...@@ -115,7 +115,7 @@ class SystemUserSerializer(serializers.ModelSerializer): ...@@ -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): ...@@ -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):
......
# -*- 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()
This diff is collapsed.
...@@ -39,30 +39,29 @@ ...@@ -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 @@ ...@@ -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 %}
\ No newline at end of file
...@@ -86,10 +86,10 @@ ...@@ -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 @@ ...@@ -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) { ...@@ -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);
......
...@@ -236,7 +236,7 @@ ...@@ -236,7 +236,7 @@
</table> </table>
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
......
...@@ -16,8 +16,18 @@ ...@@ -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() { ...@@ -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);
......
{% 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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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');#}
{##} {##}
......
...@@ -50,6 +50,7 @@ urlpatterns = [ ...@@ -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'),
......
...@@ -28,7 +28,7 @@ class AdminUserListView(AdminUserRequiredMixin, TemplateView): ...@@ -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, ...@@ -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, ...@@ -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): ...@@ -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): ...@@ -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())
......
...@@ -28,7 +28,6 @@ from .. import forms ...@@ -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): ...@@ -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): ...@@ -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
......
# ~*~ 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): ...@@ -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 ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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)
# ~*~ 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 ...@@ -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': (),
}
} }
) )
...@@ -14,7 +14,7 @@ import hashlib ...@@ -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"): ...@@ -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 ...@@ -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)
......
[{"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"]}}]
\ No newline at end of file \ No newline at end of file
...@@ -34,8 +34,8 @@ th a { ...@@ -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,
......
...@@ -3,7 +3,6 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 %}
......
{% 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" >
......
...@@ -5,9 +5,7 @@ ...@@ -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 @@ ...@@ -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>
......
...@@ -124,7 +124,7 @@ class SessionDetailView(SingleObjectMixin, ListView): ...@@ -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):
......
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