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 = get_object_or_404(Asset, pk=asset_id)
|
||||
summary = update_assets_hardware_info([asset])
|
||||
if len(summary['failed']) == 0:
|
||||
return super(AssetRefreshHardwareView, self).retrieve(request, *args, **kwargs)
|
||||
print(summary)
|
||||
if summary.get('dark'):
|
||||
return Response(summary['dark'].values(), status=501)
|
||||
else:
|
||||
return Response('', status=502)
|
||||
return Response({"msg": "ok"})
|
||||
|
||||
|
||||
class AssetAdminUserTestView(AssetRefreshHardwareView):
|
||||
|
|
|
@ -5,3 +5,8 @@ from django.apps import AppConfig
|
|||
|
||||
class AssetsConfig(AppConfig):
|
||||
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):
|
||||
# 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:
|
||||
model = Asset
|
||||
fields = [
|
||||
'hostname', 'ip', 'public_ip', 'port', 'type', 'comment',
|
||||
'cluster', 'groups', 'status', 'env', 'is_active', 'username',
|
||||
'cluster', 'groups', 'status', 'env', 'is_active',
|
||||
'admin_user'
|
||||
|
||||
]
|
||||
widgets = {
|
||||
'groups': forms.SelectMultiple(
|
||||
attrs={'class': 'select2',
|
||||
'data-placeholder': _('Select asset groups')}),
|
||||
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
|
||||
'cluster': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select cluster')}),
|
||||
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select admin user')}),
|
||||
}
|
||||
help_texts = {
|
||||
'hostname': '* 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 = [
|
||||
'hostname', 'ip', 'port', 'groups', "cluster", 'is_active',
|
||||
'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no',
|
||||
'cabinet_pos', 'number', 'comment'
|
||||
'cabinet_pos', 'number', 'comment', 'admin_user',
|
||||
]
|
||||
widgets = {
|
||||
'groups': forms.SelectMultiple(
|
||||
attrs={'class': 'select2',
|
||||
'data-placeholder': _('Select asset groups')}),
|
||||
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
|
||||
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _("Default using cluster admin user")})
|
||||
}
|
||||
help_texts = {
|
||||
'hostname': '* 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 os
|
||||
import logging
|
||||
from hashlib import md5
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.cache import cache
|
||||
|
||||
from common.utils import signer, ssh_key_string_to_obj
|
||||
from .utils import private_key_validator
|
||||
from ..const import ADMIN_USER_CONN_CACHE_KEY_PREFIX
|
||||
from .cluster import Cluster
|
||||
from .group import AssetGroup
|
||||
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'))
|
||||
|
||||
# Auth
|
||||
username = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('Username'))
|
||||
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
||||
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
|
||||
admin_user = models.ForeignKey('assets.AdminUser', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
|
||||
|
||||
# Some information
|
||||
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
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
if self._password:
|
||||
return signer.unsign(self._password)
|
||||
def hardware_info(self):
|
||||
if self.cpu_count:
|
||||
return '{} Core {} {}'.format(
|
||||
self.cpu_count * self.cpu_cores,
|
||||
self.memory, self.disk_total
|
||||
)
|
||||
else:
|
||||
return ''
|
||||
|
||||
@password.setter
|
||||
def password(self, password_raw):
|
||||
self._password = signer.sign(password_raw)
|
||||
|
||||
@property
|
||||
def private_key(self):
|
||||
if self._private_key:
|
||||
key_str = signer.unsign(self._private_key)
|
||||
return ssh_key_string_to_obj(key_str)
|
||||
def is_connective(self):
|
||||
val = cache.get(ADMIN_USER_CONN_CACHE_KEY_PREFIX + self.hostname)
|
||||
if val == 1:
|
||||
return True
|
||||
else:
|
||||
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
|
||||
return False
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
|
@ -148,25 +125,28 @@ class Asset(models.Model):
|
|||
'groups': [group.name for group in self.groups.all()],
|
||||
}
|
||||
|
||||
def is_connective(self):
|
||||
return cache.get(self.hostname)
|
||||
|
||||
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
|
||||
"""
|
||||
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({
|
||||
'username': self.cluster.admin_user.username,
|
||||
'password': self.cluster.admin_user.password,
|
||||
'private_key': self.cluster.admin_user.private_key_file,
|
||||
'username': admin_user.username,
|
||||
'password': admin_user.password,
|
||||
'private_key': admin_user.private_key_file,
|
||||
'become': {
|
||||
'method': self.cluster.admin_user.become_method,
|
||||
'user': self.cluster.admin_user.become_user,
|
||||
'pass': self.cluster.admin_user.become_pass,
|
||||
'method': admin_user.become_method,
|
||||
'user': admin_user.become_user,
|
||||
'pass': admin_user.become_pass,
|
||||
}
|
||||
})
|
||||
return data
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
|
@ -18,7 +16,7 @@ logger = logging.getLogger(__name__)
|
|||
class Cluster(models.Model):
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
name = models.CharField(max_length=32, verbose_name=_('Name'))
|
||||
admin_user = models.ForeignKey('assets.AdminUser', 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'))
|
||||
contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact'))
|
||||
phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))
|
||||
|
|
|
@ -96,12 +96,16 @@ class AdminUser(models.Model):
|
|||
def become_pass(self, 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
|
||||
def assets_amount(self):
|
||||
amount = 0
|
||||
for cluster in self.cluster_set.all():
|
||||
amount += cluster.assets.all().count()
|
||||
return amount
|
||||
return len(self.get_related_assets())
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
@ -209,9 +213,14 @@ class SystemUser(models.Model):
|
|||
'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
|
||||
def assets_amount(self):
|
||||
return self.assets.count()
|
||||
return len(self.get_clusters_assets())
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.core.cache import cache
|
||||
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 .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):
|
||||
# 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):
|
||||
model = Asset
|
||||
list_serializer_class = BulkListSerializer
|
||||
fields = '__all__'
|
||||
|
||||
@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'
|
||||
validators = [] # If not set to [], partial bulk update will be error
|
||||
|
||||
def get_field_names(self, declared_fields, info):
|
||||
fields = super(AssetSerializer, self).get_field_names(declared_fields, info)
|
||||
fields.extend(['get_type_display', 'get_env_display'])
|
||||
fields = super().get_field_names(declared_fields, info)
|
||||
fields.extend([
|
||||
'get_type_display', 'get_env_display',
|
||||
'hardware_info', 'is_connective',
|
||||
])
|
||||
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 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 .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
|
||||
TIMEOUT = 60
|
||||
logger = get_logger(__file__)
|
||||
ADMIN_USER_CONN_CACHE_KEY_PREFIX = "ADMIN_USER_CONN_"
|
||||
SYSTEM_USER_CONN_CACHE_KEY_PREFIX = 'SYSTEM_USER_CONN_'
|
||||
|
||||
|
||||
@shared_task
|
||||
|
@ -75,6 +73,12 @@ def update_assets_hardware_info(assets):
|
|||
if k.startswith('___'):
|
||||
setattr(asset, k.strip('_'), v)
|
||||
asset.save()
|
||||
|
||||
for hostname, task in summary['dark'].items():
|
||||
logger.warn("Update {} hardware info error: {}".format(
|
||||
hostname, task[name],
|
||||
))
|
||||
|
||||
return summary
|
||||
|
||||
|
||||
|
@ -96,8 +100,7 @@ def test_admin_user_connectability(admin_user):
|
|||
:return:
|
||||
"""
|
||||
from ops.utils import run_adhoc
|
||||
assets = admin_user.assets.all()
|
||||
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
|
||||
assets = admin_user.get_related_assets()
|
||||
hosts = [asset.hostname for asset in assets]
|
||||
tasks = [
|
||||
{
|
||||
|
@ -126,6 +129,7 @@ def test_admin_user_connectability_period():
|
|||
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 0, 60*60*60)
|
||||
|
||||
|
||||
@shared_task
|
||||
def test_admin_user_connectability_manual(asset):
|
||||
from ops.utils import run_adhoc
|
||||
# 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)
|
||||
if result.results_summary['dark']:
|
||||
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + asset.hostname, 0, 60*60*60)
|
||||
return False
|
||||
else:
|
||||
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + asset.hostname, 1, 60*60* 60)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -153,7 +159,7 @@ def test_system_user_connectability(system_user):
|
|||
:return:
|
||||
"""
|
||||
from ops.utils import run_adhoc
|
||||
assets = system_user.assets.all()
|
||||
assets = system_user.get_clusters_assets()
|
||||
hosts = [asset.hostname for asset in assets]
|
||||
tasks = [
|
||||
{
|
||||
|
@ -171,7 +177,7 @@ def test_system_user_connectability(system_user):
|
|||
def test_system_user_connectability_period():
|
||||
for system_user in SystemUser.objects.all():
|
||||
summary = test_system_user_connectability(system_user)
|
||||
cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name , summary, 60*60*60)
|
||||
cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name, summary, 60*60*60)
|
||||
|
||||
|
||||
def get_push_system_user_tasks(system_user):
|
||||
|
@ -207,19 +213,12 @@ def get_push_system_user_tasks(system_user):
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
return tasks
|
||||
|
||||
|
||||
PUSH_SYSTEM_USER_PERIOD_TASK_NAME = 'PUSH SYSTEM USER {} PERIOD...'
|
||||
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
|
||||
PUSH_SYSTEM_USER_PERIOD_TASK_NAME = 'PUSH SYSTEM USER [{}] PERIOD...'
|
||||
PUSH_SYSTEM_USER_TASK_NAME = 'PUSH SYSTEM USER [{}] ASSETS'
|
||||
|
||||
|
||||
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():
|
||||
logger.debug("Push system user period")
|
||||
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)
|
||||
push_system_user(s, assets, name)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
{% block help_message %}
|
||||
<div class="alert alert-info help-message">
|
||||
管理用户是 服务器上已存在的特权用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等
|
||||
管理用户是 服务器上已存在的特权用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。可以设置主机级别管理用户,也设置集群级别管理用户,这样资产可以不用再单独设置
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -14,20 +14,18 @@
|
|||
<h3>{% trans 'Basic' %}</h3>
|
||||
{% bootstrap_field form.hostname layout="horizontal" %}
|
||||
{% bootstrap_field form.ip layout="horizontal" %}
|
||||
{% bootstrap_field form.public_ip 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.env layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Auth' %}</h3>
|
||||
{% bootstrap_field form.username layout="horizontal" %}
|
||||
{% bootstrap_field form.password layout="horizontal" %}
|
||||
{% bootstrap_field form.private_key_file layout="horizontal" %}
|
||||
{% bootstrap_field form.admin_user layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Cluster and group' %}</h3>
|
||||
{% bootstrap_field form.cluster layout="horizontal" %}
|
||||
<h3>{% trans 'Group' %}</h3>
|
||||
{% bootstrap_field form.groups layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
@ -50,11 +48,11 @@
|
|||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
$("#id_tags").select2({
|
||||
tags: true,
|
||||
maximumSelectionLength: 8 //最多能够选择的个数
|
||||
//closeOnSelect: false
|
||||
});
|
||||
{# $("#id_tags").select2({#}
|
||||
{# tags: true,#}
|
||||
{# maximumSelectionLength: 8 //最多能够选择的个数#}
|
||||
{# //closeOnSelect: false#}
|
||||
{# });#}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -19,9 +19,6 @@
|
|||
<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>
|
||||
</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 %}
|
||||
<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>
|
||||
|
@ -198,14 +195,6 @@
|
|||
</span>
|
||||
</td>
|
||||
</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>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -239,7 +228,7 @@
|
|||
<tr>
|
||||
<td ><b class="bdg_group" data-gid={{ asset_group.id }}>{{ asset_group.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>
|
||||
<button class="btn btn-danger pull-right btn-xs btn-leave-group" type="button"><i class="fa fa-minus"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -274,7 +263,7 @@ function updateAssetGroups(groups) {
|
|||
$('#add-asset2group tbody').append(
|
||||
'<tr>' +
|
||||
'<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>'
|
||||
)
|
||||
});
|
||||
|
@ -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() {
|
||||
var the_url = "{% url 'api-assets:asset-refresh' pk=asset.id %}";
|
||||
var success = function (data) {
|
||||
location.reload();
|
||||
};
|
||||
var error = function (data) {
|
||||
alert(data)
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
success: success,
|
||||
error: error,
|
||||
method: 'GET'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -337,13 +302,6 @@ $(document).ready(function () {
|
|||
var data = evt.params.data;
|
||||
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 () {
|
||||
var the_url = '{% url "api-assets:asset-detail" pk=asset.id %}';
|
||||
var checked = $(this).prop('checked');
|
||||
|
@ -370,11 +328,11 @@ $(document).ready(function () {
|
|||
return $(this).data('gid');
|
||||
}).get();
|
||||
$.map(jumpserver.groups_selected, function(value, index) {
|
||||
groups.push(parseInt(index));
|
||||
groups.push(index);
|
||||
$('#opt_' + index).remove();
|
||||
});
|
||||
updateAssetGroups(groups)
|
||||
}).on('click', '.btn_leave_group', function() {
|
||||
}).on('click', '.btn-leave-group', function() {
|
||||
var $this = $(this);
|
||||
var $tr = $this.closest('tr');
|
||||
var $badge = $tr.find('.bdg_group');
|
||||
|
@ -388,34 +346,6 @@ $(document).ready(function () {
|
|||
return $(this).data('gid');
|
||||
}).get();
|
||||
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 () {
|
||||
var $this = $(this);
|
||||
var name = "{{ asset.hostname }}";
|
||||
|
@ -424,7 +354,7 @@ $(document).ready(function () {
|
|||
var redirect_url = "{% url 'assets:asset-list' %}";
|
||||
objectDelete($this, name, the_url, redirect_url);
|
||||
}).on('click', '#btn_refresh_asset', function () {
|
||||
alert('请等待几秒, 等待完成');
|
||||
alert('关闭alert, 等待完成, 自动刷新页面');
|
||||
refreshAssetHardware()
|
||||
}).on('click', '#btn_test_admin_user', function () {
|
||||
$.ajax({
|
||||
|
@ -436,6 +366,5 @@ $(document).ready(function () {
|
|||
})
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -67,16 +67,16 @@
|
|||
<form>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<select data-placeholder="{% trans 'Select assets' %}" class="select2 system-user-select" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for system_user in system_users %}
|
||||
<option value="{{ system_user.id }}"> {{ system_user.name }} </option>
|
||||
<select data-placeholder="{% trans 'Select assets' %}" class="select2 asset-select" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for asset in assets_remain %}
|
||||
<option value="{{ asset.id }}"> {{ asset.hostname }} </option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="no-borders-tr">
|
||||
<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>
|
||||
</tr>
|
||||
</form>
|
||||
|
@ -93,28 +93,30 @@
|
|||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
jumpserver.assets_selected = {};
|
||||
function updateGroupAssets(assets) {
|
||||
jumpserver.assets_selected = {};
|
||||
function updateGroupAssets(assets) {
|
||||
var the_url = "{}";
|
||||
var body = {
|
||||
assets: Object.assign([], assets)
|
||||
assets: Object.assign([], assets)
|
||||
};
|
||||
var $data_table = $("#asset_list_table").DataTable();
|
||||
var success = function(data) {
|
||||
$('.select2-selection__rendered').empty();
|
||||
$.map(jumpserver.assets_selected, function(asset_ip, index) {
|
||||
$('#opt_' + index).remove();
|
||||
$data_table.ajax.reload();
|
||||
});
|
||||
jumpserver.groups_selected = {};
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
method: 'PUT',
|
||||
success: success
|
||||
$('.select2-selection__rendered').empty();
|
||||
$.map(jumpserver.assets_selected, function(asset_ip, index) {
|
||||
$('#opt_' + index).remove();
|
||||
$data_table.ajax.reload();
|
||||
});
|
||||
}
|
||||
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 doDelete() {
|
||||
|
@ -203,20 +205,21 @@ $(document).ready(function () {
|
|||
$('.select2').select2();
|
||||
|
||||
$('.select2.asset-select').select2()
|
||||
.on('select2:select', function(evt) {
|
||||
var data = evt.params.data;
|
||||
jumpserver.assets_selected[data.id] = data.text;
|
||||
console.log(jumpserver.assets_selected)
|
||||
})
|
||||
.on('select2:unselect', function(evt) {
|
||||
var data = evt.params.data;
|
||||
delete jumpserver.assets_selected[data.id]
|
||||
});
|
||||
.on('select2:select', function(evt) {
|
||||
var data = evt.params.data;
|
||||
jumpserver.assets_selected[data.id] = data.text;
|
||||
console.log(jumpserver.assets_selected)
|
||||
})
|
||||
.on('select2:unselect', function(evt) {
|
||||
var data = evt.params.data;
|
||||
delete jumpserver.assets_selected[data.id]
|
||||
});
|
||||
|
||||
initTable();
|
||||
|
||||
})
|
||||
|
||||
.on('click', ".btn-asset-group-add-asset", function () {
|
||||
.on('click', ".btn-add-asset", function () {
|
||||
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
||||
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 the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}";
|
||||
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 'Env' %}</th>
|
||||
<th class="text-center">{% trans 'Hardware' %}</th>
|
||||
<th class="text-center">{% trans 'Valid' %}</th>
|
||||
<th class="text-center">{% trans 'Alive' %}</th>
|
||||
<th class="text-center">{% trans 'Active' %}</th>
|
||||
<th class="text-center">{% trans 'Connective' %}</th>
|
||||
<th class="text-center">{% trans 'Action' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<option value="delete">{% trans 'Delete selected' %}</option>
|
||||
<option value="update">{% trans 'Update selected' %}</option>
|
||||
<option value="deactive">{% trans 'Deactive selected' %}</option>
|
||||
<option value="active">{% trans 'Active' %}</option>
|
||||
<option value="active">{% trans 'Active selected' %}</option>
|
||||
</select>
|
||||
<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">
|
||||
|
@ -114,8 +114,8 @@ $(document).ready(function(){
|
|||
],
|
||||
ajax_url: '{% url "api-assets:asset-list" %}',
|
||||
columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware"},
|
||||
{data: "is_active" }, {data: "is_online"}, {data: "id" }],
|
||||
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"},
|
||||
{data: "is_active" }, {data: "is_connective"}, {data: "id" }],
|
||||
op_html: $('#actions').html()
|
||||
};
|
||||
var table = jumpserver.initDataTable(options);
|
||||
|
@ -180,24 +180,28 @@ $(document).ready(function(){
|
|||
$data_table.rows({selected: true}).every(function(){
|
||||
id_list.push(this.data().id);
|
||||
});
|
||||
if (id_list.length == 0) {
|
||||
if (id_list.length === 0) {
|
||||
return false;
|
||||
}
|
||||
var the_url = "{% url 'api-assets:asset-list' %}";
|
||||
|
||||
function doDeactive() {
|
||||
var body = $.each(id_list, function(index, asset_object) {
|
||||
asset_object['is_active'] = false;
|
||||
var data = [];
|
||||
$.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();
|
||||
jumpserver.checked = false;
|
||||
}
|
||||
function doActive() {
|
||||
var body = $.each(id_list, function(index, asset_object) {
|
||||
asset_object['is_active'] = true;
|
||||
var data = [];
|
||||
$.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();
|
||||
jumpserver.checked = false;
|
||||
}
|
||||
|
@ -220,8 +224,13 @@ $(document).ready(function(){
|
|||
var msg = "{% trans 'Asset Deleting failed.' %}";
|
||||
swal("{% trans 'Asset Delete' %}", msg, "error");
|
||||
};
|
||||
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
|
||||
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
|
||||
var url_delete = the_url + '?id__in=' + JSON.stringify(id_list);
|
||||
APIUpdateAttr({
|
||||
url: url_delete,
|
||||
method: 'DELETE',
|
||||
success: success,
|
||||
error: fail
|
||||
});
|
||||
$data_table.ajax.reload();
|
||||
jumpserver.checked = false;
|
||||
});
|
||||
|
@ -248,65 +257,6 @@ $(document).ready(function(){
|
|||
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>
|
||||
{% endblock %}
|
||||
|
|
|
@ -19,13 +19,18 @@
|
|||
<h3>{% trans 'Basic' %}</h3>
|
||||
{% bootstrap_field form.hostname layout="horizontal" %}
|
||||
{% bootstrap_field form.ip layout="horizontal" %}
|
||||
{% bootstrap_field form.public_ip 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.env layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Cluster and group' %}</h3>
|
||||
{% bootstrap_field form.cluster layout="horizontal" %}
|
||||
<h3>{% trans 'Auth' %}</h3>
|
||||
{% bootstrap_field form.admin_user layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Group' %}</h3>
|
||||
{% bootstrap_field form.groups layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
|
|
@ -30,30 +30,31 @@
|
|||
<div class="panel blank-panel">
|
||||
<div class="panel-body">
|
||||
<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">
|
||||
{% csrf_token %}
|
||||
<h3 class="widget-head-color-box">基本信息</h3>
|
||||
{% bootstrap_field form.name layout="horizontal" %}
|
||||
{% bootstrap_field form.admin_user layout="horizontal" %}
|
||||
{% bootstrap_field form.address layout="horizontal" %}
|
||||
{% bootstrap_field form.contact layout="horizontal" %}
|
||||
{% bootstrap_field form.phone layout="horizontal" %}
|
||||
<form id="ClusterForm" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<h3 class="widget-head-color-box">{% trans 'Basic' %}</h3>
|
||||
{% bootstrap_field form.name layout="horizontal" %}
|
||||
{% bootstrap_field form.address 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">IP段</h3>
|
||||
{% bootstrap_field form.operator layout="horizontal" %}
|
||||
{% bootstrap_field form.intranet layout="horizontal" %}
|
||||
{% bootstrap_field form.extranet layout="horizontal" %}
|
||||
<h3 class="widget-head-color-box">{% trans 'Settings' %}</h3>
|
||||
{% bootstrap_field form.admin_user 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-default" type="reset"> {% trans 'Reset' %}</button>
|
||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3 class="widget-head-color-box">{% trans 'Other' %}</h3>
|
||||
{% 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="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>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# coding:utf-8
|
||||
from django.conf.urls import url
|
||||
from .. import api
|
||||
from rest_framework import routers
|
||||
from rest_framework_bulk.routes import BulkRouter
|
||||
|
||||
app_name = 'assets'
|
||||
|
|
|
@ -15,3 +15,5 @@ def get_assets_by_hostname_list(hostname_list):
|
|||
def get_system_user_by_name(name):
|
||||
system_user = get_object_or_none(SystemUser, name=name)
|
||||
return system_user
|
||||
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ from .. import forms
|
|||
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
|
||||
from ..hands import AdminUserRequiredMixin
|
||||
from ..tasks import update_assets_hardware_info
|
||||
from ..signals import on_asset_created
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -73,11 +74,12 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
|
|||
success_url = reverse_lazy('assets:asset-list')
|
||||
|
||||
def form_valid(self, form):
|
||||
self.asset = asset = form.save()
|
||||
asset = form.save()
|
||||
asset.created_by = self.request.user.username or 'Admin'
|
||||
asset.date_created = timezone.now()
|
||||
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):
|
||||
context = {
|
||||
|
@ -85,11 +87,11 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
|
|||
'action': 'Create asset',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(AssetCreateView, self).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(AssetCreateView, self).get_success_url()
|
||||
# update_assets_hardware_info.delay([self.asset._to_secret_json()])
|
||||
return super().get_success_url()
|
||||
|
||||
|
||||
class AssetModalListView(AdminUserRequiredMixin, ListView):
|
||||
|
|
|
@ -66,20 +66,15 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
|
|||
context_object_name = 'asset_group'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
assets_remain = Asset.objects.exclude(id__in=self.object.assets.all())
|
||||
system_users = SystemUser.objects.all()
|
||||
system_users_remain = SystemUser.objects.exclude(id__in=system_users)
|
||||
assets_remain = Asset.objects.exclude(groups__in=[self.object])
|
||||
context = {
|
||||
'app': _('Assets'),
|
||||
'action': _('Asset group detail'),
|
||||
'assets_remain': assets_remain,
|
||||
'assets': [asset for asset in Asset.objects.all()
|
||||
if asset not in assets_remain],
|
||||
'system_users': system_users,
|
||||
'system_users_remain': system_users_remain,
|
||||
'assets': self.object.assets.all(),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(AssetGroupDetailView, self).get_context_data(**kwargs)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||
|
|
|
@ -17,8 +17,7 @@ app = Celery('jumpserver')
|
|||
# Using a string here means the worker will not have to
|
||||
# pickle the object when using Windows.
|
||||
app.config_from_object('django.conf:settings')
|
||||
app.autodiscover_tasks(lambda: [app_config.split('.')[0]
|
||||
for app_config in settings.INSTALLED_APPS])
|
||||
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
|
||||
|
||||
app.conf.update(
|
||||
CELERYBEAT_SCHEDULE={
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# coding: utf-8
|
||||
|
||||
import inspect
|
||||
from django.db import models
|
||||
from django.http import JsonResponse
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
|
||||
class NoDeleteQuerySet(models.query.QuerySet):
|
||||
|
||||
def delete(self):
|
||||
|
@ -58,3 +60,31 @@ class IDInFilterMixin(object):
|
|||
if isinstance(ids, list):
|
||||
queryset = queryset.filter(id__in=ids)
|
||||
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.conf import settings
|
||||
from common import celery_app as app
|
||||
from .utils import get_logger
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@app.task
|
||||
|
@ -26,4 +27,7 @@ def send_mail_async(*args, **kwargs):
|
|||
args.insert(2, settings.EMAIL_HOST_USER)
|
||||
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',
|
||||
'port': CONFIG.REDIS_PORT or 6379,
|
||||
}
|
||||
CELERY_TASK_SERIALIZER = 'pickle'
|
||||
CELERY_RESULT_SERIALIZER = 'pickle'
|
||||
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
|
||||
|
||||
# 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
|
||||
CACHES = {
|
||||
|
|
|
@ -21,6 +21,7 @@ def is_uuid(s):
|
|||
return False
|
||||
|
||||
|
||||
|
||||
def record_adhoc(func):
|
||||
def _deco(adhoc, **options):
|
||||
record = AdHocRunHistory(adhoc=adhoc)
|
||||
|
|
|
@ -156,7 +156,7 @@ function activeNav() {
|
|||
function APIUpdateAttr(props) {
|
||||
// props = {url: .., body: , success: , error: , method: ,}
|
||||
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.';
|
||||
$.ajax({
|
||||
url: props.url,
|
||||
|
@ -169,11 +169,10 @@ function APIUpdateAttr(props) {
|
|||
if (typeof props.success === 'function') {
|
||||
return props.success(data);
|
||||
}
|
||||
|
||||
}).fail(function(jqXHR, textStatue, errorThrown) {
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
toastr.error(fail_message);
|
||||
if (typeof props.error === 'function') {
|
||||
return props.error(errorThrown);
|
||||
return props.error(jqXHR.responseText);
|
||||
}
|
||||
});
|
||||
// return true;
|
||||
|
@ -265,7 +264,7 @@ jumpserver.initDataTable = function (options) {
|
|||
language: {
|
||||
url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json"
|
||||
},
|
||||
order: options.order || [[ 1, 'asc' ]],
|
||||
order: options.order || [],
|
||||
select: options.select || 'multi',
|
||||
buttons: [],
|
||||
columnDefs: columnDefs,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% if 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 }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -5,3 +5,8 @@ from django.apps import AppConfig
|
|||
|
||||
class ApplicationsConfig(AppConfig):
|
||||
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 -*-
|
||||
#
|
||||
import threading
|
||||
import time
|
||||
|
||||
from celery import shared_task
|
||||
from django.core.cache import cache
|
||||
from django.db.utils import ProgrammingError
|
||||
from django.db.utils import ProgrammingError, OperationalError
|
||||
|
||||
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
|
||||
RUNNING = False
|
||||
|
||||
|
@ -24,46 +20,7 @@ def clean_terminal_history():
|
|||
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 -*-
|
||||
#
|
||||
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():
|
||||
|
|
|
@ -4,11 +4,11 @@ from rest_framework import generics
|
|||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import viewsets
|
||||
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 .models import User, UserGroup
|
||||
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
|
||||
|
@ -20,49 +20,23 @@ from common.utils import get_logger
|
|||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
# class UserListView(generics.ListAPIView):
|
||||
# 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.
|
||||
"""
|
||||
class UserViewSet(BulkModelViewSet):
|
||||
queryset = User.objects.all()
|
||||
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
|
||||
serializer_class = serializers.UserSerializer
|
||||
serializer_class = UserSerializer
|
||||
permission_classes = (IsSuperUser,)
|
||||
filter_fields = ('username', 'email', 'name', 'id')
|
||||
|
||||
|
||||
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = serializers.UserUpdateGroupSerializer
|
||||
serializer_class = UserUpdateGroupSerializer
|
||||
permission_classes = (IsSuperUser,)
|
||||
|
||||
|
||||
class UserResetPasswordApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = serializers.UserSerializer
|
||||
serializer_class = UserSerializer
|
||||
|
||||
def perform_update(self, serializer):
|
||||
# Note: we are not updating the user object here.
|
||||
|
@ -77,7 +51,7 @@ class UserResetPasswordApi(generics.UpdateAPIView):
|
|||
|
||||
class UserResetPKApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = serializers.UserSerializer
|
||||
serializer_class = UserSerializer
|
||||
|
||||
def perform_update(self, serializer):
|
||||
from .utils import send_reset_ssh_key_mail
|
||||
|
@ -89,7 +63,7 @@ class UserResetPKApi(generics.UpdateAPIView):
|
|||
|
||||
class UserUpdatePKApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = serializers.UserPKUpdateSerializer
|
||||
serializer_class = UserPKUpdateSerializer
|
||||
permission_classes = (IsCurrentUserOrReadOnly,)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
|
@ -100,12 +74,12 @@ class UserUpdatePKApi(generics.UpdateAPIView):
|
|||
|
||||
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||
queryset = UserGroup.objects.all()
|
||||
serializer_class = serializers.UserGroupSerializer
|
||||
serializer_class = UserGroupSerializer
|
||||
|
||||
|
||||
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
||||
queryset = UserGroup.objects.all()
|
||||
serializer_class = serializers.UserGroupUpdateMemeberSerializer
|
||||
serializer_class = UserGroupUpdateMemeberSerializer
|
||||
permission_classes = (IsSuperUser,)
|
||||
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ class User(AbstractUser):
|
|||
return 'https://www.gravatar.com/avatar/c6812ab450230979465d7bf288eadce2a?s=120&d=identicon'
|
||||
|
||||
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):
|
||||
return OrderedDict({
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
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.mixins import BulkSerializerMixin
|
||||
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
|
||||
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'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
|
||||
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/(?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/(?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
|
||||
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
|
||||
|
|
|
@ -31,7 +31,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin):
|
|||
return True
|
||||
|
||||
|
||||
def user_add_success_next(user):
|
||||
def send_user_created_mail(user):
|
||||
subject = _('Create account successfully')
|
||||
recipient_list = [user.email]
|
||||
message = _("""
|
||||
|
@ -58,6 +58,8 @@ def user_add_success_next(user):
|
|||
'email': user.email,
|
||||
'login_url': reverse('users:login', external=True),
|
||||
}
|
||||
if settings.DEBUG:
|
||||
print(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 ..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.utils import get_logger, get_object_or_none
|
||||
from perms.models import AssetPermission
|
||||
|
@ -74,7 +75,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
|||
user = form.save(commit=False)
|
||||
user.created_by = self.request.user.username or 'System'
|
||||
user.save()
|
||||
user_add_success_next(user)
|
||||
on_user_created.send(self.__class__, user=user)
|
||||
return super(UserCreateView, self).form_valid(form)
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
|
@ -281,7 +282,7 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
|||
user = User.objects.create(**user_dict)
|
||||
user.groups.set(groups)
|
||||
created.append(user_dict['username'])
|
||||
user_add_success_next(user)
|
||||
on_user_created.send(self.__class__, user=user)
|
||||
except Exception as e:
|
||||
failed.append('%s: %s' % (user_dict['username'], str(e)))
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue