mirror of https://github.com/jumpserver/jumpserver
[Feature] 添加signals 解耦代码
parent
cbc000696e
commit
99b4c66b5e
|
@ -0,0 +1 @@
|
||||||
|
from . import signals
|
|
@ -181,10 +181,11 @@ class AssetRefreshHardwareView(generics.RetrieveAPIView):
|
||||||
asset_id = kwargs.get('pk')
|
asset_id = kwargs.get('pk')
|
||||||
asset = get_object_or_404(Asset, pk=asset_id)
|
asset = get_object_or_404(Asset, pk=asset_id)
|
||||||
summary = update_assets_hardware_info([asset])
|
summary = update_assets_hardware_info([asset])
|
||||||
if len(summary['failed']) == 0:
|
print(summary)
|
||||||
return super(AssetRefreshHardwareView, self).retrieve(request, *args, **kwargs)
|
if summary.get('dark'):
|
||||||
|
return Response(summary['dark'].values(), status=501)
|
||||||
else:
|
else:
|
||||||
return Response('', status=502)
|
return Response({"msg": "ok"})
|
||||||
|
|
||||||
|
|
||||||
class AssetAdminUserTestView(AssetRefreshHardwareView):
|
class AssetAdminUserTestView(AssetRefreshHardwareView):
|
||||||
|
|
|
@ -5,3 +5,8 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
class AssetsConfig(AppConfig):
|
class AssetsConfig(AppConfig):
|
||||||
name = 'assets'
|
name = 'assets'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
from .signals import on_app_ready
|
||||||
|
on_app_ready.send(self.__class__)
|
||||||
|
super().ready()
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
ADMIN_USER_CONN_CACHE_KEY_PREFIX = "ADMIN_USER_CONN_"
|
||||||
|
SYSTEM_USER_CONN_CACHE_KEY_PREFIX = 'SYSTEM_USER_CONN_'
|
|
@ -10,52 +10,26 @@ logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class AssetCreateForm(forms.ModelForm):
|
class AssetCreateForm(forms.ModelForm):
|
||||||
# Form field name can not start with `_`, so redefine it,
|
|
||||||
password = forms.CharField(
|
|
||||||
widget=forms.PasswordInput, max_length=100,
|
|
||||||
strip=True, required=False,
|
|
||||||
help_text=_('If also set private key, use that first'),
|
|
||||||
)
|
|
||||||
# Need use upload private key file except paste private key content
|
|
||||||
private_key_file = forms.FileField(required=False)
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
# Because we define custom field, so we need rewrite :method: `save`
|
|
||||||
obj = super().save(commit=commit)
|
|
||||||
password = self.cleaned_data['password']
|
|
||||||
private_key = self.cleaned_data['private_key_file']
|
|
||||||
|
|
||||||
if password:
|
|
||||||
obj.password = password
|
|
||||||
if private_key:
|
|
||||||
obj.private_key = private_key
|
|
||||||
obj.save()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def clean_private_key_file(self):
|
|
||||||
private_key_file = self.cleaned_data['private_key_file']
|
|
||||||
if private_key_file:
|
|
||||||
private_key = private_key_file.read()
|
|
||||||
if not validate_ssh_private_key(private_key):
|
|
||||||
raise forms.ValidationError(_('Invalid private key'))
|
|
||||||
return private_key
|
|
||||||
return private_key_file
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
fields = [
|
fields = [
|
||||||
'hostname', 'ip', 'public_ip', 'port', 'type', 'comment',
|
'hostname', 'ip', 'public_ip', 'port', 'type', 'comment',
|
||||||
'cluster', 'groups', 'status', 'env', 'is_active', 'username',
|
'cluster', 'groups', 'status', 'env', 'is_active',
|
||||||
|
'admin_user'
|
||||||
|
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'groups': forms.SelectMultiple(
|
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
|
||||||
attrs={'class': 'select2',
|
'cluster': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select cluster')}),
|
||||||
'data-placeholder': _('Select asset groups')}),
|
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select admin user')}),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'hostname': '* required',
|
'hostname': '* required',
|
||||||
'ip': '* required',
|
'ip': '* required',
|
||||||
|
'port': '* required',
|
||||||
|
'cluster': '* required',
|
||||||
|
'admin_user': _('Host level admin user, If not set using cluster admin user default')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,16 +39,18 @@ class AssetUpdateForm(forms.ModelForm):
|
||||||
fields = [
|
fields = [
|
||||||
'hostname', 'ip', 'port', 'groups', "cluster", 'is_active',
|
'hostname', 'ip', 'port', 'groups', "cluster", 'is_active',
|
||||||
'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no',
|
'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no',
|
||||||
'cabinet_pos', 'number', 'comment'
|
'cabinet_pos', 'number', 'comment', 'admin_user',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'groups': forms.SelectMultiple(
|
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
|
||||||
attrs={'class': 'select2',
|
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _("Default using cluster admin user")})
|
||||||
'data-placeholder': _('Select asset groups')}),
|
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'hostname': '* required',
|
'hostname': '* required',
|
||||||
'ip': '* required',
|
'ip': '* required',
|
||||||
|
'port': '* required',
|
||||||
|
'cluster': '* required',
|
||||||
|
'admin_user': _('Host level admin user, If not set using cluster admin user default')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
from hashlib import md5
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
from common.utils import signer, ssh_key_string_to_obj
|
from ..const import ADMIN_USER_CONN_CACHE_KEY_PREFIX
|
||||||
from .utils import private_key_validator
|
|
||||||
from .cluster import Cluster
|
from .cluster import Cluster
|
||||||
from .group import AssetGroup
|
from .group import AssetGroup
|
||||||
from .user import AdminUser, SystemUser
|
from .user import AdminUser, SystemUser
|
||||||
|
@ -59,9 +55,7 @@ class Asset(models.Model):
|
||||||
status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, default='In use', verbose_name=_('Asset status'))
|
status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, default='In use', verbose_name=_('Asset status'))
|
||||||
|
|
||||||
# Auth
|
# Auth
|
||||||
username = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('Username'))
|
admin_user = models.ForeignKey('assets.AdminUser', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
|
||||||
_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, ])
|
|
||||||
|
|
||||||
# Some information
|
# Some information
|
||||||
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
|
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
|
||||||
|
@ -105,39 +99,22 @@ class Asset(models.Model):
|
||||||
return False, warning
|
return False, warning
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def password(self):
|
def hardware_info(self):
|
||||||
if self._password:
|
if self.cpu_count:
|
||||||
return signer.unsign(self._password)
|
return '{} Core {} {}'.format(
|
||||||
|
self.cpu_count * self.cpu_cores,
|
||||||
|
self.memory, self.disk_total
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@password.setter
|
|
||||||
def password(self, password_raw):
|
|
||||||
self._password = signer.sign(password_raw)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def private_key(self):
|
def is_connective(self):
|
||||||
if self._private_key:
|
val = cache.get(ADMIN_USER_CONN_CACHE_KEY_PREFIX + self.hostname)
|
||||||
key_str = signer.unsign(self._private_key)
|
if val == 1:
|
||||||
return ssh_key_string_to_obj(key_str)
|
return True
|
||||||
else:
|
else:
|
||||||
return None
|
return False
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return {
|
return {
|
||||||
|
@ -148,25 +125,28 @@ class Asset(models.Model):
|
||||||
'groups': [group.name for group in self.groups.all()],
|
'groups': [group.name for group in self.groups.all()],
|
||||||
}
|
}
|
||||||
|
|
||||||
def is_connective(self):
|
|
||||||
return cache.get(self.hostname)
|
|
||||||
|
|
||||||
def _to_secret_json(self):
|
def _to_secret_json(self):
|
||||||
"""
|
"""
|
||||||
Ansible use it create inventory
|
Ansible use it create inventory, First using asset user,
|
||||||
|
otherwise using cluster admin user
|
||||||
|
|
||||||
Todo: May be move to ops implements it
|
Todo: May be move to ops implements it
|
||||||
"""
|
"""
|
||||||
data = self.to_json()
|
data = self.to_json()
|
||||||
if self.cluster and self.cluster.admin_user:
|
admin_user = None
|
||||||
|
if self.admin_user:
|
||||||
|
admin_user = self.admin_user
|
||||||
|
elif self.cluster and self.cluster.admin_user:
|
||||||
|
admin_user = self.cluster.admin_user
|
||||||
|
if admin_user:
|
||||||
data.update({
|
data.update({
|
||||||
'username': self.cluster.admin_user.username,
|
'username': admin_user.username,
|
||||||
'password': self.cluster.admin_user.password,
|
'password': admin_user.password,
|
||||||
'private_key': self.cluster.admin_user.private_key_file,
|
'private_key': admin_user.private_key_file,
|
||||||
'become': {
|
'become': {
|
||||||
'method': self.cluster.admin_user.become_method,
|
'method': admin_user.become_method,
|
||||||
'user': self.cluster.admin_user.become_user,
|
'user': admin_user.become_user,
|
||||||
'pass': self.cluster.admin_user.become_pass,
|
'pass': admin_user.become_pass,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -18,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', on_delete=models.CASCADE, verbose_name=_("Admin user"))
|
admin_user = models.ForeignKey('assets.AdminUser', null=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'))
|
||||||
|
|
|
@ -96,12 +96,16 @@ class AdminUser(models.Model):
|
||||||
def become_pass(self, password):
|
def become_pass(self, password):
|
||||||
self._become_pass = signer.sign(password)
|
self._become_pass = signer.sign(password)
|
||||||
|
|
||||||
|
def get_related_assets(self):
|
||||||
|
assets = []
|
||||||
|
for cluster in self.cluster_set.all():
|
||||||
|
assets.extend(cluster.assets.all())
|
||||||
|
assets.extend(self.asset_set.all())
|
||||||
|
return list(set(assets))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assets_amount(self):
|
def assets_amount(self):
|
||||||
amount = 0
|
return len(self.get_related_assets())
|
||||||
for cluster in self.cluster_set.all():
|
|
||||||
amount += cluster.assets.all().count()
|
|
||||||
return amount
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
|
@ -209,9 +213,14 @@ class SystemUser(models.Model):
|
||||||
'private_key_file': self.private_key_file,
|
'private_key_file': self.private_key_file,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_clusters_assets(self):
|
||||||
|
from .asset import Asset
|
||||||
|
clusters = self.cluster.all()
|
||||||
|
return Asset.objects.filter(cluster__in=clusters)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assets_amount(self):
|
def assets_amount(self):
|
||||||
return self.assets.count()
|
return len(self.get_clusters_assets())
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from rest_framework import viewsets, serializers, generics
|
from rest_framework import viewsets, serializers, generics
|
||||||
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
|
from rest_framework_bulk.serializers import BulkListSerializer
|
||||||
|
|
||||||
|
from common.mixins import BulkSerializerMixin
|
||||||
from .models import AssetGroup, Asset, Cluster, AdminUser, SystemUser
|
from .models import AssetGroup, Asset, Cluster, AdminUser, SystemUser
|
||||||
from .tasks import SYSTEM_USER_CONN_CACHE_KEY_PREFIX, ADMIN_USER_CONN_CACHE_KEY_PREFIX
|
from .tasks import SYSTEM_USER_CONN_CACHE_KEY_PREFIX, ADMIN_USER_CONN_CACHE_KEY_PREFIX
|
||||||
|
|
||||||
|
@ -140,36 +141,18 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
# system_users = SystemUserSerializer(many=True, read_only=True)
|
|
||||||
# admin_user = AdminUserSerializer(many=False, read_only=True)
|
|
||||||
hardware = serializers.SerializerMethodField()
|
|
||||||
is_online = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
model = Asset
|
model = Asset
|
||||||
list_serializer_class = BulkListSerializer
|
list_serializer_class = BulkListSerializer
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
validators = [] # If not set to [], partial bulk update will be error
|
||||||
@staticmethod
|
|
||||||
def get_hardware(obj):
|
|
||||||
if obj.cpu_count:
|
|
||||||
return '{} Core {} {}'.format(obj.cpu_count*obj.cpu_cores, obj.memory, obj.disk_total)
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_is_online(obj):
|
|
||||||
hostname = obj.hostname
|
|
||||||
if cache.get(hostname) == '1':
|
|
||||||
return True
|
|
||||||
elif cache.get(hostname) == '0':
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return 'Unknown'
|
|
||||||
|
|
||||||
def get_field_names(self, declared_fields, info):
|
def get_field_names(self, declared_fields, info):
|
||||||
fields = super(AssetSerializer, self).get_field_names(declared_fields, info)
|
fields = super().get_field_names(declared_fields, info)
|
||||||
fields.extend(['get_type_display', 'get_env_display'])
|
fields.extend([
|
||||||
|
'get_type_display', 'get_env_display',
|
||||||
|
'hardware_info', 'is_connective',
|
||||||
|
])
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.dispatch import Signal, receiver
|
||||||
|
|
||||||
|
from common.utils import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
on_asset_created = Signal(providing_args=['asset'])
|
||||||
|
on_app_ready = Signal()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(on_asset_created)
|
||||||
|
def update_asset_info(sender, asset=None, **kwargs):
|
||||||
|
from .tasks import update_assets_hardware_info
|
||||||
|
logger.debug("Receive asset create signal, update asset hardware info")
|
||||||
|
update_assets_hardware_info.delay([asset])
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(on_asset_created)
|
||||||
|
def test_admin_user_connective(sender, asset=None, **kwargs):
|
||||||
|
from .tasks import test_admin_user_connectability_manual
|
||||||
|
logger.debug("Receive asset create signal, test admin user connectability")
|
||||||
|
test_admin_user_connectability_manual.delay(asset)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(on_app_ready)
|
||||||
|
def test_admin_user_on_app_ready(sender, **kwargs):
|
||||||
|
from .tasks import test_admin_user_connectability_period
|
||||||
|
logger.debug("Receive app ready signal, test admin connectability")
|
||||||
|
test_admin_user_connectability_period.delay()
|
|
@ -4,16 +4,14 @@ 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 assets.models import SystemUser, AdminUser
|
|
||||||
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 .models import Asset
|
from .models import SystemUser, AdminUser, Asset
|
||||||
|
from .const import ADMIN_USER_CONN_CACHE_KEY_PREFIX, SYSTEM_USER_CONN_CACHE_KEY_PREFIX
|
||||||
|
|
||||||
|
|
||||||
FORKS = 10
|
FORKS = 10
|
||||||
TIMEOUT = 60
|
TIMEOUT = 60
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
ADMIN_USER_CONN_CACHE_KEY_PREFIX = "ADMIN_USER_CONN_"
|
|
||||||
SYSTEM_USER_CONN_CACHE_KEY_PREFIX = 'SYSTEM_USER_CONN_'
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
|
@ -75,6 +73,12 @@ def update_assets_hardware_info(assets):
|
||||||
if k.startswith('___'):
|
if k.startswith('___'):
|
||||||
setattr(asset, k.strip('_'), v)
|
setattr(asset, k.strip('_'), v)
|
||||||
asset.save()
|
asset.save()
|
||||||
|
|
||||||
|
for hostname, task in summary['dark'].items():
|
||||||
|
logger.warn("Update {} hardware info error: {}".format(
|
||||||
|
hostname, task[name],
|
||||||
|
))
|
||||||
|
|
||||||
return summary
|
return summary
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,8 +100,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.assets.all()
|
assets = admin_user.get_related_assets()
|
||||||
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
|
|
||||||
hosts = [asset.hostname for asset in assets]
|
hosts = [asset.hostname for asset in assets]
|
||||||
tasks = [
|
tasks = [
|
||||||
{
|
{
|
||||||
|
@ -126,6 +129,7 @@ def test_admin_user_connectability_period():
|
||||||
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 0, 60*60*60)
|
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 0, 60*60*60)
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
def test_admin_user_connectability_manual(asset):
|
def test_admin_user_connectability_manual(asset):
|
||||||
from ops.utils import run_adhoc
|
from ops.utils import run_adhoc
|
||||||
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
|
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
|
||||||
|
@ -140,8 +144,10 @@ def test_admin_user_connectability_manual(asset):
|
||||||
]
|
]
|
||||||
result = run_adhoc(hosts, tasks=tasks, pattern="all", run_as_admin=True)
|
result = run_adhoc(hosts, tasks=tasks, pattern="all", run_as_admin=True)
|
||||||
if result.results_summary['dark']:
|
if result.results_summary['dark']:
|
||||||
|
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + asset.hostname, 0, 60*60*60)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + asset.hostname, 1, 60*60* 60)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -153,7 +159,7 @@ def test_system_user_connectability(system_user):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
from ops.utils import run_adhoc
|
from ops.utils import run_adhoc
|
||||||
assets = system_user.assets.all()
|
assets = system_user.get_clusters_assets()
|
||||||
hosts = [asset.hostname for asset in assets]
|
hosts = [asset.hostname for asset in assets]
|
||||||
tasks = [
|
tasks = [
|
||||||
{
|
{
|
||||||
|
@ -171,7 +177,7 @@ def test_system_user_connectability(system_user):
|
||||||
def test_system_user_connectability_period():
|
def test_system_user_connectability_period():
|
||||||
for system_user in SystemUser.objects.all():
|
for system_user in SystemUser.objects.all():
|
||||||
summary = test_system_user_connectability(system_user)
|
summary = test_system_user_connectability(system_user)
|
||||||
cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name , summary, 60*60*60)
|
cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name, summary, 60*60*60)
|
||||||
|
|
||||||
|
|
||||||
def get_push_system_user_tasks(system_user):
|
def get_push_system_user_tasks(system_user):
|
||||||
|
@ -207,19 +213,12 @@ def get_push_system_user_tasks(system_user):
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
PUSH_SYSTEM_USER_PERIOD_TASK_NAME = 'PUSH SYSTEM USER {} PERIOD...'
|
PUSH_SYSTEM_USER_PERIOD_TASK_NAME = 'PUSH SYSTEM USER [{}] PERIOD...'
|
||||||
PUSH_SYSTEM_USER_TASK_NAME = 'PUSH SYSTEM USER {} ASSETS'
|
PUSH_SYSTEM_USER_TASK_NAME = 'PUSH SYSTEM USER [{}] ASSETS'
|
||||||
|
|
||||||
|
|
||||||
def get_push_system_user_task(system_user):
|
|
||||||
from ops.utils import get_task_by_name
|
|
||||||
task = get_task_by_name(PUSH_SYSTEM_USER_PERIOD_TASK_NAME.format(system_user.name))
|
|
||||||
return task
|
|
||||||
|
|
||||||
|
|
||||||
def push_system_user(system_user, assets, name):
|
def push_system_user(system_user, assets, name):
|
||||||
|
@ -246,7 +245,7 @@ def push_system_user(system_user, assets, name):
|
||||||
def push_system_user_period():
|
def push_system_user_period():
|
||||||
logger.debug("Push system user period")
|
logger.debug("Push system user period")
|
||||||
for s in SystemUser.objects.filter(auto_push=True):
|
for s in SystemUser.objects.filter(auto_push=True):
|
||||||
assets = s.assets.all()
|
assets = s.get_clusters_assets()
|
||||||
|
|
||||||
name = PUSH_SYSTEM_USER_PERIOD_TASK_NAME.format(s.name)
|
name = PUSH_SYSTEM_USER_PERIOD_TASK_NAME.format(s.name)
|
||||||
push_system_user(s, assets, name)
|
push_system_user(s, assets, name)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
{% block help_message %}
|
{% block help_message %}
|
||||||
<div class="alert alert-info help-message">
|
<div class="alert alert-info help-message">
|
||||||
管理用户是 服务器上已存在的特权用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等
|
管理用户是 服务器上已存在的特权用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。可以设置主机级别管理用户,也设置集群级别管理用户,这样资产可以不用再单独设置
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,18 @@
|
||||||
<h3>{% trans 'Basic' %}</h3>
|
<h3>{% trans 'Basic' %}</h3>
|
||||||
{% bootstrap_field form.hostname layout="horizontal" %}
|
{% bootstrap_field form.hostname layout="horizontal" %}
|
||||||
{% bootstrap_field form.ip layout="horizontal" %}
|
{% bootstrap_field form.ip layout="horizontal" %}
|
||||||
{% bootstrap_field form.public_ip layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.port layout="horizontal" %}
|
{% bootstrap_field form.port layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.cluster layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.public_ip layout="horizontal" %}
|
||||||
{% bootstrap_field form.type layout="horizontal" %}
|
{% bootstrap_field form.type layout="horizontal" %}
|
||||||
{% bootstrap_field form.env layout="horizontal" %}
|
{% bootstrap_field form.env layout="horizontal" %}
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans 'Auth' %}</h3>
|
<h3>{% trans 'Auth' %}</h3>
|
||||||
{% bootstrap_field form.username layout="horizontal" %}
|
{% bootstrap_field form.admin_user layout="horizontal" %}
|
||||||
{% bootstrap_field form.password layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.private_key_file layout="horizontal" %}
|
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans 'Cluster and group' %}</h3>
|
<h3>{% trans 'Group' %}</h3>
|
||||||
{% bootstrap_field form.cluster layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.groups layout="horizontal" %}
|
{% bootstrap_field form.groups layout="horizontal" %}
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
|
@ -50,11 +48,11 @@
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2();
|
$('.select2').select2();
|
||||||
$("#id_tags").select2({
|
{# $("#id_tags").select2({#}
|
||||||
tags: true,
|
{# tags: true,#}
|
||||||
maximumSelectionLength: 8 //最多能够选择的个数
|
{# maximumSelectionLength: 8 //最多能够选择的个数#}
|
||||||
//closeOnSelect: false
|
{# //closeOnSelect: false#}
|
||||||
});
|
{# });#}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -19,9 +19,6 @@
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<a href="{% url 'assets:asset-detail' pk=asset.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Asset detail' %} </a>
|
<a href="{% url 'assets:asset-detail' pk=asset.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Asset detail' %} </a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a href="{% url 'assets:asset-detail' pk=asset.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Auth' %} </a>
|
|
||||||
</li>
|
|
||||||
{% if user.is_superuser %}
|
{% if user.is_superuser %}
|
||||||
<li class="pull-right">
|
<li class="pull-right">
|
||||||
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>Update</a>
|
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>Update</a>
|
||||||
|
@ -198,14 +195,6 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>{% trans 'Reset auth' %}:</td>
|
|
||||||
<td>
|
|
||||||
<span class="pull-right">
|
|
||||||
<button type="button" class="btn btn-primary btn-xs" id="btn_test_admin_user" style="width: 54px;">{% trans 'Reset' %}</button>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -239,7 +228,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td ><b class="bdg_group" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td>
|
<td ><b class="bdg_group" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button>
|
<button class="btn btn-danger pull-right btn-xs btn-leave-group" type="button"><i class="fa fa-minus"></i></button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -274,7 +263,7 @@ function updateAssetGroups(groups) {
|
||||||
$('#add-asset2group tbody').append(
|
$('#add-asset2group tbody').append(
|
||||||
'<tr>' +
|
'<tr>' +
|
||||||
'<td><b class="bdg_group" data-gid="' + index + '">' + group_name + '</b></td>' +
|
'<td><b class="bdg_group" data-gid="' + index + '">' + group_name + '</b></td>' +
|
||||||
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_group" type="button"><i class="fa fa-minus"></i></button></td>' +
|
'<td><button class="btn btn-danger btn-xs pull-right btn-leave-group" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||||
'</tr>'
|
'</tr>'
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -288,44 +277,20 @@ function updateAssetGroups(groups) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAssetSystemUser(system_users) {
|
|
||||||
var the_url = "{% url 'api-assets:asset-update-system-users' pk=asset.id %}";
|
|
||||||
var body = {
|
|
||||||
system_users: Object.assign([], system_users)
|
|
||||||
};
|
|
||||||
var success = function(data) {
|
|
||||||
$('.select2-selection__rendered').empty();
|
|
||||||
$('#groups_selected').val('');
|
|
||||||
$.map(jumpserver.system_user_selected, function(name, index) {
|
|
||||||
$('#opt_' + index).remove();
|
|
||||||
|
|
||||||
$('#add-asset2systemuser tbody').append(
|
|
||||||
'<tr>' +
|
|
||||||
'<td><b class="bdg_group" data-sid="' + index + '">' + name + '</b></td>' +
|
|
||||||
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_system" type="button"><i class="fa fa-minus"></i></button></td>' +
|
|
||||||
'</tr>'
|
|
||||||
)
|
|
||||||
});
|
|
||||||
// clear jumpserver.groups_selected
|
|
||||||
jumpserver.system_user_selected = {};
|
|
||||||
};
|
|
||||||
APIUpdateAttr({
|
|
||||||
url: the_url,
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
success: success
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshAssetHardware() {
|
function refreshAssetHardware() {
|
||||||
var the_url = "{% url 'api-assets:asset-refresh' pk=asset.id %}";
|
var the_url = "{% url 'api-assets:asset-refresh' pk=asset.id %}";
|
||||||
var success = function (data) {
|
var success = function (data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
|
var error = function (data) {
|
||||||
|
alert(data)
|
||||||
|
};
|
||||||
APIUpdateAttr({
|
APIUpdateAttr({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
success: success,
|
success: success,
|
||||||
|
error: error,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -337,13 +302,6 @@ $(document).ready(function () {
|
||||||
var data = evt.params.data;
|
var data = evt.params.data;
|
||||||
delete jumpserver.groups_selected[data.id]
|
delete jumpserver.groups_selected[data.id]
|
||||||
});
|
});
|
||||||
$('.select2.system-user').select2().on('select2:select', function(evt) {
|
|
||||||
var data = evt.params.data;
|
|
||||||
jumpserver.system_user_selected[data.id] = data.text;
|
|
||||||
}).on('select2:unselect', function(evt) {
|
|
||||||
var data = evt.params.data;
|
|
||||||
delete jumpserver.system_user_selected[data.id]
|
|
||||||
})
|
|
||||||
}).on('click', '#is_active', function () {
|
}).on('click', '#is_active', function () {
|
||||||
var the_url = '{% url "api-assets:asset-detail" pk=asset.id %}';
|
var the_url = '{% url "api-assets:asset-detail" pk=asset.id %}';
|
||||||
var checked = $(this).prop('checked');
|
var checked = $(this).prop('checked');
|
||||||
|
@ -370,11 +328,11 @@ $(document).ready(function () {
|
||||||
return $(this).data('gid');
|
return $(this).data('gid');
|
||||||
}).get();
|
}).get();
|
||||||
$.map(jumpserver.groups_selected, function(value, index) {
|
$.map(jumpserver.groups_selected, function(value, index) {
|
||||||
groups.push(parseInt(index));
|
groups.push(index);
|
||||||
$('#opt_' + index).remove();
|
$('#opt_' + index).remove();
|
||||||
});
|
});
|
||||||
updateAssetGroups(groups)
|
updateAssetGroups(groups)
|
||||||
}).on('click', '.btn_leave_group', function() {
|
}).on('click', '.btn-leave-group', function() {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var $tr = $this.closest('tr');
|
var $tr = $this.closest('tr');
|
||||||
var $badge = $tr.find('.bdg_group');
|
var $badge = $tr.find('.bdg_group');
|
||||||
|
@ -388,34 +346,6 @@ $(document).ready(function () {
|
||||||
return $(this).data('gid');
|
return $(this).data('gid');
|
||||||
}).get();
|
}).get();
|
||||||
updateAssetGroups(groups)
|
updateAssetGroups(groups)
|
||||||
}).on('click', '.btn-system-user', function () {
|
|
||||||
if (Object.keys(jumpserver.system_user_selected).length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var system_users = $('.bdg_group').map(function() {
|
|
||||||
return $(this).data('sid');
|
|
||||||
}).get();
|
|
||||||
$.map(jumpserver.system_user_selected, function(value, index) {
|
|
||||||
system_users.push(parseInt(index));
|
|
||||||
$('#opt_' + index).remove();
|
|
||||||
});
|
|
||||||
updateAssetSystemUser(system_users)
|
|
||||||
|
|
||||||
}).on('click', '.btn_leave_system', function () {
|
|
||||||
var $this = $(this);
|
|
||||||
var $tr = $this.closest('tr');
|
|
||||||
var $badge = $tr.find('.bdg_group');
|
|
||||||
var sid = $badge.data('sid');
|
|
||||||
var name = $badge.html() || $badge.text();
|
|
||||||
$('#groups_selected').append(
|
|
||||||
'<option value="' + sid + '" id="opt_' + sid + '">' + name + '</option>'
|
|
||||||
);
|
|
||||||
$tr.remove();
|
|
||||||
var system_users = $('.bdg_group').map(function () {
|
|
||||||
return $(this).data('sid');
|
|
||||||
}).get();
|
|
||||||
updateAssetSystemUser(system_users)
|
|
||||||
|
|
||||||
}).on('click', '.btn-delete-asset', function () {
|
}).on('click', '.btn-delete-asset', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var name = "{{ asset.hostname }}";
|
var name = "{{ asset.hostname }}";
|
||||||
|
@ -424,7 +354,7 @@ $(document).ready(function () {
|
||||||
var redirect_url = "{% url 'assets:asset-list' %}";
|
var redirect_url = "{% url 'assets:asset-list' %}";
|
||||||
objectDelete($this, name, the_url, redirect_url);
|
objectDelete($this, name, the_url, redirect_url);
|
||||||
}).on('click', '#btn_refresh_asset', function () {
|
}).on('click', '#btn_refresh_asset', function () {
|
||||||
alert('请等待几秒, 等待完成');
|
alert('关闭alert, 等待完成, 自动刷新页面');
|
||||||
refreshAssetHardware()
|
refreshAssetHardware()
|
||||||
}).on('click', '#btn_test_admin_user', function () {
|
}).on('click', '#btn_test_admin_user', function () {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -436,6 +366,5 @@ $(document).ready(function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -67,16 +67,16 @@
|
||||||
<form>
|
<form>
|
||||||
<tr class="no-borders-tr">
|
<tr class="no-borders-tr">
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<select data-placeholder="{% trans 'Select assets' %}" class="select2 system-user-select" style="width: 100%" multiple="" tabindex="4">
|
<select data-placeholder="{% trans 'Select assets' %}" class="select2 asset-select" style="width: 100%" multiple="" tabindex="4">
|
||||||
{% for system_user in system_users %}
|
{% for asset in assets_remain %}
|
||||||
<option value="{{ system_user.id }}"> {{ system_user.name }} </option>
|
<option value="{{ asset.id }}"> {{ asset.hostname }} </option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="no-borders-tr">
|
<tr class="no-borders-tr">
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<button type="button" class="btn btn-primary btn-sm btn-push-system-user">{% trans 'Push' %}</button>
|
<button type="button" class="btn btn-primary btn-sm btn-add-asset">{% trans 'Add' %}</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</form>
|
</form>
|
||||||
|
@ -93,28 +93,30 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
jumpserver.assets_selected = {};
|
jumpserver.assets_selected = {};
|
||||||
function updateGroupAssets(assets) {
|
function updateGroupAssets(assets) {
|
||||||
var the_url = "{}";
|
var the_url = "{}";
|
||||||
var body = {
|
var body = {
|
||||||
assets: Object.assign([], assets)
|
assets: Object.assign([], assets)
|
||||||
};
|
};
|
||||||
var $data_table = $("#asset_list_table").DataTable();
|
var $data_table = $("#asset_list_table").DataTable();
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
$('.select2-selection__rendered').empty();
|
$('.select2-selection__rendered').empty();
|
||||||
$.map(jumpserver.assets_selected, function(asset_ip, index) {
|
$.map(jumpserver.assets_selected, function(asset_ip, index) {
|
||||||
$('#opt_' + index).remove();
|
$('#opt_' + index).remove();
|
||||||
$data_table.ajax.reload();
|
$data_table.ajax.reload();
|
||||||
});
|
|
||||||
jumpserver.groups_selected = {};
|
|
||||||
};
|
|
||||||
APIUpdateAttr({
|
|
||||||
url: the_url,
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
method: 'PUT',
|
|
||||||
success: success
|
|
||||||
});
|
});
|
||||||
}
|
jumpserver.groups_selected = {};
|
||||||
|
};
|
||||||
|
console.log(body);
|
||||||
|
|
||||||
|
//APIUpdateAttr({
|
||||||
|
// url: the_url,
|
||||||
|
// body: JSON.stringify(body),
|
||||||
|
// method: 'PUT',
|
||||||
|
// success: success
|
||||||
|
//});
|
||||||
|
}
|
||||||
|
|
||||||
function leaveGroup(obj, name, url, data) {
|
function leaveGroup(obj, name, url, data) {
|
||||||
function doDelete() {
|
function doDelete() {
|
||||||
|
@ -203,20 +205,21 @@ $(document).ready(function () {
|
||||||
$('.select2').select2();
|
$('.select2').select2();
|
||||||
|
|
||||||
$('.select2.asset-select').select2()
|
$('.select2.asset-select').select2()
|
||||||
.on('select2:select', function(evt) {
|
.on('select2:select', function(evt) {
|
||||||
var data = evt.params.data;
|
var data = evt.params.data;
|
||||||
jumpserver.assets_selected[data.id] = data.text;
|
jumpserver.assets_selected[data.id] = data.text;
|
||||||
console.log(jumpserver.assets_selected)
|
console.log(jumpserver.assets_selected)
|
||||||
})
|
})
|
||||||
.on('select2:unselect', function(evt) {
|
.on('select2:unselect', function(evt) {
|
||||||
var data = evt.params.data;
|
var data = evt.params.data;
|
||||||
delete jumpserver.assets_selected[data.id]
|
delete jumpserver.assets_selected[data.id]
|
||||||
});
|
});
|
||||||
|
|
||||||
initTable();
|
initTable();
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.on('click', ".btn-asset-group-add-asset", function () {
|
.on('click', ".btn-add-asset", function () {
|
||||||
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -225,16 +228,8 @@ $(document).ready(function () {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.on('click', '.btn-push-system-user', function () {
|
|
||||||
var data = $('.system-user-select').select2();
|
|
||||||
var system_id = data.val()[0];
|
|
||||||
if (!system_id) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
pushSystemUser(system_id)
|
|
||||||
})
|
|
||||||
|
|
||||||
.on('click', '.btn_asset_delete', function () {
|
.on('click', '.btn-leave-group', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}";
|
var the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}";
|
||||||
var name = $(this).closest("tr").find(":nth-child(1) > a").html();
|
var name = $(this).closest("tr").find(":nth-child(1) > a").html();
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
<th class="text-center">{% trans 'Type' %}</th>
|
<th class="text-center">{% trans 'Type' %}</th>
|
||||||
<th class="text-center">{% trans 'Env' %}</th>
|
<th class="text-center">{% trans 'Env' %}</th>
|
||||||
<th class="text-center">{% trans 'Hardware' %}</th>
|
<th class="text-center">{% trans 'Hardware' %}</th>
|
||||||
<th class="text-center">{% trans 'Valid' %}</th>
|
<th class="text-center">{% trans 'Active' %}</th>
|
||||||
<th class="text-center">{% trans 'Alive' %}</th>
|
<th class="text-center">{% trans 'Connective' %}</th>
|
||||||
<th class="text-center">{% trans 'Action' %}</th>
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
<option value="delete">{% trans 'Delete selected' %}</option>
|
<option value="delete">{% trans 'Delete selected' %}</option>
|
||||||
<option value="update">{% trans 'Update selected' %}</option>
|
<option value="update">{% trans 'Update selected' %}</option>
|
||||||
<option value="deactive">{% trans 'Deactive selected' %}</option>
|
<option value="deactive">{% trans 'Deactive selected' %}</option>
|
||||||
<option value="active">{% trans 'Active' %}</option>
|
<option value="active">{% trans 'Active selected' %}</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
||||||
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
|
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
|
||||||
|
@ -114,8 +114,8 @@ $(document).ready(function(){
|
||||||
],
|
],
|
||||||
ajax_url: '{% url "api-assets:asset-list" %}',
|
ajax_url: '{% url "api-assets:asset-list" %}',
|
||||||
columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
|
columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||||
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware"},
|
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"},
|
||||||
{data: "is_active" }, {data: "is_online"}, {data: "id" }],
|
{data: "is_active" }, {data: "is_connective"}, {data: "id" }],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
var table = jumpserver.initDataTable(options);
|
var table = jumpserver.initDataTable(options);
|
||||||
|
@ -180,24 +180,28 @@ $(document).ready(function(){
|
||||||
$data_table.rows({selected: true}).every(function(){
|
$data_table.rows({selected: true}).every(function(){
|
||||||
id_list.push(this.data().id);
|
id_list.push(this.data().id);
|
||||||
});
|
});
|
||||||
if (id_list.length == 0) {
|
if (id_list.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var the_url = "{% url 'api-assets:asset-list' %}";
|
var the_url = "{% url 'api-assets:asset-list' %}";
|
||||||
|
|
||||||
function doDeactive() {
|
function doDeactive() {
|
||||||
var body = $.each(id_list, function(index, asset_object) {
|
var data = [];
|
||||||
asset_object['is_active'] = false;
|
$.each(id_list, function(index, object_id) {
|
||||||
|
var obj = {"pk": object_id, "is_active": false};
|
||||||
|
data.push(obj);
|
||||||
});
|
});
|
||||||
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
|
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(data)});
|
||||||
$data_table.ajax.reload();
|
$data_table.ajax.reload();
|
||||||
jumpserver.checked = false;
|
jumpserver.checked = false;
|
||||||
}
|
}
|
||||||
function doActive() {
|
function doActive() {
|
||||||
var body = $.each(id_list, function(index, asset_object) {
|
var data = [];
|
||||||
asset_object['is_active'] = true;
|
$.each(id_list, function(index, object_id) {
|
||||||
|
var obj = {"pk": object_id, "is_active": true};
|
||||||
|
data.push(obj);
|
||||||
});
|
});
|
||||||
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
|
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(data)});
|
||||||
$data_table.ajax.reload();
|
$data_table.ajax.reload();
|
||||||
jumpserver.checked = false;
|
jumpserver.checked = false;
|
||||||
}
|
}
|
||||||
|
@ -220,8 +224,13 @@ $(document).ready(function(){
|
||||||
var msg = "{% trans 'Asset Deleting failed.' %}";
|
var msg = "{% trans 'Asset Deleting failed.' %}";
|
||||||
swal("{% trans 'Asset Delete' %}", msg, "error");
|
swal("{% trans 'Asset Delete' %}", msg, "error");
|
||||||
};
|
};
|
||||||
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
|
var url_delete = the_url + '?id__in=' + JSON.stringify(id_list);
|
||||||
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
|
APIUpdateAttr({
|
||||||
|
url: url_delete,
|
||||||
|
method: 'DELETE',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
$data_table.ajax.reload();
|
$data_table.ajax.reload();
|
||||||
jumpserver.checked = false;
|
jumpserver.checked = false;
|
||||||
});
|
});
|
||||||
|
@ -248,65 +257,6 @@ $(document).ready(function(){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
{##}
|
|
||||||
{#.on('click', '#btn_asset_bulk_update', function () {#}
|
|
||||||
{# var json_data = $("#fm_asset_bulk_update").serializeObject();#}
|
|
||||||
{# var body = {};#}
|
|
||||||
{# body.enable_otp = (json_data.enable_otp === 'on')? true: false;#}
|
|
||||||
{# if (json_data.type != '') {#}
|
|
||||||
{# body.type = json_data.type;#}
|
|
||||||
{# }#}
|
|
||||||
{# if (json_data.groups != undefined) {#}
|
|
||||||
{# body.groups = json_data.groups;#}
|
|
||||||
{# }#}
|
|
||||||
{# if (typeof body.groups === 'string') {#}
|
|
||||||
{# body.groups = [parseInt(body.groups)]#}
|
|
||||||
{# } else if(typeof body.groups === 'array') {#}
|
|
||||||
{# var new_groups = body.groups.map(Number);#}
|
|
||||||
{# body.groups = new_groups;#}
|
|
||||||
{# }#}
|
|
||||||
{##}
|
|
||||||
{# if (json_data.system_users != undefined) {#}
|
|
||||||
{# body.system_users = json_data.system_users;#}
|
|
||||||
{# }#}
|
|
||||||
{# if (typeof body.system_users === 'string') {#}
|
|
||||||
{# body.system_users = [parseInt(body.system_users)]#}
|
|
||||||
{# } else if(typeof body.system_users === 'array') {#}
|
|
||||||
{# var new_users = body.system_users.map(Number);#}
|
|
||||||
{# body.system_users = new_users;#}
|
|
||||||
{# }#}
|
|
||||||
{##}
|
|
||||||
{# if (json_data.tags != undefined) {#}
|
|
||||||
{# body.tags = json_data.tags;#}
|
|
||||||
{# }#}
|
|
||||||
{# if (typeof body.tags == 'string') {#}
|
|
||||||
{# body.tags = [parseInt(body.tags)];#}
|
|
||||||
{# } else if (typeof body.tags === 'array') {#}
|
|
||||||
{# var new_tags = body.tags.map(Number);#}
|
|
||||||
{# body.tags = new_tags;#}
|
|
||||||
{# }#}
|
|
||||||
{##}
|
|
||||||
{# var $data_table = $('#asset_list_table').DataTable();#}
|
|
||||||
{# var post_list = [];#}
|
|
||||||
{# $data_table.rows({selected: true}).every(function(){#}
|
|
||||||
{# var content = Object.assign({id: this.data().id}, body);#}
|
|
||||||
{# post_list.push(content);#}
|
|
||||||
{# });#}
|
|
||||||
{# if (post_list === []) {#}
|
|
||||||
{# return false#}
|
|
||||||
{# }#}
|
|
||||||
{# var the_url = "{% url 'api-assets:asset-list' %}";#}
|
|
||||||
{# var success = function() {#}
|
|
||||||
{# var msg = "{% trans 'The selected assets has been updated successfully.' %}";#}
|
|
||||||
{# swal("{% trans 'Asset Updated' %}", msg, "success");#}
|
|
||||||
{# $('#asset_list_table').DataTable().ajax.reload();#}
|
|
||||||
{# jumpserver.checked = false;#}
|
|
||||||
{# };#}
|
|
||||||
{# console.log(JSON.stringify(post_list));#}
|
|
||||||
{# console.log(the_url);#}
|
|
||||||
{# APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#}
|
|
||||||
{# $('#asset_bulk_update_modal').modal('hide');#}
|
|
||||||
{#})#}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -19,13 +19,18 @@
|
||||||
<h3>{% trans 'Basic' %}</h3>
|
<h3>{% trans 'Basic' %}</h3>
|
||||||
{% bootstrap_field form.hostname layout="horizontal" %}
|
{% bootstrap_field form.hostname layout="horizontal" %}
|
||||||
{% bootstrap_field form.ip layout="horizontal" %}
|
{% bootstrap_field form.ip layout="horizontal" %}
|
||||||
{% bootstrap_field form.public_ip layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.port layout="horizontal" %}
|
{% bootstrap_field form.port layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.cluster layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.public_ip layout="horizontal" %}
|
||||||
{% bootstrap_field form.type layout="horizontal" %}
|
{% bootstrap_field form.type layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.env layout="horizontal" %}
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans 'Cluster and group' %}</h3>
|
<h3>{% trans 'Auth' %}</h3>
|
||||||
{% bootstrap_field form.cluster layout="horizontal" %}
|
{% bootstrap_field form.admin_user layout="horizontal" %}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<h3>{% trans 'Group' %}</h3>
|
||||||
{% bootstrap_field form.groups layout="horizontal" %}
|
{% bootstrap_field form.groups layout="horizontal" %}
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
|
|
|
@ -30,30 +30,31 @@
|
||||||
<div class="panel blank-panel">
|
<div class="panel blank-panel">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div id="tab-1" class="ibox float-e-margins tab-pane active"></div>
|
<form id="ClusterForm" method="post" class="form-horizontal">
|
||||||
<form id="ClusterForm" method="post" class="form-horizontal">
|
{% csrf_token %}
|
||||||
{% csrf_token %}
|
<h3 class="widget-head-color-box">{% trans 'Basic' %}</h3>
|
||||||
<h3 class="widget-head-color-box">基本信息</h3>
|
{% bootstrap_field form.name layout="horizontal" %}
|
||||||
{% bootstrap_field form.name layout="horizontal" %}
|
{% bootstrap_field form.address layout="horizontal" %}
|
||||||
{% bootstrap_field form.admin_user layout="horizontal" %}
|
{% bootstrap_field form.contact layout="horizontal" %}
|
||||||
{% bootstrap_field form.address layout="horizontal" %}
|
{% bootstrap_field form.phone layout="horizontal" %}
|
||||||
{% bootstrap_field form.contact layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.phone layout="horizontal" %}
|
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<h3 class="widget-head-color-box">{% trans 'Settings' %}</h3>
|
||||||
<h3 class="widget-head-color-box">IP段</h3>
|
{% bootstrap_field form.admin_user layout="horizontal" %}
|
||||||
{% bootstrap_field form.operator layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.intranet layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.extranet layout="horizontal" %}
|
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
<h3 class="widget-head-color-box">{% trans 'Other' %}</h3>
|
||||||
<div class="col-sm-4 col-sm-offset-2">
|
{% bootstrap_field form.operator layout="horizontal" %}
|
||||||
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
|
{% bootstrap_field form.intranet layout="horizontal" %}
|
||||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
{% bootstrap_field form.extranet layout="horizontal" %}
|
||||||
</div>
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from .. import api
|
from .. import api
|
||||||
from rest_framework import routers
|
|
||||||
from rest_framework_bulk.routes import BulkRouter
|
from rest_framework_bulk.routes import BulkRouter
|
||||||
|
|
||||||
app_name = 'assets'
|
app_name = 'assets'
|
||||||
|
|
|
@ -15,3 +15,5 @@ def get_assets_by_hostname_list(hostname_list):
|
||||||
def get_system_user_by_name(name):
|
def get_system_user_by_name(name):
|
||||||
system_user = get_object_or_none(SystemUser, name=name)
|
system_user = get_object_or_none(SystemUser, name=name)
|
||||||
return system_user
|
return system_user
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ 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__ = [
|
||||||
|
@ -73,11 +74,12 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
success_url = reverse_lazy('assets:asset-list')
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
self.asset = asset = form.save()
|
asset = form.save()
|
||||||
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()
|
||||||
return super(AssetCreateView, self).form_valid(form)
|
on_asset_created.send(sender=self.__class__, asset=asset)
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
|
@ -85,11 +87,11 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
'action': 'Create asset',
|
'action': 'Create asset',
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super(AssetCreateView, self).get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
update_assets_hardware_info.delay([self.asset._to_secret_json()])
|
# update_assets_hardware_info.delay([self.asset._to_secret_json()])
|
||||||
return super(AssetCreateView, self).get_success_url()
|
return super().get_success_url()
|
||||||
|
|
||||||
|
|
||||||
class AssetModalListView(AdminUserRequiredMixin, ListView):
|
class AssetModalListView(AdminUserRequiredMixin, ListView):
|
||||||
|
|
|
@ -66,20 +66,15 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
|
||||||
context_object_name = 'asset_group'
|
context_object_name = 'asset_group'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
assets_remain = Asset.objects.exclude(id__in=self.object.assets.all())
|
assets_remain = Asset.objects.exclude(groups__in=[self.object])
|
||||||
system_users = SystemUser.objects.all()
|
|
||||||
system_users_remain = SystemUser.objects.exclude(id__in=system_users)
|
|
||||||
context = {
|
context = {
|
||||||
'app': _('Assets'),
|
'app': _('Assets'),
|
||||||
'action': _('Asset group detail'),
|
'action': _('Asset group detail'),
|
||||||
'assets_remain': assets_remain,
|
'assets_remain': assets_remain,
|
||||||
'assets': [asset for asset in Asset.objects.all()
|
'assets': self.object.assets.all(),
|
||||||
if asset not in assets_remain],
|
|
||||||
'system_users': system_users,
|
|
||||||
'system_users_remain': system_users_remain,
|
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super(AssetGroupDetailView, self).get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
|
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
|
|
@ -17,8 +17,7 @@ app = Celery('jumpserver')
|
||||||
# Using a string here means the worker will not have to
|
# Using a string here means the worker will not have to
|
||||||
# pickle the object when using Windows.
|
# pickle the object when using Windows.
|
||||||
app.config_from_object('django.conf:settings')
|
app.config_from_object('django.conf:settings')
|
||||||
app.autodiscover_tasks(lambda: [app_config.split('.')[0]
|
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
|
||||||
for app_config in settings.INSTALLED_APPS])
|
|
||||||
|
|
||||||
app.conf.update(
|
app.conf.update(
|
||||||
CELERYBEAT_SCHEDULE={
|
CELERYBEAT_SCHEDULE={
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
|
import inspect
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NoDeleteQuerySet(models.query.QuerySet):
|
class NoDeleteQuerySet(models.query.QuerySet):
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
@ -58,3 +60,31 @@ class IDInFilterMixin(object):
|
||||||
if isinstance(ids, list):
|
if isinstance(ids, list):
|
||||||
queryset = queryset.filter(id__in=ids)
|
queryset = queryset.filter(id__in=ids)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class BulkSerializerMixin(object):
|
||||||
|
"""
|
||||||
|
Become rest_framework_bulk not support uuid as a primary key
|
||||||
|
so rewrite it. https://github.com/miki725/django-rest-framework-bulk/issues/66
|
||||||
|
"""
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
from rest_framework_bulk import BulkListSerializer
|
||||||
|
ret = super(BulkSerializerMixin, self).to_internal_value(data)
|
||||||
|
|
||||||
|
id_attr = getattr(self.Meta, 'update_lookup_field', 'id')
|
||||||
|
request_method = getattr(getattr(self.context.get('view'), 'request'), 'method', '')
|
||||||
|
# add update_lookup_field field back to validated data
|
||||||
|
# since super by default strips out read-only fields
|
||||||
|
# hence id will no longer be present in validated_data
|
||||||
|
if all((isinstance(self.root, BulkListSerializer),
|
||||||
|
id_attr,
|
||||||
|
request_method in ('PUT', 'PATCH'))):
|
||||||
|
id_field = self.fields[id_attr]
|
||||||
|
if data.get("id"):
|
||||||
|
id_value = id_field.to_internal_value(data.get("id"))
|
||||||
|
else:
|
||||||
|
id_value = id_field.to_internal_value(data.get("pk"))
|
||||||
|
ret[id_attr] = id_value
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
# from celery import shared_task
|
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from common import celery_app as app
|
from common import celery_app as app
|
||||||
|
from .utils import get_logger
|
||||||
|
|
||||||
|
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
|
@ -26,4 +27,7 @@ def send_mail_async(*args, **kwargs):
|
||||||
args.insert(2, settings.EMAIL_HOST_USER)
|
args.insert(2, settings.EMAIL_HOST_USER)
|
||||||
args = tuple(args)
|
args = tuple(args)
|
||||||
|
|
||||||
send_mail(*args, **kwargs)
|
try:
|
||||||
|
send_mail(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Sending mail error: {}".format(e))
|
||||||
|
|
|
@ -331,20 +331,16 @@ BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % {
|
||||||
'host': CONFIG.REDIS_HOST or '127.0.0.1',
|
'host': CONFIG.REDIS_HOST or '127.0.0.1',
|
||||||
'port': CONFIG.REDIS_PORT or 6379,
|
'port': CONFIG.REDIS_PORT or 6379,
|
||||||
}
|
}
|
||||||
|
CELERY_TASK_SERIALIZER = 'pickle'
|
||||||
|
CELERY_RESULT_SERIALIZER = 'pickle'
|
||||||
CELERY_RESULT_BACKEND = BROKER_URL
|
CELERY_RESULT_BACKEND = BROKER_URL
|
||||||
|
CELERY_ACCEPT_CONTENT = ['json', 'pickle']
|
||||||
|
CELERY_TASK_RESULT_EXPIRES = 3600
|
||||||
|
CELERYD_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s'
|
||||||
|
CELERYD_TASK_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s'
|
||||||
|
CELERY_TIMEZONE = TIME_ZONE
|
||||||
# TERMINAL_HEATBEAT_INTERVAL = CONFIG.TERMINAL_HEATBEAT_INTERVAL or 30
|
# TERMINAL_HEATBEAT_INTERVAL = CONFIG.TERMINAL_HEATBEAT_INTERVAL or 30
|
||||||
|
|
||||||
# crontab job
|
|
||||||
# CELERYBEAT_SCHEDULE = {
|
|
||||||
# Check applications is alive every 10m
|
|
||||||
# 'check_terminal_alive': {
|
|
||||||
# 'task': 'applications.tasks.check_terminal_alive',
|
|
||||||
# 'schedule': timedelta(seconds=TERMINAL_HEATBEAT_INTERVAL),
|
|
||||||
# 'args': (),
|
|
||||||
# },
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
# Cache use redis
|
# Cache use redis
|
||||||
CACHES = {
|
CACHES = {
|
||||||
|
|
|
@ -21,6 +21,7 @@ def is_uuid(s):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def record_adhoc(func):
|
def record_adhoc(func):
|
||||||
def _deco(adhoc, **options):
|
def _deco(adhoc, **options):
|
||||||
record = AdHocRunHistory(adhoc=adhoc)
|
record = AdHocRunHistory(adhoc=adhoc)
|
||||||
|
|
|
@ -156,7 +156,7 @@ function activeNav() {
|
||||||
function APIUpdateAttr(props) {
|
function APIUpdateAttr(props) {
|
||||||
// props = {url: .., body: , success: , error: , method: ,}
|
// props = {url: .., body: , success: , error: , method: ,}
|
||||||
props = props || {};
|
props = props || {};
|
||||||
var success_message = props.success_message || 'Update Successfully!';
|
var success_message = props.success_message || 'Update successfully!';
|
||||||
var fail_message = props.fail_message || 'Error occurred while updating.';
|
var fail_message = props.fail_message || 'Error occurred while updating.';
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: props.url,
|
url: props.url,
|
||||||
|
@ -169,11 +169,10 @@ function APIUpdateAttr(props) {
|
||||||
if (typeof props.success === 'function') {
|
if (typeof props.success === 'function') {
|
||||||
return props.success(data);
|
return props.success(data);
|
||||||
}
|
}
|
||||||
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
}).fail(function(jqXHR, textStatue, errorThrown) {
|
|
||||||
toastr.error(fail_message);
|
toastr.error(fail_message);
|
||||||
if (typeof props.error === 'function') {
|
if (typeof props.error === 'function') {
|
||||||
return props.error(errorThrown);
|
return props.error(jqXHR.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// return true;
|
// return true;
|
||||||
|
@ -265,7 +264,7 @@ jumpserver.initDataTable = function (options) {
|
||||||
language: {
|
language: {
|
||||||
url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json"
|
url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json"
|
||||||
},
|
},
|
||||||
order: options.order || [[ 1, 'asc' ]],
|
order: options.order || [],
|
||||||
select: options.select || 'multi',
|
select: options.select || 'multi',
|
||||||
buttons: [],
|
buttons: [],
|
||||||
columnDefs: columnDefs,
|
columnDefs: columnDefs,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="alert alert-{{ message.tags }}" style="margin: 20px auto 0px">
|
<div class="alert alert-{{ message.tags }} help-message" >
|
||||||
{{ message|safe }}
|
{{ message|safe }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -5,3 +5,8 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
class ApplicationsConfig(AppConfig):
|
class ApplicationsConfig(AppConfig):
|
||||||
name = 'terminal'
|
name = 'terminal'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
from .signals import on_app_ready
|
||||||
|
on_app_ready.send(self.__class__)
|
||||||
|
super().ready()
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
ASSETS_CACHE_KEY = "terminal__session__assets"
|
||||||
|
USERS_CACHE_KEY = "terminal__session__users"
|
||||||
|
SYSTEM_USER_CACHE_KEY = "terminal__session__system_users"
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.dispatch import Signal, receiver
|
||||||
|
from django.db.utils import ProgrammingError, OperationalError
|
||||||
|
|
||||||
|
from common.utils import get_logger
|
||||||
|
from .const import ASSETS_CACHE_KEY, USERS_CACHE_KEY, SYSTEM_USER_CACHE_KEY
|
||||||
|
|
||||||
|
RUNNING = False
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
on_app_ready = Signal()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(on_app_ready)
|
||||||
|
def on_app_ready_set_cache(sender, **kwargs):
|
||||||
|
from .utils import get_session_asset_list, get_session_user_list, \
|
||||||
|
get_session_system_user_list
|
||||||
|
global RUNNING
|
||||||
|
|
||||||
|
def set_cache():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
assets = get_session_asset_list()
|
||||||
|
users = get_session_user_list()
|
||||||
|
system_users = get_session_system_user_list()
|
||||||
|
|
||||||
|
cache.set(ASSETS_CACHE_KEY, assets)
|
||||||
|
cache.set(USERS_CACHE_KEY, users)
|
||||||
|
cache.set(SYSTEM_USER_CACHE_KEY, system_users)
|
||||||
|
except (ProgrammingError, OperationalError):
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
time.sleep(10)
|
||||||
|
if RUNNING:
|
||||||
|
return
|
||||||
|
threads = []
|
||||||
|
thread = threading.Thread(target=set_cache)
|
||||||
|
threads.append(thread)
|
||||||
|
|
||||||
|
logger.debug("Receive app ready signal: set cache task start")
|
||||||
|
for t in threads:
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
RUNNING = True
|
|
@ -1,19 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.utils import ProgrammingError
|
from django.db.utils import ProgrammingError, OperationalError
|
||||||
|
|
||||||
from .models import Session
|
from .models import Session
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ASSETS_CACHE_KEY = "terminal__session__assets"
|
|
||||||
USERS_CACHE_KEY = "terminal__session__users"
|
|
||||||
SYSTEM_USER_CACHE_KEY = "terminal__session__system_users"
|
|
||||||
CACHE_REFRESH_INTERVAL = 10
|
CACHE_REFRESH_INTERVAL = 10
|
||||||
RUNNING = False
|
RUNNING = False
|
||||||
|
|
||||||
|
@ -24,46 +20,7 @@ def clean_terminal_history():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_session_asset_list():
|
|
||||||
return set(list(Session.objects.values_list('asset', flat=True)))
|
|
||||||
|
|
||||||
|
|
||||||
def get_session_user_list():
|
|
||||||
return set(list(Session.objects.values_list('user', flat=True)))
|
|
||||||
|
|
||||||
|
|
||||||
def get_session_system_user_list():
|
|
||||||
return set(list(Session.objects.values_list('system_user', flat=True)))
|
|
||||||
|
|
||||||
|
|
||||||
def set_cache():
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
assets = get_session_asset_list()
|
|
||||||
users = get_session_user_list()
|
|
||||||
system_users = get_session_system_user_list()
|
|
||||||
|
|
||||||
cache.set(ASSETS_CACHE_KEY, assets)
|
|
||||||
cache.set(USERS_CACHE_KEY, users)
|
|
||||||
cache.set(SYSTEM_USER_CACHE_KEY, system_users)
|
|
||||||
except ProgrammingError:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
global RUNNING
|
|
||||||
if RUNNING:
|
|
||||||
return
|
|
||||||
threads = []
|
|
||||||
thread = threading.Thread(target=set_cache)
|
|
||||||
threads.append(thread)
|
|
||||||
|
|
||||||
for t in threads:
|
|
||||||
t.daemon = True
|
|
||||||
t.start()
|
|
||||||
RUNNING = True
|
|
||||||
|
|
||||||
# Todo: 不能migrations了
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,8 +1,23 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
from .models import Session
|
||||||
|
|
||||||
|
from .const import USERS_CACHE_KEY, ASSETS_CACHE_KEY, SYSTEM_USER_CACHE_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def get_session_asset_list():
|
||||||
|
return set(list(Session.objects.values_list('asset', flat=True)))
|
||||||
|
|
||||||
|
|
||||||
|
def get_session_user_list():
|
||||||
|
return set(list(Session.objects.values_list('user', flat=True)))
|
||||||
|
|
||||||
|
|
||||||
|
def get_session_system_user_list():
|
||||||
|
return set(list(Session.objects.values_list('system_user', flat=True)))
|
||||||
|
|
||||||
|
|
||||||
from .tasks import USERS_CACHE_KEY, ASSETS_CACHE_KEY, SYSTEM_USER_CACHE_KEY
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_list_from_cache():
|
def get_user_list_from_cache():
|
||||||
|
|
|
@ -4,11 +4,11 @@ from rest_framework import generics
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework import viewsets
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
|
||||||
|
|
||||||
from . import serializers
|
from .serializers import UserSerializer, UserGroupSerializer, \
|
||||||
|
UserGroupUpdateMemeberSerializer, UserPKUpdateSerializer, \
|
||||||
|
UserUpdateGroupSerializer
|
||||||
from .tasks import write_login_log_async
|
from .tasks import write_login_log_async
|
||||||
from .models import User, UserGroup
|
from .models import User, UserGroup
|
||||||
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
|
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
|
||||||
|
@ -20,49 +20,23 @@ from common.utils import get_logger
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# class UserListView(generics.ListAPIView):
|
class UserViewSet(BulkModelViewSet):
|
||||||
# queryset = User.objects.all()
|
|
||||||
# serializer_class = serializers.UserSerializer
|
|
||||||
# filter_fields = ('username', 'email', 'name', 'id')
|
|
||||||
|
|
||||||
|
|
||||||
class UserViewSet(viewsets.ModelViewSet):
|
|
||||||
# class UserViewSet(IDInFilterMixin, BulkModelViewSet):
|
|
||||||
"""
|
|
||||||
retrieve:
|
|
||||||
Return a user instance .
|
|
||||||
|
|
||||||
list:
|
|
||||||
Return all users except app user, ordered by most recently joined.
|
|
||||||
|
|
||||||
create:
|
|
||||||
Create a new user.
|
|
||||||
|
|
||||||
delete:
|
|
||||||
Remove an existing user.
|
|
||||||
|
|
||||||
partial_update:
|
|
||||||
Update one or more fields on an existing user.
|
|
||||||
|
|
||||||
update:
|
|
||||||
Update a user.
|
|
||||||
"""
|
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
|
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = UserSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser,)
|
||||||
filter_fields = ('username', 'email', 'name', 'id')
|
filter_fields = ('username', 'email', 'name', 'id')
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = serializers.UserUpdateGroupSerializer
|
serializer_class = UserUpdateGroupSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
|
||||||
class UserResetPasswordApi(generics.UpdateAPIView):
|
class UserResetPasswordApi(generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = UserSerializer
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
# Note: we are not updating the user object here.
|
# Note: we are not updating the user object here.
|
||||||
|
@ -77,7 +51,7 @@ class UserResetPasswordApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
class UserResetPKApi(generics.UpdateAPIView):
|
class UserResetPKApi(generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = UserSerializer
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
from .utils import send_reset_ssh_key_mail
|
from .utils import send_reset_ssh_key_mail
|
||||||
|
@ -89,7 +63,7 @@ class UserResetPKApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
class UserUpdatePKApi(generics.UpdateAPIView):
|
class UserUpdatePKApi(generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = serializers.UserPKUpdateSerializer
|
serializer_class = UserPKUpdateSerializer
|
||||||
permission_classes = (IsCurrentUserOrReadOnly,)
|
permission_classes = (IsCurrentUserOrReadOnly,)
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
|
@ -100,12 +74,12 @@ class UserUpdatePKApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
|
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||||
queryset = UserGroup.objects.all()
|
queryset = UserGroup.objects.all()
|
||||||
serializer_class = serializers.UserGroupSerializer
|
serializer_class = UserGroupSerializer
|
||||||
|
|
||||||
|
|
||||||
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
||||||
queryset = UserGroup.objects.all()
|
queryset = UserGroup.objects.all()
|
||||||
serializer_class = serializers.UserGroupUpdateMemeberSerializer
|
serializer_class = UserGroupUpdateMemeberSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,7 @@ class User(AbstractUser):
|
||||||
return 'https://www.gravatar.com/avatar/c6812ab450230979465d7bf288eadce2a?s=120&d=identicon'
|
return 'https://www.gravatar.com/avatar/c6812ab450230979465d7bf288eadce2a?s=120&d=identicon'
|
||||||
|
|
||||||
def generate_reset_token(self):
|
def generate_reset_token(self):
|
||||||
return signer.sign_t({'reset': self.id, 'email': self.email}, expires_in=3600)
|
return signer.sign_t({'reset': str(self.id), 'email': self.email}, expires_in=3600)
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return OrderedDict({
|
return OrderedDict({
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
|
from rest_framework_bulk import BulkListSerializer
|
||||||
|
|
||||||
from common.utils import signer, validate_ssh_public_key
|
from common.utils import signer, validate_ssh_public_key
|
||||||
|
from common.mixins import BulkSerializerMixin
|
||||||
from .models import User, UserGroup
|
from .models import User, UserGroup
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.dispatch import Signal, receiver
|
||||||
|
|
||||||
|
from common.utils import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
on_user_created = Signal(providing_args=['user'])
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(on_user_created)
|
||||||
|
def send_user_add_mail_to_user(sender, user=None, **kwargs):
|
||||||
|
from .utils import send_user_created_mail
|
||||||
|
logger.debug("Receive asset create signal, update asset hardware info")
|
||||||
|
send_user_created_mail(user)
|
||||||
|
|
|
@ -23,17 +23,18 @@ urlpatterns = [
|
||||||
|
|
||||||
# User view
|
# User view
|
||||||
url(r'^user$', views.UserListView.as_view(), name='user-list'),
|
url(r'^user$', views.UserListView.as_view(), name='user-list'),
|
||||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)$', views.UserDetailView.as_view(), name='user-detail'),
|
|
||||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission$', views.UserAssetPermissionView.as_view(), name='user-asset-permission'),
|
|
||||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), name='user-asset-permission-create'),
|
|
||||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
|
|
||||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
|
|
||||||
url(r'^user/export/', views.UserExportView.as_view(), name='user-export'),
|
url(r'^user/export/', views.UserExportView.as_view(), name='user-export'),
|
||||||
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
|
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
|
||||||
url(r'^user/import/$', views.UserBulkImportView.as_view(), name='user-import'),
|
url(r'^user/import/$', views.UserBulkImportView.as_view(), name='user-import'),
|
||||||
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
|
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
|
||||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.UserUpdateView.as_view(), name='user-update'),
|
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.UserUpdateView.as_view(), name='user-update'),
|
||||||
url(r'^user/update$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'),
|
url(r'^user/update$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'),
|
||||||
|
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)$', views.UserDetailView.as_view(), name='user-detail'),
|
||||||
|
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission$', views.UserAssetPermissionView.as_view(), name='user-asset-permission'),
|
||||||
|
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), name='user-asset-permission-create'),
|
||||||
|
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
|
||||||
|
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
|
||||||
|
|
||||||
|
|
||||||
# User group view
|
# User group view
|
||||||
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
|
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
|
||||||
|
|
|
@ -31,7 +31,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def user_add_success_next(user):
|
def send_user_created_mail(user):
|
||||||
subject = _('Create account successfully')
|
subject = _('Create account successfully')
|
||||||
recipient_list = [user.email]
|
recipient_list = [user.email]
|
||||||
message = _("""
|
message = _("""
|
||||||
|
@ -58,6 +58,8 @@ def user_add_success_next(user):
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'login_url': reverse('users:login', external=True),
|
'login_url': reverse('users:login', external=True),
|
||||||
}
|
}
|
||||||
|
if settings.DEBUG:
|
||||||
|
print(message)
|
||||||
|
|
||||||
send_mail_async.delay(subject, message, recipient_list, html_message=message)
|
send_mail_async.delay(subject, message, recipient_list, html_message=message)
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ from django.contrib.auth import logout as auth_logout
|
||||||
|
|
||||||
from .. import forms
|
from .. import forms
|
||||||
from ..models import User, UserGroup
|
from ..models import User, UserGroup
|
||||||
from ..utils import AdminUserRequiredMixin, user_add_success_next
|
from ..utils import AdminUserRequiredMixin, send_user_created_mail
|
||||||
|
from ..signals import on_user_created
|
||||||
from common.mixins import JSONResponseMixin
|
from common.mixins import JSONResponseMixin
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger, get_object_or_none
|
||||||
from perms.models import AssetPermission
|
from perms.models import AssetPermission
|
||||||
|
@ -74,7 +75,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
||||||
user = form.save(commit=False)
|
user = form.save(commit=False)
|
||||||
user.created_by = self.request.user.username or 'System'
|
user.created_by = self.request.user.username or 'System'
|
||||||
user.save()
|
user.save()
|
||||||
user_add_success_next(user)
|
on_user_created.send(self.__class__, user=user)
|
||||||
return super(UserCreateView, self).form_valid(form)
|
return super(UserCreateView, self).form_valid(form)
|
||||||
|
|
||||||
def get_success_message(self, cleaned_data):
|
def get_success_message(self, cleaned_data):
|
||||||
|
@ -281,7 +282,7 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
||||||
user = User.objects.create(**user_dict)
|
user = User.objects.create(**user_dict)
|
||||||
user.groups.set(groups)
|
user.groups.set(groups)
|
||||||
created.append(user_dict['username'])
|
created.append(user_dict['username'])
|
||||||
user_add_success_next(user)
|
on_user_created.send(self.__class__, user=user)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
failed.append('%s: %s' % (user_dict['username'], str(e)))
|
failed.append('%s: %s' % (user_dict['username'], str(e)))
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue