mirror of https://github.com/jumpserver/jumpserver
commit
a27fb18a17
|
@ -1 +1,2 @@
|
||||||
from .remote_app import *
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
|
||||||
|
from .. import models
|
||||||
|
from .. import serializers
|
||||||
|
from ..hands import IsOrgAdminOrAppUser
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'DatabaseAppViewSet',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppViewSet(OrgBulkModelViewSet):
|
||||||
|
model = models.DatabaseApp
|
||||||
|
filter_fields = ('name',)
|
||||||
|
search_fields = filter_fields
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
serializer_class = serializers.DatabaseAppSerializer
|
|
@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
# RemoteApp
|
# RemoteApp
|
||||||
|
|
||||||
REMOTE_APP_BOOT_PROGRAM_NAME = '||jmservisor'
|
REMOTE_APP_BOOT_PROGRAM_NAME = '||jmservisor'
|
||||||
|
|
||||||
REMOTE_APP_TYPE_CHROME = 'chrome'
|
REMOTE_APP_TYPE_CHROME = 'chrome'
|
||||||
|
@ -12,29 +13,6 @@ REMOTE_APP_TYPE_MYSQL_WORKBENCH = 'mysql_workbench'
|
||||||
REMOTE_APP_TYPE_VMWARE_CLIENT = 'vmware_client'
|
REMOTE_APP_TYPE_VMWARE_CLIENT = 'vmware_client'
|
||||||
REMOTE_APP_TYPE_CUSTOM = 'custom'
|
REMOTE_APP_TYPE_CUSTOM = 'custom'
|
||||||
|
|
||||||
REMOTE_APP_TYPE_CHOICES = (
|
|
||||||
(
|
|
||||||
_('Browser'),
|
|
||||||
(
|
|
||||||
(REMOTE_APP_TYPE_CHROME, 'Chrome'),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
_('Database tools'),
|
|
||||||
(
|
|
||||||
(REMOTE_APP_TYPE_MYSQL_WORKBENCH, 'MySQL Workbench'),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
_('Virtualization tools'),
|
|
||||||
(
|
|
||||||
(REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
(REMOTE_APP_TYPE_CUSTOM, _('Custom')),
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
# Fields attribute write_only default => False
|
# Fields attribute write_only default => False
|
||||||
|
|
||||||
REMOTE_APP_TYPE_CHROME_FIELDS = [
|
REMOTE_APP_TYPE_CHROME_FIELDS = [
|
||||||
|
@ -60,9 +38,26 @@ REMOTE_APP_TYPE_CUSTOM_FIELDS = [
|
||||||
{'name': 'custom_password', 'write_only': True}
|
{'name': 'custom_password', 'write_only': True}
|
||||||
]
|
]
|
||||||
|
|
||||||
REMOTE_APP_TYPE_MAP_FIELDS = {
|
REMOTE_APP_TYPE_FIELDS_MAP = {
|
||||||
REMOTE_APP_TYPE_CHROME: REMOTE_APP_TYPE_CHROME_FIELDS,
|
REMOTE_APP_TYPE_CHROME: REMOTE_APP_TYPE_CHROME_FIELDS,
|
||||||
REMOTE_APP_TYPE_MYSQL_WORKBENCH: REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS,
|
REMOTE_APP_TYPE_MYSQL_WORKBENCH: REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS,
|
||||||
REMOTE_APP_TYPE_VMWARE_CLIENT: REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS,
|
REMOTE_APP_TYPE_VMWARE_CLIENT: REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS,
|
||||||
REMOTE_APP_TYPE_CUSTOM: REMOTE_APP_TYPE_CUSTOM_FIELDS
|
REMOTE_APP_TYPE_CUSTOM: REMOTE_APP_TYPE_CUSTOM_FIELDS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REMOTE_APP_TYPE_CHOICES = (
|
||||||
|
(REMOTE_APP_TYPE_CHROME, 'Chrome'),
|
||||||
|
(REMOTE_APP_TYPE_MYSQL_WORKBENCH, 'MySQL Workbench'),
|
||||||
|
(REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'),
|
||||||
|
(REMOTE_APP_TYPE_CUSTOM, _('Custom')),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# DatabaseApp
|
||||||
|
|
||||||
|
|
||||||
|
DATABASE_APP_TYPE_MYSQL = 'mysql'
|
||||||
|
|
||||||
|
DATABASE_APP_TYPE_CHOICES = (
|
||||||
|
(DATABASE_APP_TYPE_MYSQL, 'MySQL'),
|
||||||
|
)
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
from .remote_app import *
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from .. import models
|
||||||
|
|
||||||
|
__all__ = ['DatabaseAppMySQLForm']
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDatabaseAppForm(forms.ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['type'].widget.attrs['disabled'] = True
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.DatabaseApp
|
||||||
|
fields = [
|
||||||
|
'name', 'type', 'host', 'port', 'database', 'comment'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppMySQLForm(BaseDatabaseAppForm):
|
||||||
|
pass
|
|
@ -5,18 +5,52 @@ from django.utils.translation import ugettext as _
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from orgs.mixins.forms import OrgModelForm
|
from orgs.mixins.forms import OrgModelForm
|
||||||
from assets.models import SystemUser
|
|
||||||
|
|
||||||
from ..models import RemoteApp
|
from ..models import RemoteApp
|
||||||
from .. import const
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'RemoteAppCreateUpdateForm',
|
'RemoteAppChromeForm', 'RemoteAppMySQLWorkbenchForm',
|
||||||
|
'RemoteAppVMwareForm', 'RemoteAppCustomForm'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppTypeChromeForm(forms.ModelForm):
|
class BaseRemoteAppForm(OrgModelForm):
|
||||||
|
default_initial_data = {}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# 过滤RDP资产和系统用户
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
field_asset = self.fields['asset']
|
||||||
|
field_asset.queryset = field_asset.queryset.has_protocol('rdp')
|
||||||
|
self.fields['type'].widget.attrs['disabled'] = True
|
||||||
|
self.fields.move_to_end('comment')
|
||||||
|
self.initial_default()
|
||||||
|
|
||||||
|
def initial_default(self):
|
||||||
|
for name, value in self.default_initial_data.items():
|
||||||
|
field = self.fields.get(name)
|
||||||
|
if not field:
|
||||||
|
continue
|
||||||
|
field.initial = value
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RemoteApp
|
||||||
|
fields = [
|
||||||
|
'name', 'asset', 'type', 'path', 'comment'
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'asset': forms.Select(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Asset')
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteAppChromeForm(BaseRemoteAppForm):
|
||||||
|
default_initial_data = {
|
||||||
|
'path': r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
|
||||||
|
}
|
||||||
|
|
||||||
chrome_target = forms.CharField(
|
chrome_target = forms.CharField(
|
||||||
max_length=128, label=_('Target URL'), required=False
|
max_length=128, label=_('Target URL'), required=False
|
||||||
)
|
)
|
||||||
|
@ -29,7 +63,12 @@ class RemoteAppTypeChromeForm(forms.ModelForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppTypeMySQLWorkbenchForm(forms.ModelForm):
|
class RemoteAppMySQLWorkbenchForm(BaseRemoteAppForm):
|
||||||
|
default_initial_data = {
|
||||||
|
'path': r'C:\Program Files\MySQL\MySQL Workbench 8.0 CE'
|
||||||
|
r'\MySQLWorkbench.exe'
|
||||||
|
}
|
||||||
|
|
||||||
mysql_workbench_ip = forms.CharField(
|
mysql_workbench_ip = forms.CharField(
|
||||||
max_length=128, label=_('Database IP'), required=False
|
max_length=128, label=_('Database IP'), required=False
|
||||||
)
|
)
|
||||||
|
@ -45,7 +84,12 @@ class RemoteAppTypeMySQLWorkbenchForm(forms.ModelForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppTypeVMwareForm(forms.ModelForm):
|
class RemoteAppVMwareForm(BaseRemoteAppForm):
|
||||||
|
default_initial_data = {
|
||||||
|
'path': r'C:\Program Files (x86)\VMware\Infrastructure'
|
||||||
|
r'\Virtual Infrastructure Client\Launcher\VpxClient.exe'
|
||||||
|
}
|
||||||
|
|
||||||
vmware_target = forms.CharField(
|
vmware_target = forms.CharField(
|
||||||
max_length=128, label=_('Target address'), required=False
|
max_length=128, label=_('Target address'), required=False
|
||||||
)
|
)
|
||||||
|
@ -58,7 +102,8 @@ class RemoteAppTypeVMwareForm(forms.ModelForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppTypeCustomForm(forms.ModelForm):
|
class RemoteAppCustomForm(BaseRemoteAppForm):
|
||||||
|
|
||||||
custom_cmdline = forms.CharField(
|
custom_cmdline = forms.CharField(
|
||||||
max_length=128, label=_('Operating parameter'), required=False
|
max_length=128, label=_('Operating parameter'), required=False
|
||||||
)
|
)
|
||||||
|
@ -73,51 +118,3 @@ class RemoteAppTypeCustomForm(forms.ModelForm):
|
||||||
max_length=128, label=_('Login password'), required=False
|
max_length=128, label=_('Login password'), required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppTypeForms(
|
|
||||||
RemoteAppTypeChromeForm,
|
|
||||||
RemoteAppTypeMySQLWorkbenchForm,
|
|
||||||
RemoteAppTypeVMwareForm,
|
|
||||||
RemoteAppTypeCustomForm
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppCreateUpdateForm(RemoteAppTypeForms, OrgModelForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
# 过滤RDP资产和系统用户
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
field_asset = self.fields['asset']
|
|
||||||
field_asset.queryset = field_asset.queryset.has_protocol('rdp')
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = RemoteApp
|
|
||||||
fields = [
|
|
||||||
'name', 'asset', 'type', 'path', 'comment'
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'asset': forms.Select(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Asset')
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
def _clean_params(self):
|
|
||||||
app_type = self.data.get('type')
|
|
||||||
fields = const.REMOTE_APP_TYPE_MAP_FIELDS.get(app_type, [])
|
|
||||||
params = {}
|
|
||||||
for field in fields:
|
|
||||||
name = field['name']
|
|
||||||
value = self.cleaned_data[name]
|
|
||||||
params.update({name: value})
|
|
||||||
return params
|
|
||||||
|
|
||||||
def _save_params(self, instance):
|
|
||||||
params = self._clean_params()
|
|
||||||
instance.params = params
|
|
||||||
instance.save()
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
instance = super().save(commit=commit)
|
|
||||||
instance = self._save_params(instance)
|
|
||||||
return instance
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.1.11 on 2019-12-10 08:59
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('applications', '0002_remove_remoteapp_system_user'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='remoteapp',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Generated by Django 2.1.11 on 2019-12-18 09:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('applications', '0003_auto_20191210_1659'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DatabaseApp',
|
||||||
|
fields=[
|
||||||
|
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||||
|
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||||
|
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||||
|
('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=128, verbose_name='Type')),
|
||||||
|
('host', models.CharField(db_index=True, max_length=128, verbose_name='Host')),
|
||||||
|
('port', models.IntegerField(default=3306, verbose_name='Port')),
|
||||||
|
('database', models.CharField(blank=True, db_index=True, max_length=128, null=True, verbose_name='Database')),
|
||||||
|
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'DatabaseApp',
|
||||||
|
'ordering': ('name',),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='databaseapp',
|
||||||
|
unique_together={('org_id', 'name')},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1 +1,2 @@
|
||||||
from .remote_app import *
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
from common.mixins import CommonModelMixin
|
||||||
|
from .. import const
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['DatabaseApp']
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseApp(CommonModelMixin, OrgModelMixin):
|
||||||
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
|
type = models.CharField(
|
||||||
|
default=const.DATABASE_APP_TYPE_MYSQL,
|
||||||
|
choices=const.DATABASE_APP_TYPE_CHOICES,
|
||||||
|
max_length=128, verbose_name=_('Type')
|
||||||
|
)
|
||||||
|
host = models.CharField(
|
||||||
|
max_length=128, verbose_name=_('Host'), db_index=True
|
||||||
|
)
|
||||||
|
port = models.IntegerField(default=3306, verbose_name=_('Port'))
|
||||||
|
database = models.CharField(
|
||||||
|
max_length=128, blank=True, null=True, verbose_name=_('Database'),
|
||||||
|
db_index=True
|
||||||
|
)
|
||||||
|
comment = models.TextField(
|
||||||
|
max_length=128, default='', blank=True, verbose_name=_('Comment')
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = [('org_id', 'name'), ]
|
||||||
|
verbose_name = _("DatabaseApp")
|
||||||
|
ordering = ('name', )
|
|
@ -62,7 +62,7 @@ class RemoteApp(OrgModelMixin):
|
||||||
_parameters.append(self.type)
|
_parameters.append(self.type)
|
||||||
path = '\"%s\"' % self.path
|
path = '\"%s\"' % self.path
|
||||||
_parameters.append(path)
|
_parameters.append(path)
|
||||||
for field in const.REMOTE_APP_TYPE_MAP_FIELDS[self.type]:
|
for field in const.REMOTE_APP_TYPE_FIELDS_MAP[self.type]:
|
||||||
value = self.params.get(field['name'])
|
value = self.params.get(field['name'])
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
from .remote_app import *
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
|
|
||||||
|
from .. import models
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'DatabaseAppSerializer',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.DatabaseApp
|
||||||
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
|
fields = [
|
||||||
|
'id', 'name', 'type', 'get_type_display', 'host', 'port',
|
||||||
|
'database', 'comment', 'created_by', 'date_created', 'date_updated',
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'created_by', 'date_created', 'date_updated'
|
||||||
|
'get_type_display',
|
||||||
|
]
|
|
@ -1,10 +1,11 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import copy
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from common.serializers import AdaptedBulkListSerializer
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
|
from common.fields.serializer import CustomMetaDictField
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
|
|
||||||
from .. import const
|
from .. import const
|
||||||
|
@ -16,72 +17,54 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppParamsDictField(serializers.DictField):
|
class RemoteAppParamsDictField(CustomMetaDictField):
|
||||||
"""
|
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
|
||||||
RemoteApp field => params
|
default_type = const.REMOTE_APP_TYPE_CHROME
|
||||||
"""
|
convert_key_remove_type_prefix = False
|
||||||
@staticmethod
|
convert_key_to_upper = False
|
||||||
def filter_attribute(attribute, instance):
|
|
||||||
"""
|
|
||||||
过滤掉params字段值中write_only特性的key-value值
|
|
||||||
For example, the chrome_password field is not returned when serializing
|
|
||||||
{
|
|
||||||
'chrome_target': 'http://www.jumpserver.org/',
|
|
||||||
'chrome_username': 'admin',
|
|
||||||
'chrome_password': 'admin',
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
for field in const.REMOTE_APP_TYPE_MAP_FIELDS[instance.type]:
|
|
||||||
if field.get('write_only', False):
|
|
||||||
attribute.pop(field['name'], None)
|
|
||||||
return attribute
|
|
||||||
|
|
||||||
def get_attribute(self, instance):
|
|
||||||
"""
|
|
||||||
序列化时调用
|
|
||||||
"""
|
|
||||||
attribute = super().get_attribute(instance)
|
|
||||||
attribute = self.filter_attribute(attribute, instance)
|
|
||||||
return attribute
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def filter_value(dictionary, value):
|
|
||||||
"""
|
|
||||||
过滤掉不属于当前app_type所包含的key-value值
|
|
||||||
"""
|
|
||||||
app_type = dictionary.get('type', const.REMOTE_APP_TYPE_CHROME)
|
|
||||||
fields = const.REMOTE_APP_TYPE_MAP_FIELDS[app_type]
|
|
||||||
fields_names = [field['name'] for field in fields]
|
|
||||||
no_need_keys = [k for k in value.keys() if k not in fields_names]
|
|
||||||
for k in no_need_keys:
|
|
||||||
value.pop(k)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
|
||||||
"""
|
|
||||||
反序列化时调用
|
|
||||||
"""
|
|
||||||
value = super().get_value(dictionary)
|
|
||||||
value = self.filter_value(dictionary, value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppSerializer(BulkOrgResourceModelSerializer):
|
class RemoteAppSerializer(BulkOrgResourceModelSerializer):
|
||||||
params = RemoteAppParamsDictField()
|
params = RemoteAppParamsDictField()
|
||||||
|
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RemoteApp
|
model = RemoteApp
|
||||||
list_serializer_class = AdaptedBulkListSerializer
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'name', 'asset', 'type', 'path', 'params',
|
'id', 'name', 'asset', 'asset_info', 'type', 'get_type_display',
|
||||||
'comment', 'created_by', 'date_created', 'asset_info',
|
'path', 'params', 'date_created', 'created_by', 'comment',
|
||||||
'get_type_display',
|
|
||||||
]
|
]
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
'created_by', 'date_created', 'asset_info',
|
'created_by', 'date_created', 'asset_info',
|
||||||
'get_type_display'
|
'get_type_display'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def process_params(self, instance, validated_data):
|
||||||
|
new_params = copy.deepcopy(validated_data.get('params', {}))
|
||||||
|
tp = validated_data.get('type', '')
|
||||||
|
|
||||||
|
if tp != instance.type:
|
||||||
|
return new_params
|
||||||
|
|
||||||
|
old_params = instance.params
|
||||||
|
fields = self.type_fields_map.get(instance.type, [])
|
||||||
|
for field in fields:
|
||||||
|
if not field.get('write_only', False):
|
||||||
|
continue
|
||||||
|
field_name = field['name']
|
||||||
|
new_value = new_params.get(field_name, '')
|
||||||
|
old_value = old_params.get(field_name, '')
|
||||||
|
field_value = new_value if new_value else old_value
|
||||||
|
new_params[field_name] = field_value
|
||||||
|
|
||||||
|
return new_params
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
params = self.process_params(instance, validated_data)
|
||||||
|
validated_data['params'] = params
|
||||||
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer):
|
class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer):
|
||||||
parameter_remote_app = serializers.SerializerMethodField()
|
parameter_remote_app = serializers.SerializerMethodField()
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
{% extends '_base_create_update.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
<form id="DatabaseAppForm" method="post" class="form-horizontal">
|
||||||
|
{% bootstrap_form form 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>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var app_type_id = '#' + '{{ form.type.id_for_label }}';
|
||||||
|
|
||||||
|
function getFormDataType(){
|
||||||
|
return $(app_type_id+ " option:selected").val();
|
||||||
|
}
|
||||||
|
function getFormData(form){
|
||||||
|
var data = form.serializeObject();
|
||||||
|
data['type'] = getFormDataType();
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
})
|
||||||
|
.on("submit", "form", function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var the_url = '{% url "api-applications:database-app-list" %}';
|
||||||
|
var redirect_to = '{% url "applications:database-app-list" %}';
|
||||||
|
var method = "POST";
|
||||||
|
{% if api_action == "update" %}
|
||||||
|
the_url = '{% url "api-applications:database-app-detail" object.id %}';
|
||||||
|
method = "PUT";
|
||||||
|
{% endif %}
|
||||||
|
var form = $("form");
|
||||||
|
var data = getFormData(form);
|
||||||
|
var props = {
|
||||||
|
url: the_url,
|
||||||
|
data: data,
|
||||||
|
method: method,
|
||||||
|
form: form,
|
||||||
|
redirect_to: redirect_to
|
||||||
|
};
|
||||||
|
formSubmit(props);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,103 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'applications:database-app-detail' pk=database_app.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'applications:database-app-update' pk=database_app.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-danger btn-delete-application">
|
||||||
|
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="col-sm-8" style="padding-left: 0;">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label"><b>{{ database_app.name }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td>{% trans 'Name' %}:</td>
|
||||||
|
<td><b>{{ database_app.name }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Type' %}:</td>
|
||||||
|
<td><b>{{ database_app.get_type_display }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Host' %}:</td>
|
||||||
|
<td><b>{{ database_app.host }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Port' %}:</td>
|
||||||
|
<td><b>{{ database_app.port }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Database' %}:</td>
|
||||||
|
<td><b>{{ database_app.database }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date created' %}:</td>
|
||||||
|
<td><b>{{ database_app.date_created }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Created by' %}:</td>
|
||||||
|
<td><b>{{ database_app.created_by }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Comment' %}:</td>
|
||||||
|
<td><b>{{ database_app.comment }}</b></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
})
|
||||||
|
.on('click', '.btn-delete-application', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var name = "{{ database_app.name }}";
|
||||||
|
var rid = "{{ database_app.id }}";
|
||||||
|
var the_url = '{% url "api-applications:database-app-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', rid);
|
||||||
|
var redirect_url = "{% url 'applications:database-app-list' %}";
|
||||||
|
objectDelete($this, name, the_url, redirect_url);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,89 @@
|
||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n static %}
|
||||||
|
{% block help_message %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block table_search %}{% endblock %}
|
||||||
|
{% block table_container %}
|
||||||
|
<div class="btn-group uc pull-left m-r-5">
|
||||||
|
<button class="btn btn-sm btn-primary">
|
||||||
|
{% trans "Create DatabaseApp" %}
|
||||||
|
</button>
|
||||||
|
<button data-toggle="dropdown" class="btn btn-primary btn-sm dropdown-toggle"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% for key, value in type_choices %}
|
||||||
|
<li><a class="" href="{% url 'applications:database-app-create' %}?type={{ key }}">{{ value }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="database_app_list_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_all" class="ipt_check_all" >
|
||||||
|
</th>
|
||||||
|
<th class="text-center">{% trans 'Name' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Type' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Host' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Port' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Database' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Comment' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content_bottom_left %}{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
function initTable() {
|
||||||
|
var options = {
|
||||||
|
ele: $('#database_app_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
cellData = htmlEscape(cellData);
|
||||||
|
{% url 'applications:database-app-detail' pk=DEFAULT_PK as the_url %}
|
||||||
|
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 2, createdCell: function (td, cellData, rowData) {
|
||||||
|
$(td).html(rowData.get_type_display)
|
||||||
|
}},
|
||||||
|
{targets: 7, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "applications:database-app-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
|
||||||
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-rid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-applications:database-app-list" %}',
|
||||||
|
columns: [
|
||||||
|
{data: "id"},
|
||||||
|
{data: "name" },
|
||||||
|
{data: "type"},
|
||||||
|
{data: "host"},
|
||||||
|
{data: "port"},
|
||||||
|
{data: "database"},
|
||||||
|
{data: "comment"},
|
||||||
|
{data: "id", orderable: false, width: "120px"}
|
||||||
|
],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initServerSideDataTable(options);
|
||||||
|
}
|
||||||
|
$(document).ready(function(){
|
||||||
|
initTable();
|
||||||
|
})
|
||||||
|
.on('click', '.btn-delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $data_table = $('#database_app_list_table').DataTable();
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
|
||||||
|
var rid = $this.data('rid');
|
||||||
|
var the_url = '{% url "api-applications:database-app-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', rid);
|
||||||
|
objectDelete($this, name, the_url);
|
||||||
|
setTimeout( function () {
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -4,51 +4,8 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form %}
|
{% block form %}
|
||||||
<form id="appForm" method="post" class="form-horizontal">
|
<form id="RemoteAppForm" method="post" class="form-horizontal">
|
||||||
{% if form.non_field_errors %}
|
{% bootstrap_form form layout="horizontal" %}
|
||||||
<div class="alert alert-danger">
|
|
||||||
{{ form.non_field_errors }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% csrf_token %}
|
|
||||||
{% bootstrap_field form.name layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.asset layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.type layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.path layout="horizontal" %}
|
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
|
||||||
|
|
||||||
{# chrome #}
|
|
||||||
<div class="chrome-fields">
|
|
||||||
{% bootstrap_field form.chrome_target layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.chrome_username layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.chrome_password layout="horizontal" %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# mysql workbench #}
|
|
||||||
<div class="mysql_workbench-fields">
|
|
||||||
{% bootstrap_field form.mysql_workbench_ip layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.mysql_workbench_name layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.mysql_workbench_username layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.mysql_workbench_password layout="horizontal" %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# vmware #}
|
|
||||||
<div class="vmware_client-fields">
|
|
||||||
{% bootstrap_field form.vmware_target layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.vmware_username layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.vmware_password layout="horizontal" %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# custom #}
|
|
||||||
<div class="custom-fields">
|
|
||||||
{% bootstrap_field form.custom_cmdline layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.custom_target layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.custom_username layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.custom_password layout="horizontal" %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% bootstrap_field form.comment layout="horizontal" %}
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-4 col-sm-offset-2">
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
@ -57,93 +14,49 @@
|
||||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var app_type_id = '#' + '{{ form.type.id_for_label }}';
|
var app_type_id = '#' + '{{ form.type.id_for_label }}';
|
||||||
var app_path_id = '#' + '{{ form.path.id_for_label }}';
|
|
||||||
var all_type_fields = [
|
function getFormDataType(){
|
||||||
'.chrome-fields',
|
|
||||||
'.mysql_workbench-fields',
|
|
||||||
'.vmware_client-fields',
|
|
||||||
'.custom-fields'
|
|
||||||
];
|
|
||||||
var app_type_map_default_fields_value = {
|
|
||||||
'chrome': {
|
|
||||||
'app_path': 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
|
|
||||||
},
|
|
||||||
'mysql_workbench': {
|
|
||||||
'app_path': 'C:\\Program Files\\MySQL\\MySQL Workbench 8.0 CE\\MySQLWorkbench.exe'
|
|
||||||
},
|
|
||||||
'vmware_client': {
|
|
||||||
'app_path': 'C:\\Program Files (x86)\\VMware\\Infrastructure\\Virtual Infrastructure Client\\Launcher\\VpxClient.exe'
|
|
||||||
},
|
|
||||||
'custom': {'app_path': ''}
|
|
||||||
};
|
|
||||||
function getAppType(){
|
|
||||||
return $(app_type_id+ " option:selected").val();
|
return $(app_type_id+ " option:selected").val();
|
||||||
}
|
}
|
||||||
function initialDefaultValue(){
|
function constructFormDataParams(data){
|
||||||
var app_type = getAppType();
|
|
||||||
var app_path = $(app_path_id).val();
|
|
||||||
if(app_path){
|
|
||||||
app_type_map_default_fields_value[app_type]['app_path'] = app_path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function setDefaultValue(){
|
|
||||||
// 设置类型相关字段的默认值
|
|
||||||
var app_type = getAppType();
|
|
||||||
var app_path = app_type_map_default_fields_value[app_type]['app_path'];
|
|
||||||
$(app_path_id).val(app_path)
|
|
||||||
}
|
|
||||||
function hiddenFields(){
|
|
||||||
var app_type = getAppType();
|
|
||||||
$.each(all_type_fields, function(index, value){
|
|
||||||
$(value).addClass('hidden')
|
|
||||||
});
|
|
||||||
$('.' + app_type + '-fields').removeClass('hidden');
|
|
||||||
}
|
|
||||||
function constructParams(data) {
|
|
||||||
var typeList = ['chrome', 'mysql_workbench', 'vmware_client', 'custom'];
|
|
||||||
var params = {};
|
var params = {};
|
||||||
$.each(typeList, function(index, value){
|
var type =data.type;
|
||||||
if (data.type === value){
|
for (var k in data){
|
||||||
for (var k in data){
|
if (k.startsWith(type)){
|
||||||
if (k.startsWith(value)){
|
params[k] = data[k];
|
||||||
params[k] = data[k]
|
delete data[k]
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return params;
|
return params
|
||||||
}
|
}
|
||||||
|
function getFormData(form){
|
||||||
|
var data = form.serializeObject();
|
||||||
|
data['type'] = getFormDataType();
|
||||||
|
data['params'] = constructFormDataParams(data);
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2({
|
$('.select2').select2({
|
||||||
closeOnSelect: true
|
closeOnSelect: true
|
||||||
});
|
});
|
||||||
initialDefaultValue();
|
}).on("submit", "form", function (evt) {
|
||||||
hiddenFields();
|
|
||||||
setDefaultValue();
|
|
||||||
})
|
|
||||||
.on('change', app_type_id, function(){
|
|
||||||
hiddenFields();
|
|
||||||
setDefaultValue();
|
|
||||||
})
|
|
||||||
.on("submit", "form", function (evt) {
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
var the_url = '{% url "api-applications:remote-app-list" %}';
|
var the_url = '{% url "api-applications:remote-app-list" %}';
|
||||||
var redirect_to = '{% url "applications:remote-app-list" %}';
|
var redirect_to = '{% url "applications:remote-app-list" %}';
|
||||||
var method = "POST";
|
var method = "POST";
|
||||||
{% if type == "update" %}
|
{% if api_action == "update" %}
|
||||||
the_url = '{% url "api-applications:remote-app-detail" object.id %}';
|
the_url = '{% url "api-applications:remote-app-detail" object.id %}';
|
||||||
method = "PUT";
|
method = "PUT";
|
||||||
{% endif %}
|
{% endif %}
|
||||||
var form = $("form");
|
var form = $("form");
|
||||||
var data = form.serializeObject();
|
var data = getFormData(form);
|
||||||
data["params"] = constructParams(data);
|
|
||||||
var props = {
|
var props = {
|
||||||
url: the_url,
|
url: the_url,
|
||||||
data: data,
|
data: data,
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -6,8 +6,16 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block table_search %}{% endblock %}
|
{% block table_search %}{% endblock %}
|
||||||
{% block table_container %}
|
{% block table_container %}
|
||||||
<div class="uc pull-left m-r-5">
|
<div class="btn-group uc pull-left m-r-5">
|
||||||
<a href="{% url 'applications:remote-app-create' %}" class="btn btn-sm btn-primary"> {% trans "Create RemoteApp" %} </a>
|
<button class="btn btn-sm btn-primary">
|
||||||
|
{% trans "Create RemoteApp" %}
|
||||||
|
</button>
|
||||||
|
<button data-toggle="dropdown" class="btn btn-primary btn-sm dropdown-toggle"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% for key, value in type_choices %}
|
||||||
|
<li><a class="" href="{% url 'applications:remote-app-create' %}?type={{ key }}">{{ value }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped table-bordered table-hover " id="remote_app_list_table" >
|
<table class="table table-striped table-bordered table-hover " id="remote_app_list_table" >
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -61,7 +69,7 @@ function initTable() {
|
||||||
{data: "get_type_display", orderable: false},
|
{data: "get_type_display", orderable: false},
|
||||||
{data: "asset_info", orderable: false},
|
{data: "asset_info", orderable: false},
|
||||||
{data: "comment"},
|
{data: "comment"},
|
||||||
{data: "id", orderable: false}
|
{data: "id", orderable: false, width: "120px"}
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n static %}
|
||||||
|
|
||||||
|
{% block custom_head_css_js %}
|
||||||
|
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="mail-box-header">
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="database_app_list_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_all" class="ipt_check_all" >
|
||||||
|
</th>
|
||||||
|
<th class="text-center">{% trans 'Name' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Type' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Host' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Database' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Comment' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
var inited = false;
|
||||||
|
var database_app_table, url;
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
if (inited){
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
inited = true;
|
||||||
|
}
|
||||||
|
url = '{% url "api-perms:my-database-apps" %}';
|
||||||
|
var options = {
|
||||||
|
ele: $('#database_app_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
var name = htmlEscape(cellData);
|
||||||
|
$(td).html(name)
|
||||||
|
}},
|
||||||
|
{targets: 2, createdCell: function (td, cellData, rowData) {
|
||||||
|
var type = htmlEscape(rowData.get_type_display);
|
||||||
|
$(td).html(type);
|
||||||
|
}},
|
||||||
|
{targets: 3, createdCell: function (td, cellData, rowData) {
|
||||||
|
var host = htmlEscape(cellData);
|
||||||
|
$(td).html(host);
|
||||||
|
}},
|
||||||
|
{targets: 4, createdCell: function (td, cellData, rowData) {
|
||||||
|
var database = htmlEscape(cellData);
|
||||||
|
$(td).html(database);
|
||||||
|
}},
|
||||||
|
{targets: 6, createdCell: function (td, cellData, rowData) {
|
||||||
|
var conn_btn = '<a href="{% url "luna-view" %}?type=database_app&login_to=' + cellData +'" class="btn btn-xs btn-primary" target="_blank">{% trans "Connect" %}</a>';
|
||||||
|
$(td).html(conn_btn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: url,
|
||||||
|
columns: [
|
||||||
|
{data: "id"},
|
||||||
|
{data: "name"},
|
||||||
|
{data: "type"},
|
||||||
|
{data: "host"},
|
||||||
|
{data: "database"},
|
||||||
|
{data: "comment", orderable: false},
|
||||||
|
{data: "id", orderable: false}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
database_app_table = jumpserver.initServerSideDataTable(options);
|
||||||
|
return database_app_table
|
||||||
|
}
|
||||||
|
$(document).ready(function(){
|
||||||
|
initTable();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -11,10 +11,12 @@ app_name = 'applications'
|
||||||
|
|
||||||
router = BulkRouter()
|
router = BulkRouter()
|
||||||
router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app')
|
router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app')
|
||||||
|
router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
|
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
|
||||||
]
|
]
|
||||||
|
|
||||||
old_version_urlpatterns = [
|
old_version_urlpatterns = [
|
||||||
re_path('(?P<resource>remote-app)/.*', capi.redirect_plural_name_api)
|
re_path('(?P<resource>remote-app)/.*', capi.redirect_plural_name_api)
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,6 +11,13 @@ urlpatterns = [
|
||||||
path('remote-app/<uuid:pk>/update/', views.RemoteAppUpdateView.as_view(), name='remote-app-update'),
|
path('remote-app/<uuid:pk>/update/', views.RemoteAppUpdateView.as_view(), name='remote-app-update'),
|
||||||
path('remote-app/<uuid:pk>/', views.RemoteAppDetailView.as_view(), name='remote-app-detail'),
|
path('remote-app/<uuid:pk>/', views.RemoteAppDetailView.as_view(), name='remote-app-detail'),
|
||||||
# User RemoteApp view
|
# User RemoteApp view
|
||||||
path('user-remote-app/', views.UserRemoteAppListView.as_view(), name='user-remote-app-list')
|
path('user-remote-app/', views.UserRemoteAppListView.as_view(), name='user-remote-app-list'),
|
||||||
|
|
||||||
|
path('database-app/', views.DatabaseAppListView.as_view(), name='database-app-list'),
|
||||||
|
path('database-app/create/', views.DatabaseAppCreateView.as_view(), name='database-app-create'),
|
||||||
|
path('database-app/<uuid:pk>/update/', views.DatabaseAppUpdateView.as_view(), name='database-app-update'),
|
||||||
|
path('database-app/<uuid:pk>/', views.DatabaseAppDetailView.as_view(), name='database-app-detail'),
|
||||||
|
# User DatabaseApp view
|
||||||
|
path('user-database-app/', views.UserDatabaseAppListView.as_view(), name='user-database-app-list'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
from .remote_app import *
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.http import Http404
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.views.generic.detail import DetailView
|
||||||
|
|
||||||
|
from common.permissions import PermissionsMixin, IsOrgAdmin, IsValidUser
|
||||||
|
|
||||||
|
from .. import models, const, forms
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'DatabaseAppListView', 'DatabaseAppCreateView', 'DatabaseAppUpdateView',
|
||||||
|
'DatabaseAppDetailView', 'UserDatabaseAppListView',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppListView(PermissionsMixin, TemplateView):
|
||||||
|
template_name = 'applications/database_app_list.html'
|
||||||
|
permission_classes = [IsOrgAdmin]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _("Application"),
|
||||||
|
'action': _('DatabaseApp list'),
|
||||||
|
'type_choices': const.DATABASE_APP_TYPE_CHOICES
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDatabaseAppCreateUpdateView:
|
||||||
|
template_name = 'applications/database_app_create_update.html'
|
||||||
|
model = models.DatabaseApp
|
||||||
|
permission_classes = [IsOrgAdmin]
|
||||||
|
default_type = const.DATABASE_APP_TYPE_MYSQL
|
||||||
|
form_class = forms.DatabaseAppMySQLForm
|
||||||
|
form_class_choices = {
|
||||||
|
const.DATABASE_APP_TYPE_MYSQL: forms.DatabaseAppMySQLForm,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
return {'type': self.get_type()}
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return self.default_type
|
||||||
|
|
||||||
|
def get_form_class(self):
|
||||||
|
tp = self.get_type()
|
||||||
|
form_class = self.form_class_choices.get(tp)
|
||||||
|
if not form_class:
|
||||||
|
raise Http404()
|
||||||
|
return form_class
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppCreateView(BaseDatabaseAppCreateUpdateView, CreateView):
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
tp = self.request.GET.get("type")
|
||||||
|
if tp:
|
||||||
|
return tp.lower()
|
||||||
|
return super().get_type()
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Applications'),
|
||||||
|
'action': _('Create DatabaseApp'),
|
||||||
|
'api_action': 'create'
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppUpdateView(BaseDatabaseAppCreateUpdateView, UpdateView):
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return self.object.type
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Applications'),
|
||||||
|
'action': _('Create DatabaseApp'),
|
||||||
|
'api_action': 'update'
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppDetailView(PermissionsMixin, DetailView):
|
||||||
|
template_name = 'applications/database_app_detail.html'
|
||||||
|
model = models.DatabaseApp
|
||||||
|
context_object_name = 'database_app'
|
||||||
|
permission_classes = [IsOrgAdmin]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Applications'),
|
||||||
|
'action': _('DatabaseApp detail'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class UserDatabaseAppListView(PermissionsMixin, TemplateView):
|
||||||
|
template_name = 'applications/user_database_app_list.html'
|
||||||
|
permission_classes = [IsValidUser]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'action': _('My DatabaseApp'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
|
@ -1,19 +1,16 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from django.http import Http404
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.views.generic.edit import CreateView, UpdateView
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
from django.views.generic.detail import DetailView
|
from django.views.generic.detail import DetailView
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
|
||||||
from django.urls import reverse_lazy
|
|
||||||
|
|
||||||
|
|
||||||
from common.permissions import PermissionsMixin, IsOrgAdmin, IsValidUser
|
from common.permissions import PermissionsMixin, IsOrgAdmin, IsValidUser
|
||||||
from common.const import create_success_msg, update_success_msg
|
|
||||||
|
|
||||||
from ..models import RemoteApp
|
from ..models import RemoteApp
|
||||||
from .. import forms
|
from .. import forms, const
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -30,53 +27,79 @@ class RemoteAppListView(PermissionsMixin, TemplateView):
|
||||||
context = {
|
context = {
|
||||||
'app': _('Applications'),
|
'app': _('Applications'),
|
||||||
'action': _('RemoteApp list'),
|
'action': _('RemoteApp list'),
|
||||||
|
'type_choices': const.REMOTE_APP_TYPE_CHOICES,
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppCreateView(PermissionsMixin, SuccessMessageMixin, CreateView):
|
class BaseRemoteAppCreateUpdateView:
|
||||||
template_name = 'applications/remote_app_create_update.html'
|
template_name = 'applications/remote_app_create_update.html'
|
||||||
model = RemoteApp
|
model = RemoteApp
|
||||||
form_class = forms.RemoteAppCreateUpdateForm
|
|
||||||
success_url = reverse_lazy('applications:remote-app-list')
|
|
||||||
permission_classes = [IsOrgAdmin]
|
permission_classes = [IsOrgAdmin]
|
||||||
|
default_type = const.REMOTE_APP_TYPE_CHROME
|
||||||
|
form_class = forms.RemoteAppChromeForm
|
||||||
|
form_class_choices = {
|
||||||
|
const.REMOTE_APP_TYPE_CHROME: forms.RemoteAppChromeForm,
|
||||||
|
const.REMOTE_APP_TYPE_MYSQL_WORKBENCH: forms.RemoteAppMySQLWorkbenchForm,
|
||||||
|
const.REMOTE_APP_TYPE_VMWARE_CLIENT: forms.RemoteAppVMwareForm,
|
||||||
|
const.REMOTE_APP_TYPE_CUSTOM: forms.RemoteAppCustomForm
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
return {'type': self.get_type()}
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return self.default_type
|
||||||
|
|
||||||
|
def get_form_class(self):
|
||||||
|
tp = self.get_type()
|
||||||
|
form_class = self.form_class_choices.get(tp)
|
||||||
|
if not form_class:
|
||||||
|
raise Http404()
|
||||||
|
return form_class
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteAppCreateView(BaseRemoteAppCreateUpdateView,
|
||||||
|
PermissionsMixin, CreateView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'app': _('Applications'),
|
'app': _('Applications'),
|
||||||
'action': _('Create RemoteApp'),
|
'action': _('Create RemoteApp'),
|
||||||
'type': 'create'
|
'api_action': 'create'
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
def get_success_message(self, cleaned_data):
|
def get_type(self):
|
||||||
return create_success_msg % ({'name': cleaned_data['name']})
|
tp = self.request.GET.get("type")
|
||||||
|
if tp:
|
||||||
|
return tp.lower()
|
||||||
|
return super().get_type()
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppUpdateView(PermissionsMixin, SuccessMessageMixin, UpdateView):
|
class RemoteAppUpdateView(BaseRemoteAppCreateUpdateView,
|
||||||
template_name = 'applications/remote_app_create_update.html'
|
PermissionsMixin, UpdateView):
|
||||||
model = RemoteApp
|
|
||||||
form_class = forms.RemoteAppCreateUpdateForm
|
|
||||||
success_url = reverse_lazy('applications:remote-app-list')
|
|
||||||
permission_classes = [IsOrgAdmin]
|
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {k: v for k, v in self.object.params.items()}
|
initial_data = super().get_initial()
|
||||||
|
params = {k: v for k, v in self.object.params.items()}
|
||||||
|
initial_data.update(params)
|
||||||
|
return initial_data
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return self.object.type
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'app': _('Applications'),
|
'app': _('Applications'),
|
||||||
'action': _('Update RemoteApp'),
|
'action': _('Update RemoteApp'),
|
||||||
'type': 'update'
|
'api_action': 'update'
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
def get_success_message(self, cleaned_data):
|
|
||||||
return update_success_msg % ({'name': cleaned_data['name']})
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppDetailView(PermissionsMixin, DetailView):
|
class RemoteAppDetailView(PermissionsMixin, DetailView):
|
||||||
template_name = 'applications/remote_app_detail.html'
|
template_name = 'applications/remote_app_detail.html'
|
||||||
|
|
|
@ -2,6 +2,7 @@ from .admin_user import *
|
||||||
from .asset import *
|
from .asset import *
|
||||||
from .label import *
|
from .label import *
|
||||||
from .system_user import *
|
from .system_user import *
|
||||||
|
from .system_user_relation import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .cmd_filter import *
|
from .cmd_filter import *
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.db.models import Count
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
@ -44,6 +45,11 @@ class AdminUserViewSet(OrgBulkModelViewSet):
|
||||||
serializer_class = serializers.AdminUserSerializer
|
serializer_class = serializers.AdminUserSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset.annotate(_assets_amount=Count('assets'))
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class AdminUserAuthApi(generics.UpdateAPIView):
|
class AdminUserAuthApi(generics.UpdateAPIView):
|
||||||
model = AdminUser
|
model = AdminUser
|
||||||
|
|
|
@ -4,24 +4,27 @@
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
from rest_framework.generics import RetrieveAPIView
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger, get_object_or_none
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsSuperUser
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from orgs.mixins import generics
|
from orgs.mixins import generics
|
||||||
from ..models import Asset, Node
|
from ..models import Asset, Node, Platform
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import update_asset_hardware_info_manual, \
|
from ..tasks import (
|
||||||
test_asset_connectivity_manual
|
update_asset_hardware_info_manual, test_asset_connectivity_manual
|
||||||
|
)
|
||||||
from ..filters import AssetByNodeFilterBackend, LabelFilterBackend
|
from ..filters import AssetByNodeFilterBackend, LabelFilterBackend
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetViewSet',
|
'AssetViewSet', 'AssetPlatformRetrieveApi',
|
||||||
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi',
|
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi',
|
||||||
'AssetGatewayApi',
|
'AssetGatewayApi', 'AssetPlatformViewSet',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +56,34 @@ class AssetViewSet(OrgBulkModelViewSet):
|
||||||
self.set_assets_node(assets)
|
self.set_assets_node(assets)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetPlatformRetrieveApi(RetrieveAPIView):
|
||||||
|
queryset = Platform.objects.all()
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
serializer_class = serializers.PlatformSerializer
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
asset_pk = self.kwargs.get('pk')
|
||||||
|
asset = get_object_or_404(Asset, pk=asset_pk)
|
||||||
|
return asset.platform
|
||||||
|
|
||||||
|
|
||||||
|
class AssetPlatformViewSet(ModelViewSet):
|
||||||
|
queryset = Platform.objects.all()
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
serializer_class = serializers.PlatformSerializer
|
||||||
|
filterset_fields = ['name', 'base']
|
||||||
|
search_fields = ['name']
|
||||||
|
|
||||||
|
def check_object_permissions(self, request, obj):
|
||||||
|
if request.method.lower() in ['delete', 'put', 'patch'] and \
|
||||||
|
obj.internal:
|
||||||
|
self.permission_denied(
|
||||||
|
request, message={"detail": "Internal platform"}
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().check_object_permissions(request, obj)
|
||||||
|
|
||||||
|
|
||||||
class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Refresh asset hardware info
|
Refresh asset hardware info
|
||||||
|
|
|
@ -114,7 +114,7 @@ class AssetUserExportViewSet(AssetUserViewSet):
|
||||||
permission_classes = [IsOrgAdminOrAppUser]
|
permission_classes = [IsOrgAdminOrAppUser]
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA:
|
if settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||||
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ class AssetUserAuthInfoApi(generics.RetrieveAPIView):
|
||||||
permission_classes = [IsOrgAdminOrAppUser]
|
permission_classes = [IsOrgAdminOrAppUser]
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA:
|
if settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||||
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ class NodeChildrenAsTreeApi(NodeChildrenApi):
|
||||||
if not include_assets:
|
if not include_assets:
|
||||||
return queryset
|
return queryset
|
||||||
assets = self.instance.get_assets().only(
|
assets = self.instance.get_assets().only(
|
||||||
"id", "hostname", "ip", 'platform', "os",
|
"id", "hostname", "ip", "os",
|
||||||
"org_id", "protocols",
|
"org_id", "protocols",
|
||||||
)
|
)
|
||||||
for asset in assets:
|
for asset in assets:
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.conf import settings
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from django.db.models import Count
|
||||||
|
|
||||||
from common.serializers import CeleryTaskSerializer
|
from common.serializers import CeleryTaskSerializer
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -50,6 +50,11 @@ class SystemUserViewSet(OrgBulkModelViewSet):
|
||||||
serializer_class = serializers.SystemUserSerializer
|
serializer_class = serializers.SystemUserSerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset.annotate(_assets_amount=Count('assets'))
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from django.db.models import F, Value
|
||||||
|
from django.db.models.functions import Concat
|
||||||
|
|
||||||
|
from common.permissions import IsOrgAdmin
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.utils import current_org
|
||||||
|
from .. import models, serializers
|
||||||
|
|
||||||
|
__all__ = ['SystemUserAssetRelationViewSet', 'SystemUserNodeRelationViewSet']
|
||||||
|
|
||||||
|
|
||||||
|
class RelationMixin(OrgBulkModelViewSet):
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = self.model.objects.all()
|
||||||
|
org_id = current_org.org_id()
|
||||||
|
if org_id is not None:
|
||||||
|
queryset = queryset.filter(systemuser__org_id=org_id)
|
||||||
|
queryset = queryset.annotate(systemuser_display=Concat(
|
||||||
|
F('systemuser__name'), Value('('), F('systemuser__username'),
|
||||||
|
Value(')')
|
||||||
|
))
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserAssetRelationViewSet(RelationMixin):
|
||||||
|
serializer_class = serializers.SystemUserAssetRelationSerializer
|
||||||
|
model = models.SystemUser.assets.through
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
filterset_fields = [
|
||||||
|
'id', 'asset', 'systemuser',
|
||||||
|
]
|
||||||
|
search_fields = [
|
||||||
|
"id", "asset__hostname", "asset__ip",
|
||||||
|
"systemuser__name", "systemuser__username"
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
asset_display=Concat(
|
||||||
|
F('asset__hostname'), Value('('),
|
||||||
|
F('asset__ip'), Value(')')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserNodeRelationViewSet(RelationMixin):
|
||||||
|
serializer_class = serializers.SystemUserNodeRelationSerializer
|
||||||
|
model = models.SystemUser.nodes.through
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
filterset_fields = [
|
||||||
|
'id', 'node', 'systemuser',
|
||||||
|
]
|
||||||
|
search_fields = [
|
||||||
|
"node__value", "systemuser__name", "systemuser_username"
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset \
|
||||||
|
.annotate(node_key=F('node__key'))
|
||||||
|
return queryset
|
|
@ -43,7 +43,7 @@ class AssetUserQuerySet(list):
|
||||||
else:
|
else:
|
||||||
in_kwargs[k] = v
|
in_kwargs[k] = v
|
||||||
for k in in_kwargs:
|
for k in in_kwargs:
|
||||||
kwargs.pop(k)
|
kwargs.pop(k, None)
|
||||||
|
|
||||||
if len(in_kwargs) == 0:
|
if len(in_kwargs) == 0:
|
||||||
return self
|
return self
|
||||||
|
@ -56,7 +56,7 @@ class AssetUserQuerySet(list):
|
||||||
v = [str(i) for i in v]
|
v = [str(i) for i in v]
|
||||||
if isinstance(attr, uuid.UUID):
|
if isinstance(attr, uuid.UUID):
|
||||||
attr = str(attr)
|
attr = str(attr)
|
||||||
if v in attr:
|
if attr in v:
|
||||||
matched = True
|
matched = True
|
||||||
if matched:
|
if matched:
|
||||||
queryset.append(i)
|
queryset.append(i)
|
||||||
|
@ -68,11 +68,12 @@ class AssetUserQuerySet(list):
|
||||||
real = []
|
real = []
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
wanted.append(v)
|
wanted.append(v)
|
||||||
value = getattr(obj, k)
|
value = getattr(obj, k, None)
|
||||||
if isinstance(value, uuid.UUID):
|
if isinstance(value, uuid.UUID):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
real.append(value)
|
real.append(value)
|
||||||
return wanted == real
|
return wanted == real
|
||||||
|
kwargs = {k: v for k, v in kwargs.items() if k.find('__in') == -1}
|
||||||
if len(kwargs) > 0:
|
if len(kwargs) > 0:
|
||||||
queryset = AssetUserQuerySet([i for i in self if filter_it(i)])
|
queryset = AssetUserQuerySet([i for i in self if filter_it(i)])
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -5,3 +5,4 @@ from .label import *
|
||||||
from .user import *
|
from .user import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .cmd_filter import *
|
from .cmd_filter import *
|
||||||
|
from .platform import *
|
||||||
|
|
|
@ -6,13 +6,13 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from orgs.mixins.forms import OrgModelForm
|
from orgs.mixins.forms import OrgModelForm
|
||||||
|
|
||||||
from ..models import Asset, Node
|
from ..models import Asset
|
||||||
from ..const import GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_HELP_TEXT
|
from ..const import GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_HELP_TEXT
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm', 'ProtocolForm',
|
'AssetCreateUpdateForm', 'AssetBulkUpdateForm', 'ProtocolForm',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,17 +27,27 @@ class ProtocolForm(forms.Form):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AssetCreateForm(OrgModelForm):
|
class AssetCreateUpdateForm(OrgModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
if self.data:
|
self.set_platform_to_name()
|
||||||
return
|
self.set_fields_queryset()
|
||||||
|
|
||||||
|
def set_fields_queryset(self):
|
||||||
nodes_field = self.fields['nodes']
|
nodes_field = self.fields['nodes']
|
||||||
|
nodes_choices = []
|
||||||
if self.instance:
|
if self.instance:
|
||||||
nodes_field.choices = [(n.id, n.full_value) for n in
|
nodes_choices = [
|
||||||
self.instance.nodes.all()]
|
(n.id, n.full_value) for n in
|
||||||
else:
|
self.instance.nodes.all()
|
||||||
nodes_field.choices = []
|
]
|
||||||
|
nodes_field.choices = nodes_choices
|
||||||
|
|
||||||
|
def set_platform_to_name(self):
|
||||||
|
platform_field = self.fields['platform']
|
||||||
|
platform_field.to_field_name = 'name'
|
||||||
|
if self.instance:
|
||||||
|
self.initial['platform'] = self.instance.platform.name
|
||||||
|
|
||||||
def add_nodes_initial(self, node):
|
def add_nodes_initial(self, node):
|
||||||
nodes_field = self.fields['nodes']
|
nodes_field = self.fields['nodes']
|
||||||
|
@ -49,7 +59,7 @@ class AssetCreateForm(OrgModelForm):
|
||||||
fields = [
|
fields = [
|
||||||
'hostname', 'ip', 'public_ip', 'protocols', 'comment',
|
'hostname', 'ip', 'public_ip', 'protocols', 'comment',
|
||||||
'nodes', 'is_active', 'admin_user', 'labels', 'platform',
|
'nodes', 'is_active', 'admin_user', 'labels', 'platform',
|
||||||
'domain',
|
'domain', 'number',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'nodes': forms.SelectMultiple(attrs={
|
'nodes': forms.SelectMultiple(attrs={
|
||||||
|
@ -64,52 +74,8 @@ class AssetCreateForm(OrgModelForm):
|
||||||
'domain': forms.Select(attrs={
|
'domain': forms.Select(attrs={
|
||||||
'class': 'select2', 'data-placeholder': _('Domain')
|
'class': 'select2', 'data-placeholder': _('Domain')
|
||||||
}),
|
}),
|
||||||
}
|
'platform': forms.Select(attrs={
|
||||||
labels = {
|
'class': 'select2', 'data-placeholder': _('Platform')
|
||||||
'nodes': _("Node"),
|
|
||||||
}
|
|
||||||
help_texts = {
|
|
||||||
'hostname': GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_HELP_TEXT,
|
|
||||||
'admin_user': _(
|
|
||||||
'root or other NOPASSWD sudo privilege user existed in asset,'
|
|
||||||
'If asset is windows or other set any one, more see admin user left menu'
|
|
||||||
),
|
|
||||||
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
|
|
||||||
'domain': _("If your have some network not connect with each other, you can set domain")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AssetUpdateForm(OrgModelForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
if self.data:
|
|
||||||
return
|
|
||||||
nodes_field = self.fields['nodes']
|
|
||||||
if self.instance:
|
|
||||||
nodes_field.choices = ((n.id, n.full_value) for n in
|
|
||||||
self.instance.nodes.all())
|
|
||||||
else:
|
|
||||||
nodes_field.choices = []
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Asset
|
|
||||||
fields = [
|
|
||||||
'hostname', 'ip', 'protocols', 'nodes', 'is_active', 'platform',
|
|
||||||
'public_ip', 'number', 'comment', 'admin_user', 'labels',
|
|
||||||
'domain',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'nodes': forms.SelectMultiple(attrs={
|
|
||||||
'class': 'nodes-select2', 'data-placeholder': _('Node')
|
|
||||||
}),
|
|
||||||
'admin_user': forms.Select(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Admin user')
|
|
||||||
}),
|
|
||||||
'labels': forms.SelectMultiple(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Label')
|
|
||||||
}),
|
|
||||||
'domain': forms.Select(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Domain')
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from ..models import Platform
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['PlatformForm', 'PlatformMetaForm']
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformMetaForm(forms.Form):
|
||||||
|
SECURITY_CHOICES = (
|
||||||
|
('rdp', "RDP"),
|
||||||
|
('nla', "NLA"),
|
||||||
|
('tls', 'TLS'),
|
||||||
|
('any', "Any"),
|
||||||
|
)
|
||||||
|
CONSOLE_CHOICES = (
|
||||||
|
(True, _('Yes')),
|
||||||
|
(False, _('No')),
|
||||||
|
)
|
||||||
|
security = forms.ChoiceField(
|
||||||
|
choices=SECURITY_CHOICES, initial='any', label=_("RDP security"),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
console = forms.ChoiceField(
|
||||||
|
choices=CONSOLE_CHOICES, initial=False, label=_("RDP console"),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Platform
|
||||||
|
fields = [
|
||||||
|
'name', 'base', 'comment',
|
||||||
|
]
|
||||||
|
labels = {
|
||||||
|
'base': _("Base platform")
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Generated by Django 2.2.7 on 2019-12-06 07:26
|
||||||
|
|
||||||
|
import common.fields.model
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def create_internal_platform(apps, schema_editor):
|
||||||
|
model = apps.get_model("assets", "Platform")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
type_platforms = (
|
||||||
|
('Linux', 'Linux', None),
|
||||||
|
('Unix', 'Unix', None),
|
||||||
|
('MacOS', 'MacOS', None),
|
||||||
|
('BSD', 'BSD', None),
|
||||||
|
('Windows', 'Windows', None),
|
||||||
|
('Windows2016', 'Windows', {'security': 'tls'}),
|
||||||
|
('Other', 'Other', None),
|
||||||
|
)
|
||||||
|
for name, base, meta in type_platforms:
|
||||||
|
model.objects.using(db_alias).create(
|
||||||
|
name=name, base=base, internal=True, meta=meta
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0043_auto_20191114_1111'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Platform',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.SlugField(allow_unicode=True, unique=True, verbose_name='Name')),
|
||||||
|
('base', models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=16, verbose_name='Base')),
|
||||||
|
('charset', models.CharField(choices=[('utf8', 'UTF-8'), ('gbk', 'GBK')], default='utf8', max_length=8, verbose_name='Charset')),
|
||||||
|
('meta', common.fields.model.JsonDictTextField(blank=True, null=True, verbose_name='Meta')),
|
||||||
|
('internal', models.BooleanField(default=False, verbose_name='Internal')),
|
||||||
|
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Platform'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
migrations.RunPython(create_internal_platform)
|
||||||
|
]
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Generated by Django 2.2.7 on 2019-12-06 08:07
|
||||||
|
|
||||||
|
import assets.models.asset
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_platform_to_asset_type(apps, schema_editor):
|
||||||
|
asset_model = apps.get_model("assets", "Asset")
|
||||||
|
platform_model = apps.get_model("assets", "Platform")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
platforms = platform_model.objects.using(db_alias).all()
|
||||||
|
platforms_map = {p.name: p for p in platforms}
|
||||||
|
for name, p in platforms_map.items():
|
||||||
|
asset_model.objects.using(db_alias)\
|
||||||
|
.filter(_platform=name)\
|
||||||
|
.update(platform=p)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0044_platform'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='asset',
|
||||||
|
old_name='platform',
|
||||||
|
new_name='_platform',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='platform',
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default=assets.models.asset.Platform.default,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name='assets', to='assets.Platform',
|
||||||
|
verbose_name='Platform'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(migrate_platform_to_asset_type),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='_platform',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.1.11 on 2019-12-18 09:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0045_auto_20191206_1607'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='protocol',
|
||||||
|
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet'), ('vnc', 'vnc'), ('mysql', 'mysql')], default='ssh', max_length=16, verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -11,10 +11,12 @@ from collections import OrderedDict
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .utils import Connectivity
|
from common.fields.model import JsonDictTextField
|
||||||
|
from common.utils import lazyproperty
|
||||||
from orgs.mixins.models import OrgModelMixin, OrgManager
|
from orgs.mixins.models import OrgModelMixin, OrgManager
|
||||||
|
from .utils import Connectivity
|
||||||
|
|
||||||
__all__ = ['Asset', 'ProtocolsMixin']
|
__all__ = ['Asset', 'ProtocolsMixin', 'Platform']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,6 +39,13 @@ def default_node():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class AssetManager(OrgManager):
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().annotate(
|
||||||
|
platform_base=models.F('platform__base')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AssetQuerySet(models.QuerySet):
|
class AssetQuerySet(models.QuerySet):
|
||||||
def active(self):
|
def active(self):
|
||||||
return self.filter(is_active=True)
|
return self.filter(is_active=True)
|
||||||
|
@ -119,6 +128,47 @@ class NodesRelationMixin:
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
|
class Platform(models.Model):
|
||||||
|
CHARSET_CHOICES = (
|
||||||
|
('utf8', 'UTF-8'),
|
||||||
|
('gbk', 'GBK'),
|
||||||
|
)
|
||||||
|
BASE_CHOICES = (
|
||||||
|
('Linux', 'Linux'),
|
||||||
|
('Unix', 'Unix'),
|
||||||
|
('MacOS', 'MacOS'),
|
||||||
|
('BSD', 'BSD'),
|
||||||
|
('Windows', 'Windows'),
|
||||||
|
('Other', 'Other'),
|
||||||
|
)
|
||||||
|
name = models.SlugField(verbose_name=_("Name"), unique=True, allow_unicode=True)
|
||||||
|
base = models.CharField(choices=BASE_CHOICES, max_length=16, default='Linux', verbose_name=_("Base"))
|
||||||
|
charset = models.CharField(default='utf8', choices=CHARSET_CHOICES, max_length=8, verbose_name=_("Charset"))
|
||||||
|
meta = JsonDictTextField(blank=True, null=True, verbose_name=_("Meta"))
|
||||||
|
internal = models.BooleanField(default=False, verbose_name=_("Internal"))
|
||||||
|
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls):
|
||||||
|
linux, created = cls.objects.get_or_create(
|
||||||
|
defaults={'name': 'Linux'}, name='Linux'
|
||||||
|
)
|
||||||
|
return linux.id
|
||||||
|
|
||||||
|
def is_windows(self):
|
||||||
|
return self.base.lower() in ('windows',)
|
||||||
|
|
||||||
|
def is_unixlike(self):
|
||||||
|
return self.base.lower() in ("linux", "unix", "macos", "bsd")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Platform")
|
||||||
|
# ordering = ('name',)
|
||||||
|
|
||||||
|
|
||||||
class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
||||||
# Important
|
# Important
|
||||||
PLATFORM_CHOICES = (
|
PLATFORM_CHOICES = (
|
||||||
|
@ -138,9 +188,8 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
||||||
choices=ProtocolsMixin.PROTOCOL_CHOICES,
|
choices=ProtocolsMixin.PROTOCOL_CHOICES,
|
||||||
verbose_name=_('Protocol'))
|
verbose_name=_('Protocol'))
|
||||||
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
||||||
|
|
||||||
protocols = models.CharField(max_length=128, default='ssh/22', blank=True, verbose_name=_("Protocols"))
|
protocols = models.CharField(max_length=128, default='ssh/22', blank=True, verbose_name=_("Protocols"))
|
||||||
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform'))
|
platform = models.ForeignKey(Platform, default=Platform.default, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets')
|
||||||
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL)
|
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL)
|
||||||
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
|
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
||||||
|
@ -175,7 +224,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
||||||
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
|
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
|
||||||
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
|
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
|
||||||
|
|
||||||
objects = OrgManager.from_queryset(AssetQuerySet)()
|
objects = AssetManager.from_queryset(AssetQuerySet)()
|
||||||
_connectivity = None
|
_connectivity = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -190,20 +239,18 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
||||||
return False, warning
|
return False, warning
|
||||||
return True, warning
|
return True, warning
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def platform_base(self):
|
||||||
|
return self.platform.base
|
||||||
|
|
||||||
def is_windows(self):
|
def is_windows(self):
|
||||||
if self.platform in ("Windows", "Windows2016"):
|
return self.platform.is_windows()
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_unixlike(self):
|
def is_unixlike(self):
|
||||||
if self.platform not in ("Windows", "Windows2016", "Other"):
|
return self.platform.is_unixlike()
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_support_ansible(self):
|
def is_support_ansible(self):
|
||||||
return self.has_protocol('ssh') and self.platform not in ("Other",)
|
return self.has_protocol('ssh') and self.platform_base not in ("Other",)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cpu_info(self):
|
def cpu_info(self):
|
||||||
|
@ -264,9 +311,9 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
||||||
def as_tree_node(self, parent_node):
|
def as_tree_node(self, parent_node):
|
||||||
from common.tree import TreeNode
|
from common.tree import TreeNode
|
||||||
icon_skin = 'file'
|
icon_skin = 'file'
|
||||||
if self.platform.lower() == 'windows':
|
if self.platform_base.lower() == 'windows':
|
||||||
icon_skin = 'windows'
|
icon_skin = 'windows'
|
||||||
elif self.platform.lower() == 'linux':
|
elif self.platform_base.lower() == 'linux':
|
||||||
icon_skin = 'linux'
|
icon_skin = 'linux'
|
||||||
data = {
|
data = {
|
||||||
'id': str(self.id),
|
'id': str(self.id),
|
||||||
|
@ -283,7 +330,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
||||||
'hostname': self.hostname,
|
'hostname': self.hostname,
|
||||||
'ip': self.ip,
|
'ip': self.ip,
|
||||||
'protocols': self.protocols_as_list,
|
'protocols': self.protocols_as_list,
|
||||||
'platform': self.platform,
|
'platform': self.platform_base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,13 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from common.utils import (
|
from common.utils import (
|
||||||
get_signer, ssh_key_string_to_obj, ssh_key_gen, get_logger
|
signer, ssh_key_string_to_obj, ssh_key_gen, get_logger
|
||||||
)
|
)
|
||||||
from common.validators import alphanumeric
|
from common.validators import alphanumeric
|
||||||
from common import fields
|
from common import fields
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from .utils import private_key_validator, Connectivity
|
from .utils import private_key_validator, Connectivity
|
||||||
|
|
||||||
signer = get_signer()
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
@ -41,6 +40,7 @@ class AssetUser(OrgModelMixin):
|
||||||
ASSET_USER_CACHE_TIME = 3600 * 24
|
ASSET_USER_CACHE_TIME = 3600 * 24
|
||||||
|
|
||||||
_prefer = "system_user"
|
_prefer = "system_user"
|
||||||
|
_assets_amount = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def private_key_obj(self):
|
def private_key_obj(self):
|
||||||
|
@ -143,6 +143,8 @@ class AssetUser(OrgModelMixin):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assets_amount(self):
|
def assets_amount(self):
|
||||||
|
if self._assets_amount is not None:
|
||||||
|
return self._assets_amount
|
||||||
cache_key = self.ASSETS_AMOUNT_CACHE_KEY.format(self.id)
|
cache_key = self.ASSETS_AMOUNT_CACHE_KEY.format(self.id)
|
||||||
cached = cache.get(cache_key)
|
cached = cache.get(cache_key)
|
||||||
if not cached:
|
if not cached:
|
||||||
|
|
|
@ -10,14 +10,13 @@ from django.db.models import Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
|
|
||||||
from common.utils import get_signer
|
from common.utils import signer
|
||||||
from .base import AssetUser
|
from .base import AssetUser
|
||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['AdminUser', 'SystemUser']
|
__all__ = ['AdminUser', 'SystemUser']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
signer = get_signer()
|
|
||||||
|
|
||||||
|
|
||||||
class AdminUser(AssetUser):
|
class AdminUser(AssetUser):
|
||||||
|
@ -93,11 +92,13 @@ class SystemUser(AssetUser):
|
||||||
PROTOCOL_RDP = 'rdp'
|
PROTOCOL_RDP = 'rdp'
|
||||||
PROTOCOL_TELNET = 'telnet'
|
PROTOCOL_TELNET = 'telnet'
|
||||||
PROTOCOL_VNC = 'vnc'
|
PROTOCOL_VNC = 'vnc'
|
||||||
|
PROTOCOL_MYSQL = 'mysql'
|
||||||
PROTOCOL_CHOICES = (
|
PROTOCOL_CHOICES = (
|
||||||
(PROTOCOL_SSH, 'ssh'),
|
(PROTOCOL_SSH, 'ssh'),
|
||||||
(PROTOCOL_RDP, 'rdp'),
|
(PROTOCOL_RDP, 'rdp'),
|
||||||
(PROTOCOL_TELNET, 'telnet'),
|
(PROTOCOL_TELNET, 'telnet'),
|
||||||
(PROTOCOL_VNC, 'vnc'),
|
(PROTOCOL_VNC, 'vnc'),
|
||||||
|
(PROTOCOL_MYSQL, 'mysql'),
|
||||||
)
|
)
|
||||||
|
|
||||||
LOGIN_AUTO = 'auto'
|
LOGIN_AUTO = 'auto'
|
||||||
|
@ -134,6 +135,18 @@ class SystemUser(AssetUser):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_need_cmd_filter(self):
|
||||||
|
return self.protocol not in [self.PROTOCOL_RDP, self.PROTOCOL_MYSQL]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_need_test_asset_connective(self):
|
||||||
|
return self.protocol not in [self.PROTOCOL_MYSQL]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def can_perm_to_asset(self):
|
||||||
|
return self.protocol not in [self.PROTOCOL_MYSQL]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cmd_filter_rules(self):
|
def cmd_filter_rules(self):
|
||||||
from .cmd_filter import CommandFilterRule
|
from .cmd_filter import CommandFilterRule
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from common.serializers import AdaptedBulkListSerializer
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
from ..models import Asset, Node, Label
|
from ..models import Asset, Node, Label, Platform
|
||||||
from ..const import (
|
from ..const import (
|
||||||
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN,
|
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN,
|
||||||
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_ERROR_MSG
|
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_ERROR_MSG
|
||||||
|
@ -16,7 +16,8 @@ from .base import ConnectivitySerializer
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetSerializer', 'AssetSimpleSerializer',
|
'AssetSerializer', 'AssetSimpleSerializer',
|
||||||
'ProtocolsField',
|
'ProtocolsField', 'PlatformSerializer',
|
||||||
|
'AssetDetailSerializer',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,6 +66,9 @@ class ProtocolsField(serializers.ListField):
|
||||||
|
|
||||||
|
|
||||||
class AssetSerializer(BulkOrgResourceModelSerializer):
|
class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
platform = serializers.SlugRelatedField(
|
||||||
|
slug_field='name', queryset=Platform.objects.all(), label=_("Platform")
|
||||||
|
)
|
||||||
protocols = ProtocolsField(label=_('Protocols'), required=False)
|
protocols = ProtocolsField(label=_('Protocols'), required=False)
|
||||||
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
|
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
|
||||||
|
|
||||||
|
@ -111,7 +115,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||||
queryset = queryset.prefetch_related(
|
queryset = queryset.prefetch_related(
|
||||||
Prefetch('nodes', queryset=Node.objects.all().only('id')),
|
Prefetch('nodes', queryset=Node.objects.all().only('id')),
|
||||||
Prefetch('labels', queryset=Label.objects.all().only('id')),
|
Prefetch('labels', queryset=Label.objects.all().only('id')),
|
||||||
).select_related('admin_user', 'domain')
|
).select_related('admin_user', 'domain', 'platform')
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def compatible_with_old_protocol(self, validated_data):
|
def compatible_with_old_protocol(self, validated_data):
|
||||||
|
@ -139,6 +143,21 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||||
return super().update(instance, validated_data)
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformSerializer(serializers.ModelSerializer):
|
||||||
|
meta = serializers.DictField(required=False, allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Platform
|
||||||
|
fields = [
|
||||||
|
'id', 'name', 'base', 'charset',
|
||||||
|
'internal', 'meta', 'comment'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AssetDetailSerializer(AssetSerializer):
|
||||||
|
platform = PlatformSerializer(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class AssetSimpleSerializer(serializers.ModelSerializer):
|
class AssetSimpleSerializer(serializers.ModelSerializer):
|
||||||
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
|
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@ from rest_framework import serializers
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.serializers import AdaptedBulkListSerializer
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
|
from common.mixins.serializers import BulkSerializerMixin
|
||||||
from common.utils import ssh_pubkey_gen
|
from common.utils import ssh_pubkey_gen
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
|
from assets.models import Node
|
||||||
from ..models import SystemUser
|
from ..models import SystemUser
|
||||||
from ..const import (
|
from ..const import (
|
||||||
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN,
|
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN,
|
||||||
|
@ -13,6 +15,12 @@ from ..const import (
|
||||||
)
|
)
|
||||||
from .base import AuthSerializer, AuthSerializerMixin
|
from .base import AuthSerializer, AuthSerializerMixin
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'SystemUserSerializer', 'SystemUserAuthSerializer',
|
||||||
|
'SystemUserSimpleSerializer', 'SystemUserAssetRelationSerializer',
|
||||||
|
'SystemUserNodeRelationSerializer',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
"""
|
"""
|
||||||
|
@ -95,8 +103,12 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
username = attrs.get("username", "manual")
|
username = attrs.get("username", "manual")
|
||||||
|
auto_gen_key = attrs.pop("auto_generate_key", False)
|
||||||
protocol = attrs.get("protocol")
|
protocol = attrs.get("protocol")
|
||||||
auto_gen_key = attrs.get("auto_generate_key", False)
|
|
||||||
|
if protocol not in [SystemUser.PROTOCOL_RDP, SystemUser.PROTOCOL_SSH]:
|
||||||
|
return attrs
|
||||||
|
|
||||||
if auto_gen_key:
|
if auto_gen_key:
|
||||||
password = SystemUser.gen_password()
|
password = SystemUser.gen_password()
|
||||||
attrs["password"] = password
|
attrs["password"] = password
|
||||||
|
@ -111,7 +123,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
public_key = ssh_pubkey_gen(private_key, password=password,
|
public_key = ssh_pubkey_gen(private_key, password=password,
|
||||||
username=username)
|
username=username)
|
||||||
attrs["public_key"] = public_key
|
attrs["public_key"] = public_key
|
||||||
attrs.pop("auto_generate_key", None)
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -143,4 +154,43 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
||||||
fields = ('id', 'name', 'username')
|
fields = ('id', 'name', 'username')
|
||||||
|
|
||||||
|
|
||||||
|
class RelationMixin(BulkSerializerMixin, serializers.Serializer):
|
||||||
|
systemuser_display = serializers.ReadOnlyField()
|
||||||
|
|
||||||
|
def get_field_names(self, declared_fields, info):
|
||||||
|
fields = super().get_field_names(declared_fields, info)
|
||||||
|
fields.extend(['systemuser', "systemuser_display"])
|
||||||
|
return fields
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserAssetRelationSerializer(RelationMixin, serializers.ModelSerializer):
|
||||||
|
asset_display = serializers.ReadOnlyField()
|
||||||
|
|
||||||
|
class Meta(RelationMixin.Meta):
|
||||||
|
model = SystemUser.assets.through
|
||||||
|
fields = [
|
||||||
|
'id', "asset", "asset_display",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserNodeRelationSerializer(RelationMixin, serializers.ModelSerializer):
|
||||||
|
node_display = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta(RelationMixin.Meta):
|
||||||
|
model = SystemUser.nodes.through
|
||||||
|
fields = [
|
||||||
|
'id', 'node', "node_display",
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.tree = Node.tree()
|
||||||
|
|
||||||
|
def get_node_display(self, obj):
|
||||||
|
if hasattr(obj, 'node_key'):
|
||||||
|
return self.tree.get_node_full_tag(obj.node_key)
|
||||||
|
else:
|
||||||
|
return obj.node.full_value
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{% extends '_import_modal.html' %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block modal_title%}{% trans "Import admin user" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block import_modal_download_template_url %}{% url "api-assets:admin-user-list" %}{% endblock %}
|
|
|
@ -1,4 +0,0 @@
|
||||||
{% extends '_update_modal.html' %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block modal_title%}{% trans "Update admin user" %}{% endblock %}
|
|
|
@ -1,6 +0,0 @@
|
||||||
{% extends '_import_modal.html' %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block modal_title%}{% trans "Import assets" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block import_modal_download_template_url %}{% url "api-assets:asset-list" %}{% endblock %}
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
<div class="wrapper wrapper-content">
|
<div class="wrapper wrapper-content">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3" id="split-left" style="padding-left: 3px;overflow: auto;max-height: 500px">
|
<div class="col-sm-3" id="split-left" style="padding-left: 3px;overflow: auto;max-height: 500px">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
|
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
|
||||||
<div class="file-manager ">
|
<div class="file-manager ">
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-9 animated fadeInRight" id="split-right">
|
<div class="col-sm-9 animated fadeInRight" id="split-right">
|
||||||
<div class="mail-box-header">
|
<div class="mail-box-header">
|
||||||
<table class="table table-striped table-bordered table-hover " id="asset_list_modal_table" style="width: 100%">
|
<table class="table table-striped table-bordered table-hover " id="asset_list_modal_table" style="width: 100%">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -190,6 +190,16 @@ function setAssetModalOptions(options) {
|
||||||
assetModalOption = options;
|
assetModalOption = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initAssetTreeModel(selector) {
|
||||||
|
$(selector).parent().find(".select2-selection").on('click', function (e) {
|
||||||
|
if ($(e.target).attr('class') !== 'select2-selection__choice__remove'){
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
$("#asset_list_modal").modal();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
{% extends '_update_modal.html' %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block modal_title%}{% trans "Update assets" %}{% endblock %}
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
{% extends '_modal.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
<style>
|
||||||
|
.modal-body {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% block modal_id %}node_detail_modal{% endblock %}
|
||||||
|
|
||||||
|
{% block modal_title %}{% trans "Node detail" %}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block modal_body %}
|
||||||
|
<form class="form-horizontal" action="" style="padding-top: 20px">
|
||||||
|
<div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="" class="col-sm-2 control-label">{% trans 'ID' %}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<p class="form-control-static" id="id_node_detail_id_view"></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2" style="padding-left: 2px">
|
||||||
|
<a class="btn btn-white btn-sm btn-node-detail-copy-id"><i class="fa fa-copy"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="" class="col-sm-2 control-label">{% trans 'Name' %}</label>
|
||||||
|
<div class="col-sm-8" >
|
||||||
|
<p class="form-control-static" id="id_node_detail_name_view"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="" class="col-sm-2 control-label">{% trans 'Full name' %}</label>
|
||||||
|
<div class="col-sm-8" >
|
||||||
|
<p class="form-control-static" id="id_node_detail_full_name_view"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="" class="col-sm-2 control-label">{% trans 'Key' %}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<p class="form-control-static" id="id_node_detail_key_view"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<script src="{% static "js/plugins/clipboard/clipboard.min.js" %}"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function initClipboard() {
|
||||||
|
var clipboard = new Clipboard('.btn-node-detail-copy-id', {
|
||||||
|
text: function (trigger) {
|
||||||
|
return $("#id_node_detail_id_view").html()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clipboard.on("success", function (e) {
|
||||||
|
toastr.success("{% trans "Copy success" %}")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
initClipboard();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal_button %}
|
||||||
|
<button data-dismiss="modal" class="btn btn-white" type="button">{% trans "Close" %}</button>
|
||||||
|
{% endblock %}
|
|
@ -216,8 +216,9 @@ function OnRightClick(event, treeId, treeNode) {
|
||||||
|
|
||||||
function showRMenu(type, x, y) {
|
function showRMenu(type, x, y) {
|
||||||
var offset = $("#tree-node-id").offset();
|
var offset = $("#tree-node-id").offset();
|
||||||
|
var scrollTop = document.querySelector('.treebox').scrollTop;
|
||||||
x -= offset.left;
|
x -= offset.left;
|
||||||
y -= offset.top;
|
y -= offset.top + scrollTop;
|
||||||
x += document.body.scrollLeft;
|
x += document.body.scrollLeft;
|
||||||
y += document.body.scrollTop + document.documentElement.scrollTop;
|
y += document.body.scrollTop + document.documentElement.scrollTop;
|
||||||
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
|
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
@ -99,7 +95,7 @@ function autoLoginModeProtocol() {
|
||||||
// 协议+自动登录模式字段控制
|
// 协议+自动登录模式字段控制
|
||||||
$('#auth_title_id').removeClass('hidden');
|
$('#auth_title_id').removeClass('hidden');
|
||||||
var protocol = $(protocol_id + " option:selected").text();
|
var protocol = $(protocol_id + " option:selected").text();
|
||||||
if (protocol === 'rdp') {
|
if (['rdp'].indexOf(protocol) !== -1) {
|
||||||
authFieldsDisplay();
|
authFieldsDisplay();
|
||||||
$(auto_generate_key).closest('.form-group').removeClass('hidden');
|
$(auto_generate_key).closest('.form-group').removeClass('hidden');
|
||||||
$(private_key_id).closest('.form-group').addClass('hidden');
|
$(private_key_id).closest('.form-group').addClass('hidden');
|
||||||
|
@ -109,7 +105,7 @@ function autoLoginModeProtocol() {
|
||||||
$(sudo_id).closest('.form-group').addClass('hidden');
|
$(sudo_id).closest('.form-group').addClass('hidden');
|
||||||
$(shell_id).closest('.form-group').addClass('hidden');
|
$(shell_id).closest('.form-group').addClass('hidden');
|
||||||
}
|
}
|
||||||
else if (protocol === 'vnc') {
|
else if (['vnc', 'mysql'].indexOf(protocol) !== -1) {
|
||||||
$('.auth-fields').removeClass('hidden');
|
$('.auth-fields').removeClass('hidden');
|
||||||
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
||||||
$(private_key_id).closest('.form-group').addClass('hidden');
|
$(private_key_id).closest('.form-group').addClass('hidden');
|
||||||
|
@ -145,7 +141,7 @@ function manualLoginModeProtocol() {
|
||||||
// 协议+手动登录模式字段控制
|
// 协议+手动登录模式字段控制
|
||||||
$('#auth_title_id').addClass('hidden');
|
$('#auth_title_id').addClass('hidden');
|
||||||
var protocol = $(protocol_id + " option:selected").text();
|
var protocol = $(protocol_id + " option:selected").text();
|
||||||
if (protocol === 'rdp') {
|
if (['rdp'].indexOf(protocol) !== -1) {
|
||||||
$('.auth-fields').addClass('hidden');
|
$('.auth-fields').addClass('hidden');
|
||||||
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
||||||
$(password_id).closest('.form-group').addClass('hidden');
|
$(password_id).closest('.form-group').addClass('hidden');
|
||||||
|
@ -155,7 +151,7 @@ function manualLoginModeProtocol() {
|
||||||
$(sudo_id).closest('.form-group').addClass('hidden');
|
$(sudo_id).closest('.form-group').addClass('hidden');
|
||||||
$(shell_id).closest('.form-group').addClass('hidden');
|
$(shell_id).closest('.form-group').addClass('hidden');
|
||||||
}
|
}
|
||||||
else if (protocol === 'vnc') {
|
else if (['vnc', 'mysql'].indexOf(protocol) !== -1) {
|
||||||
$('.auth-fields').addClass('hidden');
|
$('.auth-fields').addClass('hidden');
|
||||||
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
||||||
$(password_id).closest('.form-group').addClass('hidden');
|
$(password_id).closest('.form-group').addClass('hidden');
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{% extends '_import_modal.html' %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block modal_title%}{% trans "Import system user" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block import_modal_download_template_url %}{% url "api-assets:system-user-list" %}{% endblock %}
|
|
|
@ -1,4 +0,0 @@
|
||||||
{% extends '_update_modal.html' %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block modal_title%}{% trans "Update system user" %}{% endblock %}
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -5,28 +5,7 @@
|
||||||
{% trans 'Jumpserver users of the system using the user to `push system user`, `get assets hardware information`, etc. '%}
|
{% trans 'Jumpserver users of the system using the user to `push system user`, `get assets hardware information`, etc. '%}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block table_search %}
|
{% block table_search %}
|
||||||
<div class="" style="float: right">
|
{% include '_csv_import_export.html' %}
|
||||||
<div class=" btn-group">
|
|
||||||
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a class=" btn_export" tabindex="0">
|
|
||||||
<span>{% trans "Export" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
|
|
||||||
<span>{% trans "Import" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
|
|
||||||
<span>{% trans "Update" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block table_container %}
|
{% block table_container %}
|
||||||
|
@ -42,9 +21,6 @@
|
||||||
<th class="text-center">{% trans 'Name' %}</th>
|
<th class="text-center">{% trans 'Name' %}</th>
|
||||||
<th class="text-center">{% trans 'Username' %}</th>
|
<th class="text-center">{% trans 'Username' %}</th>
|
||||||
<th class="text-center">{% trans 'Asset' %}</th>
|
<th class="text-center">{% trans 'Asset' %}</th>
|
||||||
{# <th class="text-center">{% trans 'Reachable' %}</th>#}
|
|
||||||
{# <th class="text-center">{% trans 'Unreachable' %}</th>#}
|
|
||||||
{# <th class="text-center">{% trans 'Ratio' %}</th>#}
|
|
||||||
<th class="text-center">{% trans 'Comment' %}</th>
|
<th class="text-center">{% trans 'Comment' %}</th>
|
||||||
<th class="text-center">{% trans 'Action' %}</th>
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -52,8 +28,6 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% include 'assets/_admin_user_import_modal.html' %}
|
|
||||||
{% include 'assets/_admin_user_update_modal.html' %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content_bottom_left %}{% endblock %}
|
{% block content_bottom_left %}{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
|
@ -77,17 +51,16 @@ function initTable() {
|
||||||
columns: [
|
columns: [
|
||||||
{data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount", orderable: false},
|
{data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount", orderable: false},
|
||||||
{#{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"},#}
|
{#{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"},#}
|
||||||
{data: "comment"}, {data: "id", orderable: false, width: "100px"}
|
{data: "comment"}, {data: "id", orderable: false, width: "120px"}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
admin_user_table = jumpserver.initServerSideDataTable(options);
|
return jumpserver.initServerSideDataTable(options);
|
||||||
return admin_user_table
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
admin_user_table = initTable();
|
||||||
|
initCsvImportExport(admin_user_table, "{% trans "Admin user" %}")
|
||||||
})
|
})
|
||||||
|
|
||||||
.on('click', '.btn_admin_user_delete', function () {
|
.on('click', '.btn_admin_user_delete', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var $data_table = $("#admin_user_list_table").DataTable();
|
var $data_table = $("#admin_user_list_table").DataTable();
|
||||||
|
@ -100,69 +73,5 @@ $(document).ready(function(){
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
})
|
})
|
||||||
.on('click', '.btn_export', function(){
|
|
||||||
var admin_users = admin_user_table.selected;
|
|
||||||
var data = {
|
|
||||||
'resources': admin_users
|
|
||||||
};
|
|
||||||
var search = $("input[type='search']").val();
|
|
||||||
var props = {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
success_url: "{% url 'api-assets:admin-user-list' %}",
|
|
||||||
format: "csv",
|
|
||||||
params: {
|
|
||||||
search: search
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APIExportData(props);
|
|
||||||
}).on('click', '#btn_import_confirm',function () {
|
|
||||||
var url = "{% url 'api-assets:admin-user-list' %}";
|
|
||||||
var file = document.getElementById('id_file').files[0];
|
|
||||||
if(!file){
|
|
||||||
toastr.error("{% trans "Please select file" %}");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var data_table = $('#admin_user_list_table').DataTable();
|
|
||||||
APIImportData({
|
|
||||||
url: url,
|
|
||||||
method: "POST",
|
|
||||||
body: file,
|
|
||||||
data_table: data_table
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on('click', '#download_update_template', function () {
|
|
||||||
var admin_users = admin_user_table.selected;
|
|
||||||
var data = {
|
|
||||||
'resources': admin_users
|
|
||||||
};
|
|
||||||
var search = $("input[type='search']").val();
|
|
||||||
var props = {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
success_url: "{% url 'api-assets:admin-user-list' %}?format=csv&template=update",
|
|
||||||
format: 'csv',
|
|
||||||
params: {
|
|
||||||
search: search
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APIExportData(props);
|
|
||||||
})
|
|
||||||
.on('click', '#btn_update_confirm', function () {
|
|
||||||
var file = document.getElementById('update_file').files[0];
|
|
||||||
if(!file){
|
|
||||||
toastr.error("{% trans "Please select file" %}");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var url = "{% url 'api-assets:admin-user-list' %}";
|
|
||||||
var data_table = $('#admin_user_list_table').DataTable();
|
|
||||||
|
|
||||||
APIImportData({
|
|
||||||
url: url,
|
|
||||||
method: "PUT",
|
|
||||||
body: file,
|
|
||||||
data_table: data_table
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -32,13 +32,7 @@
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2();
|
$('.select2').select2();
|
||||||
$("#id_assets").parent().find(".select2-selection").on('click', function (e) {
|
initAssetTreeModel("#id_assets");
|
||||||
if ($(e.target).attr('class') !== 'select2-selection__choice__remove'){
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
$("#asset_list_modal").modal();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).on('click', '.field-tag', function() {
|
}).on('click', '.field-tag', function() {
|
||||||
changeField(this);
|
changeField(this);
|
||||||
}).on('click', '#change_all', function () {
|
}).on('click', '#change_all', function () {
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
{% block custom_head_css_js %}
|
||||||
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
|
||||||
<link href='{% static "css/plugins/sweetalert/sweetalert.css" %}' rel="stylesheet">
|
<link href='{% static "css/plugins/sweetalert/sweetalert.css" %}' rel="stylesheet">
|
||||||
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
|
||||||
<script src='{% static "js/plugins/sweetalert/sweetalert.min.js" %}'></script>
|
<script src='{% static "js/plugins/sweetalert/sweetalert.min.js" %}'></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,132 +1,54 @@
|
||||||
{% extends 'base.html' %}
|
{% extends '_base_asset_tree_list.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block help_message %}
|
{% block help_message %}
|
||||||
{# <div class="alert alert-info help-message">#}
|
|
||||||
{# <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>#}
|
|
||||||
{# 左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产#}
|
|
||||||
{% trans 'The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node' %}
|
{% trans 'The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node' %}
|
||||||
{# </div>#}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
{% block table_container %}
|
||||||
{# <link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">#}
|
<div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
|
||||||
{# <script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>#}
|
{% include '_csv_import_export.html' %}
|
||||||
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
<div class="btn-group" style="float: right">
|
||||||
<style type="text/css">
|
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
|
||||||
div#rMenu {
|
<ul class="dropdown-menu labels">
|
||||||
position:absolute;
|
{% for label in labels %}
|
||||||
visibility:hidden;
|
<li><a style="font-weight: bolder">{{ label.name }}#{{ label.value }}</a></li>
|
||||||
text-align: left;
|
{% endfor %}
|
||||||
{#top: 100%;#}
|
</ul>
|
||||||
top: 0;
|
</div>
|
||||||
left: 0;
|
<table class="table table-striped table-bordered table-hover " id="asset_list_table" style="width: 100%">
|
||||||
z-index: 1000;
|
<thead>
|
||||||
{#float: left;#}
|
<tr>
|
||||||
padding: 0 0;
|
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
|
||||||
margin: 2px 0 0;
|
<th class="text-center">{% trans 'Hostname' %}</th>
|
||||||
list-style: none;
|
<th class="text-center">{% trans 'IP' %}</th>
|
||||||
background-clip: padding-box;
|
<th class="text-center">{% trans 'Hardware' %}</th>
|
||||||
}
|
<th class="text-center">{% trans 'Reachable' %}</th>
|
||||||
.dataTables_wrapper .dataTables_processing {
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
opacity: .9;
|
</tr>
|
||||||
border: none;
|
</thead>
|
||||||
}
|
<tbody>
|
||||||
div#rMenu li{
|
</tbody>
|
||||||
margin: 1px 0;
|
</table>
|
||||||
cursor: pointer;
|
<div id="actions" class="hide">
|
||||||
list-style: none outside none;
|
<div class="input-group">
|
||||||
}
|
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
|
||||||
.dropdown a:hover {
|
<option value="delete">{% trans 'Delete selected' %}</option>
|
||||||
background-color: #f1f1f1
|
<option value="update">{% trans 'Update selected' %}</option>
|
||||||
}
|
<option value="remove">{% trans 'Remove from this node' %}</option>
|
||||||
</style>
|
<option value="deactive">{% trans 'Deactive selected' %}</option>
|
||||||
|
<option value="active">{% trans 'Active selected' %}</option>
|
||||||
{% endblock %}
|
</select>
|
||||||
|
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
||||||
{% block content %}
|
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
|
||||||
<div class="wrapper wrapper-content">
|
{% trans 'Submit' %}
|
||||||
<div class="row">
|
</button>
|
||||||
<div class="col-lg-3" id="split-left" style="padding-left: 3px;padding-right: 0">
|
|
||||||
{% include 'assets/_node_tree.html' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-9 animated fadeInRight" id="split-right">
|
|
||||||
<div class="tree-toggle" style="z-index: 9999">
|
|
||||||
<div class="btn btn-sm btn-primary tree-toggle-btn">
|
|
||||||
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mail-box-header">
|
|
||||||
<div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
|
|
||||||
<div class="" style="float: right">
|
|
||||||
<div class=" btn-group">
|
|
||||||
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a class=" btn_export" tabindex="0">
|
|
||||||
<span>{% trans "Export" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
|
|
||||||
<span>{% trans "Import" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
|
|
||||||
<span>{% trans "Update" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group" style="float: right">
|
|
||||||
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
|
|
||||||
<ul class="dropdown-menu labels">
|
|
||||||
{% for label in labels %}
|
|
||||||
<li><a style="font-weight: bolder">{{ label.name }}#{{ label.value }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<table class="table table-striped table-bordered table-hover " id="asset_list_table" style="width: 100%">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
|
|
||||||
<th class="text-center">{% trans 'Hostname' %}</th>
|
|
||||||
<th class="text-center">{% trans 'IP' %}</th>
|
|
||||||
<th class="text-center">{% trans 'Hardware' %}</th>
|
|
||||||
<th class="text-center">{% trans 'Reachable' %}</th>
|
|
||||||
<th class="text-center">{% trans 'Action' %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div id="actions" class="hide">
|
|
||||||
<div class="input-group">
|
|
||||||
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
|
|
||||||
<option value="delete">{% trans 'Delete selected' %}</option>
|
|
||||||
<option value="update">{% trans 'Update selected' %}</option>
|
|
||||||
<option value="remove">{% trans 'Remove from this node' %}</option>
|
|
||||||
<option value="deactive">{% trans 'Deactive selected' %}</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">
|
|
||||||
{% trans 'Submit' %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% include 'assets/_asset_update_modal.html' %}
|
|
||||||
{% include 'assets/_asset_import_modal.html' %}
|
|
||||||
{% include 'assets/_asset_list_modal.html' %}
|
{% include 'assets/_asset_list_modal.html' %}
|
||||||
|
{% include 'assets/_node_detail_modal.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
|
@ -177,7 +99,7 @@ function initTable() {
|
||||||
data: "connectivity",
|
data: "connectivity",
|
||||||
orderable: false,
|
orderable: false,
|
||||||
width: '60px'
|
width: '60px'
|
||||||
}, {data: "id", orderable: false, width: "100px"}
|
}, {data: "id", orderable: false, width: "120px"}
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
|
@ -198,26 +120,12 @@ function initTree() {
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li id="show_current_asset" class="btn-show-current-asset" style="display: none;" tabindex="-1"><a><i class="fa fa-hand-o-up"></i> {% trans 'Display only current node assets' %}</a></li>
|
<li id="show_current_asset" class="btn-show-current-asset" style="display: none;" tabindex="-1"><a><i class="fa fa-hand-o-up"></i> {% trans 'Display only current node assets' %}</a></li>
|
||||||
<li id="show_all_asset" class="btn-show-all-asset" style="display: none;" tabindex="-1"><a><i class="fa fa-th"></i> {% trans 'Displays all child node assets' %}</a></li>
|
<li id="show_all_asset" class="btn-show-all-asset" style="display: none;" tabindex="-1"><a><i class="fa fa-th"></i> {% trans 'Displays all child node assets' %}</a></li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li id="menu_node_detail" class="btn-node-detail" tabindex="-1"><a><i class="fa fa-info-circle"></i> {% trans 'Node detail' %}</a></li>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function toggle() {
|
|
||||||
if (show === 0) {
|
|
||||||
$("#split-left").hide(500, function () {
|
|
||||||
$("#split-right").attr("class", "col-lg-12");
|
|
||||||
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
|
|
||||||
show = 1;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$("#split-right").attr("class", "col-lg-9");
|
|
||||||
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
|
|
||||||
$("#split-left").show(500);
|
|
||||||
show = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onNodeSelected(event, treeNode) {
|
function onNodeSelected(event, treeNode) {
|
||||||
current_node = treeNode;
|
current_node = treeNode;
|
||||||
current_node_id = treeNode.meta.node.id;
|
current_node_id = treeNode.meta.node.id;
|
||||||
|
@ -258,7 +166,8 @@ function onAssetModalConfirmAddAssetToNode(table) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
asset_table = initTable();
|
||||||
|
initCsvImportExport(asset_table, "{% trans "Asset" %}");
|
||||||
initTree();
|
initTree();
|
||||||
|
|
||||||
if(getCookie('show_current_asset') === '1'){
|
if(getCookie('show_current_asset') === '1'){
|
||||||
|
@ -279,81 +188,6 @@ $(document).ready(function(){
|
||||||
$("#asset_list_table_filter input").val(val);
|
$("#asset_list_table_filter input").val(val);
|
||||||
asset_table.search(val).draw();
|
asset_table.search(val).draw();
|
||||||
})
|
})
|
||||||
.on('click', '.btn_export', function () {
|
|
||||||
var assets = asset_table.selected;
|
|
||||||
var data = {
|
|
||||||
'resources': assets
|
|
||||||
};
|
|
||||||
var search = $("input[type='search']").val();
|
|
||||||
var props = {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
success_url: "{% url 'api-assets:asset-list' %}",
|
|
||||||
format: 'csv',
|
|
||||||
params: {
|
|
||||||
search: search,
|
|
||||||
node_id: current_node_id || '',
|
|
||||||
show_current_asset: getCookie('show_current_asset')
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APIExportData(props);
|
|
||||||
})
|
|
||||||
.on('click', '#btn_import_confirm', function () {
|
|
||||||
var file = document.getElementById('id_file').files[0];
|
|
||||||
if(!file){
|
|
||||||
toastr.error("{% trans "Please select file" %}");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var url = "{% url 'api-assets:asset-list' %}";
|
|
||||||
if (current_node_id){
|
|
||||||
url = setUrlParam(url, 'node_id', current_node_id);
|
|
||||||
}
|
|
||||||
var data_table = $('#asset_list_table').DataTable();
|
|
||||||
|
|
||||||
APIImportData({
|
|
||||||
url: url,
|
|
||||||
method: "POST",
|
|
||||||
body: file,
|
|
||||||
data_table: data_table
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on('click', '#download_update_template', function () {
|
|
||||||
var assets = asset_table.selected;
|
|
||||||
var data = {
|
|
||||||
'resources': assets
|
|
||||||
};
|
|
||||||
var search = $("input[type='search']").val();
|
|
||||||
var props = {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
success_url: "{% url 'api-assets:asset-list' %}?format=csv&template=update",
|
|
||||||
format: 'csv',
|
|
||||||
params: {
|
|
||||||
search: search,
|
|
||||||
node_id: current_node_id || ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APIExportData(props);
|
|
||||||
})
|
|
||||||
.on('click', '#btn_update_confirm', function () {
|
|
||||||
var file = document.getElementById('update_file').files[0];
|
|
||||||
if(!file){
|
|
||||||
toastr.error("{% trans "Please select file" %}");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var url = "{% url 'api-assets:asset-list' %}";
|
|
||||||
if (current_node_id){
|
|
||||||
url = setUrlParam(url, 'node_id', current_node_id);
|
|
||||||
}
|
|
||||||
var data_table = $('#asset_list_table').DataTable();
|
|
||||||
|
|
||||||
APIImportData({
|
|
||||||
url: url,
|
|
||||||
method: "PUT",
|
|
||||||
body: file,
|
|
||||||
data_table: data_table
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on('click', '.btn-create-asset', function () {
|
.on('click', '.btn-create-asset', function () {
|
||||||
var url = "{% url 'assets:asset-create' %}";
|
var url = "{% url 'assets:asset-create' %}";
|
||||||
if (current_node_id) {
|
if (current_node_id) {
|
||||||
|
@ -382,9 +216,6 @@ $(document).ready(function(){
|
||||||
var data = {
|
var data = {
|
||||||
'resources': id_list
|
'resources': id_list
|
||||||
};
|
};
|
||||||
function refreshPage() {
|
|
||||||
setTimeout( function () {window.location.reload();}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadTable() {
|
function reloadTable() {
|
||||||
asset_table.ajax.reload();
|
asset_table.ajax.reload();
|
||||||
|
@ -550,6 +381,30 @@ $(document).ready(function(){
|
||||||
flash_message: false
|
flash_message: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
}).on('click', '#menu_node_detail', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var the_url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}";
|
||||||
|
the_url = the_url.replace("{{ DEFAULT_PK }}", current_node_id);
|
||||||
|
function drawingNodeDetailModal(data){
|
||||||
|
$('#id_node_detail_id_view').html(data['id']);
|
||||||
|
$('#id_node_detail_name_view').html(data['name']);
|
||||||
|
$('#id_node_detail_full_name_view').html(data['full_value']);
|
||||||
|
$('#id_node_detail_key_view').html(data['key']);
|
||||||
|
$('#node_detail_modal').modal();
|
||||||
|
}
|
||||||
|
function error(data) {
|
||||||
|
alert(data)
|
||||||
|
}
|
||||||
|
function success(data) {
|
||||||
|
drawingNodeDetailModal(data)
|
||||||
|
}
|
||||||
|
requestApi({
|
||||||
|
url: the_url,
|
||||||
|
error: error,
|
||||||
|
method: 'GET',
|
||||||
|
success: success,
|
||||||
|
flash_message: false
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
|
||||||
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -62,7 +62,7 @@ function initTable() {
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id"}, {data: "name" }, {data: "rules", orderable: false},
|
{data: "id"}, {data: "name" }, {data: "rules", orderable: false},
|
||||||
{data: "system_users", orderable: false}, {data: "comment"},
|
{data: "system_users", orderable: false}, {data: "comment"},
|
||||||
{data: "id", orderable: false, width: "100px"}
|
{data: "id", orderable: false, width: "120px"}
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
|
||||||
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -25,9 +25,7 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2().off("select2:open");
|
$('.select2').select2().off("select2:open");
|
||||||
}).on('click', '.select2-selection__rendered', function (e) {
|
initAssetTreeModel('#id_assets');
|
||||||
e.preventDefault();
|
|
||||||
$("#asset_list_modal").modal();
|
|
||||||
})
|
})
|
||||||
.on("submit", "form", function (evt) {
|
.on("submit", "form", function (evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
|
||||||
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
|
||||||
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -55,7 +55,7 @@ function initTable() {
|
||||||
ajax_url: '{% url "api-assets:domain-list" %}',
|
ajax_url: '{% url "api-assets:domain-list" %}',
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id"}, {data: "name" }, {data: "asset_count", orderable: false },
|
{data: "id"}, {data: "name" }, {data: "asset_count", orderable: false },
|
||||||
{data: "gateway_count", orderable: false }, {data: "comment" }, {data: "id", orderable: false, width: "100px"}
|
{data: "gateway_count", orderable: false }, {data: "comment" }, {data: "id", orderable: false, width: "120px"}
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
|
|
@ -29,9 +29,7 @@ $(document).ready(function () {
|
||||||
$('.select2').select2({
|
$('.select2').select2({
|
||||||
closeOnSelect: false
|
closeOnSelect: false
|
||||||
})
|
})
|
||||||
}).on('click', '.select2-selection__rendered', function (e) {
|
initAssetTreeModel("#id_assets");
|
||||||
e.preventDefault();
|
|
||||||
$("#asset_list_modal").modal();
|
|
||||||
})
|
})
|
||||||
.on("submit", "form", function (evt) {
|
.on("submit", "form", function (evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
|
@ -45,7 +45,7 @@ function initTable() {
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id"}, {data: "name" }, {data: "value" },
|
{data: "id"}, {data: "name" }, {data: "value" },
|
||||||
{data: "asset_count", orderable: false},
|
{data: "asset_count", orderable: false},
|
||||||
{data: "id", orderable: false, width: "100px"}
|
{data: "id", orderable: false, width: "120px"}
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
{% extends '_base_create_update.html' %} {% load static %} {% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
<form id="platformForm" method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_field form.name layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.base layout="horizontal" %}
|
||||||
|
<div class="meta-config">
|
||||||
|
<div hidden class="windows-config">
|
||||||
|
{% bootstrap_field meta_form.security layout="horizontal" %}
|
||||||
|
{% bootstrap_field meta_form.console layout="horizontal" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% bootstrap_field form.comment 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>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
function toggleWindowConfig() {
|
||||||
|
var baseRef = $("#id_base");
|
||||||
|
var windowConfigRef = $(".windows-config");
|
||||||
|
var windowConfigInputRef = windowConfigRef.find(".form-control");
|
||||||
|
if (baseRef.val().toLowerCase() === 'windows') {
|
||||||
|
windowConfigInputRef.attr('disabled', false);
|
||||||
|
windowConfigRef.show();
|
||||||
|
} else {
|
||||||
|
windowConfigInputRef.attr('disabled', true);
|
||||||
|
windowConfigRef.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
toggleWindowConfig()
|
||||||
|
})
|
||||||
|
.on("change", "#id_base", function () {
|
||||||
|
toggleWindowConfig()
|
||||||
|
})
|
||||||
|
.on("submit", "form", function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var form = $("form");
|
||||||
|
var data = form.serializeObject();
|
||||||
|
var method = "POST";
|
||||||
|
var theUrl = '{% url "api-assets:platform-list" %}';
|
||||||
|
var redirectTo = '{% url "assets:platform-list" %}';
|
||||||
|
{% if type == "update" %}
|
||||||
|
theUrl = '{% url 'api-assets:platform-detail' pk=object.id %}';
|
||||||
|
method = "PUT";
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
var metaData = $(".meta-config .form-control").serializeObject();
|
||||||
|
objectAttrsIsBool(metaData, ['console']);
|
||||||
|
var metaKeys = Object.keys(metaData);
|
||||||
|
metaKeys.forEach(function (k, v) {
|
||||||
|
delete data[k]
|
||||||
|
});
|
||||||
|
data.meta = metaData;
|
||||||
|
console.log(data);
|
||||||
|
var props = {
|
||||||
|
url: theUrl,
|
||||||
|
data: data,
|
||||||
|
method: method,
|
||||||
|
form: form,
|
||||||
|
redirect_to: redirectTo
|
||||||
|
};
|
||||||
|
formSubmit(props);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'assets:platform-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'assets:platform-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="col-sm-8" style="padding-left: 0;">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label"><b>{{ object.name }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td>{% trans 'Name' %}:</td>
|
||||||
|
<td><b>{{ object.name }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Base platform' %}:</td>
|
||||||
|
<td><b>{{ object.base }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Charset' %}:</td>
|
||||||
|
<td><b>{{ object.charset }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Meta' %}:</td>
|
||||||
|
<td><b>{{ object.meta }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Comment' %}:</td>
|
||||||
|
<td><b>{{ object.comment }}</b></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content_bottom_left %}{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,75 @@
|
||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n static %}
|
||||||
|
{% block table_search %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_container %}
|
||||||
|
<div class="uc pull-left m-r-5">
|
||||||
|
<a href="{% url "assets:platform-create" %}" class="btn btn-sm btn-primary"> {% trans "Create platform" %} </a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="platform_list_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_all" class="ipt_check_all" >
|
||||||
|
</th>
|
||||||
|
<th class="text-center">{% trans 'Name' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Base platform' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Comment' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content_bottom_left %}{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
var platformTable = 0;
|
||||||
|
function initTable() {
|
||||||
|
var options = {
|
||||||
|
ele: $('#platform_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, render: function (cellData, tp, rowData, meta) {
|
||||||
|
cellData = htmlEscape(cellData);
|
||||||
|
var detailBtn = '<a href="{% url "assets:platform-detail" pk=999 %}">' + cellData + '</a>';
|
||||||
|
return detailBtn.replace('999', rowData.id);
|
||||||
|
}},
|
||||||
|
{targets: 4, createdCell: function (td, cellData, rowData) {
|
||||||
|
var updateBtn = '<a href="{% url "assets:platform-update" pk=9999 %}" class="btn btn-xs m-l-xs btn-info" disabled>{% trans "Update" %}</a>'.replace('9999', cellData);
|
||||||
|
var delBtn = '<a class="btn btn-xs btn-danger m-l-xs btn-object-delete" data-uid="{{ DEFAULT_PK }}" disabled>{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
|
if (!rowData.internal) {
|
||||||
|
updateBtn = updateBtn.replace('disabled', '');
|
||||||
|
delBtn = delBtn.replace('disabled', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(td).html(updateBtn + delBtn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:platform-list" %}',
|
||||||
|
columns: [
|
||||||
|
{data: "id"}, {data: "name"}, {data: "base" },
|
||||||
|
{data: "comment"}, {data: "id", orderable: false, width: "120px"}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
platformTable = jumpserver.initServerSideDataTable(options);
|
||||||
|
return platformTable
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
initTable();
|
||||||
|
})
|
||||||
|
.on('click', '.btn-object-delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
|
||||||
|
var uid = $this.data('uid');
|
||||||
|
var theUrl = '{% url "api-assets:platform-detail" pk=0 %}'.replace('0', uid);
|
||||||
|
objectDelete($this, name, theUrl);
|
||||||
|
setTimeout( function () {
|
||||||
|
platformTable.ajax.reload();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -4,9 +4,13 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
{% block custom_head_css_js %}
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
<style>
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
.table.node_edit {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -98,15 +102,8 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</form>
|
</form>
|
||||||
|
<table class="table" id="node_list_table">
|
||||||
{% for node in system_user.nodes.all|sort %}
|
</table>
|
||||||
<tr>
|
|
||||||
<td ><b class="bdg_node" data-gid={{ node.id }}>{{ node.full_value }}</b></td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-danger pull-right btn-xs btn-remove-from-node" type="button"><i class="fa fa-minus"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -120,91 +117,115 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
|
var assetsRelationUrl = "{% url 'api-assets:system-users-assets-relation-list' %}";
|
||||||
|
var nodesRelationUrl = "{% url 'api-assets:system-users-nodes-relation-list' %}";
|
||||||
|
|
||||||
function updateSystemUserNode(nodes) {
|
function getRelationUrl(type) {
|
||||||
var the_url = "{% url 'api-assets:system-user-detail' pk=system_user.id %}";
|
var theUrl = "";
|
||||||
var body = {
|
switch (type) {
|
||||||
nodes: Object.assign([], nodes)
|
case "asset":
|
||||||
};
|
theUrl = assetsRelationUrl;
|
||||||
var success = function(data) {
|
break;
|
||||||
// remove all the selected groups from select > option and rendered ul element;
|
case "node":
|
||||||
$('.select2-selection__rendered').empty();
|
theUrl = nodesRelationUrl;
|
||||||
$('#node_selected').val('');
|
break;
|
||||||
$.map(jumpserver.nodes_selected, function(node_name, index) {
|
}
|
||||||
$('#opt_' + index).remove();
|
return theUrl;
|
||||||
// change tr html of user groups.
|
}
|
||||||
$('.node_edit tbody').append(
|
|
||||||
'<tr>' +
|
function addObjects(objectsId, type) {
|
||||||
'<td><b class="bdg_node" data-gid="' + index + '">' + node_name + '</b></td>' +
|
if (!objectsId || objectsId.length === 0) {
|
||||||
'<td><button class="btn btn-danger btn-xs pull-right btn-remove-from-node" type="button"><i class="fa fa-minus"></i></button></td>' +
|
return
|
||||||
'</tr>'
|
}
|
||||||
)
|
var theUrl = getRelationUrl(type);
|
||||||
});
|
var body = [];
|
||||||
// clear jumpserver.nodes_selected
|
objectsId.forEach(function (v) {
|
||||||
jumpserver.nodes_selected = {};
|
var data = {systemuser: "{{ object.id }}"};
|
||||||
};
|
data[type] = v;
|
||||||
|
body.push(data)
|
||||||
|
});
|
||||||
requestApi({
|
requestApi({
|
||||||
url: the_url,
|
url: theUrl,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
|
method: "POST",
|
||||||
|
success: reloadPage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeObject(objectId, type, success) {
|
||||||
|
if (!objectId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var theUrl = getRelationUrl(type);
|
||||||
|
theUrl = setUrlParam(theUrl, 'systemuser', "{{ object.id }}");
|
||||||
|
theUrl = setUrlParam(theUrl, type, objectId);
|
||||||
|
if (!success) {
|
||||||
|
success = reloadPage
|
||||||
|
}
|
||||||
|
requestApi({
|
||||||
|
url: theUrl,
|
||||||
|
method: "DELETE",
|
||||||
success: success
|
success: success
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
jumpserver.nodes_selected = {};
|
|
||||||
|
function initNodeTable() {
|
||||||
|
var theUrl = setUrlParam(nodesRelationUrl, "systemuser", "{{ object.id }}");
|
||||||
|
var options = {
|
||||||
|
ele: $('#node_list_table'),
|
||||||
|
toggle: true,
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData) {
|
||||||
|
var removeBtn = '<button class="btn btn-danger pull-right btn-xs btn-remove-from-node" data-uid="UID" type="button"><i class="fa fa-minus"></i></button>'
|
||||||
|
.replace('UID', cellData);
|
||||||
|
$(td).html(removeBtn);
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: theUrl,
|
||||||
|
dom: "trp",
|
||||||
|
hideDefaultDefs: true,
|
||||||
|
columns: [
|
||||||
|
{data: "node_display", orderable: false},
|
||||||
|
{data: "node", orderable: false},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
table = jumpserver.initServerSideDataTable(options);
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2()
|
$('.select2').select2();
|
||||||
nodesSelect2Init(".nodes-select2")
|
nodesSelect2Init(".nodes-select2");
|
||||||
.on('select2:select', function(evt) {
|
|
||||||
var data = evt.params.data;
|
|
||||||
jumpserver.nodes_selected[data.id] = data.text;
|
|
||||||
})
|
|
||||||
.on('select2:unselect', function(evt) {
|
|
||||||
var data = evt.params.data;
|
|
||||||
delete jumpserver.nodes_selected[data.id];
|
|
||||||
});
|
|
||||||
assetUserListUrl = setUrlParam(assetUserListUrl, "system_user_id", "{{ system_user.id }}");
|
assetUserListUrl = setUrlParam(assetUserListUrl, "system_user_id", "{{ system_user.id }}");
|
||||||
needPush = true;
|
needPush = true;
|
||||||
initAssetUserTable();
|
initAssetUserTable();
|
||||||
|
initNodeTable();
|
||||||
})
|
})
|
||||||
.on('click', '.btn-remove-from-node', function() {
|
.on('click', '.btn-remove-from-node', function() {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var $tr = $this.closest('tr');
|
var nodeId = $(this).data('uid');
|
||||||
var $badge = $tr.find('.bdg_node');
|
var success = function() {
|
||||||
var gid = $badge.data('gid');
|
var $tr = $this.closest('tr');
|
||||||
var node_name = $badge.html() || $badge.text();
|
$tr.remove();
|
||||||
$('#groups_selected').append(
|
};
|
||||||
'<option value="' + gid + '" id="opt_' + gid + '">' + node_name + '</option>'
|
removeObject(nodeId, "node", success)
|
||||||
);
|
|
||||||
$tr.remove();
|
|
||||||
var nodes = $('.bdg_node').map(function () {
|
|
||||||
return $(this).data('gid');
|
|
||||||
}).get();
|
|
||||||
updateSystemUserNode(nodes);
|
|
||||||
})
|
})
|
||||||
.on('click', '#btn-add-to-node', function() {
|
.on('click', '#btn-add-to-node', function() {
|
||||||
if (Object.keys(jumpserver.nodes_selected).length === 0) {
|
var nodes = $("#node_selected").val();
|
||||||
return false;
|
addObjects(nodes, "node");
|
||||||
}
|
|
||||||
var nodes = $('.bdg_node').map(function() {
|
|
||||||
return $(this).data('gid');
|
|
||||||
}).get();
|
|
||||||
$.map(jumpserver.nodes_selected, function(value, index) {
|
|
||||||
nodes.push(index);
|
|
||||||
});
|
|
||||||
updateSystemUserNode(nodes);
|
|
||||||
})
|
})
|
||||||
.on('click', '.btn-push', function () {
|
.on('click', '.btn-push', function () {
|
||||||
var the_url = "{% url 'api-assets:system-user-push' pk=system_user.id %}";
|
var theUrl = "{% url 'api-assets:system-user-push' pk=system_user.id %}";
|
||||||
var error = function (data) {
|
var error = function (data) {
|
||||||
alert(data)
|
alert(data)
|
||||||
};
|
};
|
||||||
var success = function (data) {
|
var success = function (data) {
|
||||||
var task_id = data.task;
|
var taskId = data.task;
|
||||||
showCeleryTaskLog(task_id);
|
showCeleryTaskLog(taskId);
|
||||||
};
|
};
|
||||||
requestApi({
|
requestApi({
|
||||||
url: the_url,
|
url: theUrl,
|
||||||
error: error,
|
error: error,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success
|
success: success
|
||||||
|
@ -213,39 +234,37 @@ $(document).ready(function () {
|
||||||
.on('click', '.btn-push-auth', function () {
|
.on('click', '.btn-push-auth', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var asset_id = $this.data('asset');
|
var asset_id = $this.data('asset');
|
||||||
var the_url = "{% url 'api-assets:system-user-push-to-asset' pk=object.id aid=DEFAULT_PK %}";
|
var theUrl = "{% url 'api-assets:system-user-push-to-asset' pk=object.id aid=DEFAULT_PK %}";
|
||||||
the_url = the_url.replace("{{ DEFAULT_PK }}", asset_id);
|
theUrl = theUrl.replace("{{ DEFAULT_PK }}", asset_id);
|
||||||
var success = function (data) {
|
var success = function (data) {
|
||||||
var task_id = data.task;
|
var taskId = data.task;
|
||||||
showCeleryTaskLog(task_id);
|
showCeleryTaskLog(taskId);
|
||||||
};
|
};
|
||||||
var error = function (data) {
|
var error = function (data) {
|
||||||
alert(data)
|
alert(data)
|
||||||
};
|
};
|
||||||
requestApi({
|
requestApi({
|
||||||
url: the_url,
|
url: theUrl,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success,
|
success: success,
|
||||||
error: error
|
error: error
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.on('click', '.btn-test-connective', function () {
|
.on('click', '.btn-test-connective', function () {
|
||||||
var the_url = "{% url 'api-assets:system-user-connective' pk=system_user.id %}";
|
var theUrl = "{% url 'api-assets:system-user-connective' pk=system_user.id %}";
|
||||||
var error = function (data) {
|
var error = function (data) {
|
||||||
alert(data)
|
alert(data)
|
||||||
};
|
};
|
||||||
var success = function (data) {
|
var success = function (data) {
|
||||||
var task_id = data.task;
|
var taskId = data.task;
|
||||||
showCeleryTaskLog(task_id);
|
showCeleryTaskLog(taskId);
|
||||||
};
|
};
|
||||||
requestApi({
|
requestApi({
|
||||||
url: the_url,
|
url: theUrl,
|
||||||
error: error,
|
error: error,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success
|
success: success
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
|
||||||
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
|
||||||
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -17,11 +12,13 @@
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if system_user.can_perm_to_asset %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets' %}
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li class="pull-right">
|
<li class="pull-right">
|
||||||
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -144,6 +141,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if system_user.is_need_test_asset_connective %}
|
||||||
<tr>
|
<tr>
|
||||||
<td width="50%">{% trans 'Test assets connective' %}:</td>
|
<td width="50%">{% trans 'Test assets connective' %}:</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -152,13 +150,14 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if system_user.protocol != 'rdp' %}
|
{% if system_user.is_need_cmd_filter %}
|
||||||
<div class="col-sm-4" style="padding-left: 0; padding-right: 0">
|
<div class="col-sm-4" style="padding-left: 0; padding-right: 0">
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
|
|
|
@ -8,28 +8,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block table_search %}
|
{% block table_search %}
|
||||||
<div class="" style="float: right">
|
{% include '_csv_import_export.html' %}
|
||||||
<div class=" btn-group">
|
|
||||||
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a class=" btn_export" tabindex="0">
|
|
||||||
<span>{% trans "Export" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
|
|
||||||
<span>{% trans "Import" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
|
|
||||||
<span>{% trans "Update" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block table_container %}
|
{% block table_container %}
|
||||||
|
@ -57,8 +36,6 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% include 'assets/_system_user_import_modal.html' %}
|
|
||||||
{% include 'assets/_system_user_update_modal.html' %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
|
@ -85,18 +62,17 @@ function initTable() {
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"},
|
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"},
|
||||||
{data: "login_mode"}, {data: "assets_amount", orderable: false },
|
{data: "login_mode"}, {data: "assets_amount", orderable: false },
|
||||||
{data: "comment" }, {data: "id", orderable: false, width: "100px"}
|
{data: "comment" }, {data: "id", orderable: false, width: "120px"}
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
system_user_table = jumpserver.initServerSideDataTable(options);
|
return jumpserver.initServerSideDataTable(options);
|
||||||
return system_user_table
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
system_user_table = initTable();
|
||||||
|
initCsvImportExport(system_user_table, "{% trans 'System user' %}")
|
||||||
})
|
})
|
||||||
|
|
||||||
.on('click', '.btn_admin_user_delete', function () {
|
.on('click', '.btn_admin_user_delete', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var $data_table = $('#cluster_list_table').DataTable();
|
var $data_table = $('#cluster_list_table').DataTable();
|
||||||
|
@ -108,125 +84,6 @@ $(document).ready(function(){
|
||||||
$data_table.ajax.reload();
|
$data_table.ajax.reload();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
})
|
})
|
||||||
|
|
||||||
.on('click', '#btn_bulk_update', function () {
|
|
||||||
var action = $('#slct_bulk_update').val();
|
|
||||||
var $data_table = $('#system_user_list_table').DataTable();
|
|
||||||
var id_list = [];
|
|
||||||
var plain_id_list = [];
|
|
||||||
$data_table.rows({selected: true}).every(function(){
|
|
||||||
id_list.push({id: this.data().id});
|
|
||||||
plain_id_list.push(this.data().id);
|
|
||||||
});
|
|
||||||
if (id_list === []) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var the_url = "{% url 'api-assets:system-user-list' %}";
|
|
||||||
function doDelete() {
|
|
||||||
swal({
|
|
||||||
title: "{% trans 'Are you sure?' %}",
|
|
||||||
text: "{% trans 'This will delete the selected System Users !!!' %}",
|
|
||||||
type: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonText: "{% trans 'Cancel' %}",
|
|
||||||
confirmButtonColor: "#DD6B55",
|
|
||||||
confirmButtonText: "{% trans 'Confirm' %}",
|
|
||||||
closeOnConfirm: false
|
|
||||||
}, function() {
|
|
||||||
var success = function() {
|
|
||||||
var msg = "{% trans 'System Users Deleted.' %}";
|
|
||||||
swal("{% trans 'System Users Delete' %}", msg, "success");
|
|
||||||
$('#system_user_list_table').DataTable().ajax.reload();
|
|
||||||
};
|
|
||||||
var fail = function() {
|
|
||||||
var msg = "{% trans 'System Users Deleting failed.' %}";
|
|
||||||
swal("{% trans 'System Users Delete' %}", msg, "error");
|
|
||||||
};
|
|
||||||
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
|
|
||||||
requestApi({url: url_delete, method: 'DELETE', success: success, error: fail});
|
|
||||||
$data_table.ajax.reload();
|
|
||||||
jumpserver.checked = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function doUpdate() {
|
|
||||||
{# TODO: bulk update the System Users #}
|
|
||||||
}
|
|
||||||
switch (action) {
|
|
||||||
case 'delete':
|
|
||||||
doDelete();
|
|
||||||
break;
|
|
||||||
case 'update':
|
|
||||||
doUpdate();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('click', '.btn_export', function () {
|
|
||||||
var system_users = system_user_table.selected;
|
|
||||||
var data = {
|
|
||||||
'resources': system_users
|
|
||||||
};
|
|
||||||
var search = $("input[type='search']").val();
|
|
||||||
var props = {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
success_url: "{% url 'api-assets:system-user-list' %}",
|
|
||||||
format: "csv",
|
|
||||||
params:{
|
|
||||||
search:search
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APIExportData(props);
|
|
||||||
})
|
|
||||||
.on('click', '#btn_import_confirm', function () {
|
|
||||||
var url = "{% url 'api-assets:system-user-list' %}";
|
|
||||||
var file = document.getElementById('id_file').files[0];
|
|
||||||
if(!file){
|
|
||||||
toastr.error("{% trans 'Please select file' %}");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var data_table = $('#system_user_list_table').DataTable();
|
|
||||||
APIImportData({
|
|
||||||
url: url,
|
|
||||||
method: "POST",
|
|
||||||
body: file,
|
|
||||||
data_table: data_table
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on('click', '#download_update_template', function () {
|
|
||||||
var system_users = system_user_table.selected;
|
|
||||||
var data = {
|
|
||||||
'resources': system_users
|
|
||||||
};
|
|
||||||
var search = $("input[type='search']").val();
|
|
||||||
var props = {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
success_url: "{% url 'api-assets:system-user-list' %}?format=csv&template=update",
|
|
||||||
format: "csv",
|
|
||||||
params:{
|
|
||||||
search:search
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APIExportData(props);
|
|
||||||
})
|
|
||||||
.on('click', '#btn_update_confirm', function () {
|
|
||||||
var file = document.getElementById('update_file').files[0];
|
|
||||||
if(!file){
|
|
||||||
toastr.error("{% trans "Please select file" %}");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var url = "{% url 'api-assets:system-user-list' %}";
|
|
||||||
var data_table = $('#system_user_list_table').DataTable();
|
|
||||||
|
|
||||||
APIImportData({
|
|
||||||
url: url,
|
|
||||||
method: "PUT",
|
|
||||||
body: file,
|
|
||||||
data_table: data_table
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ app_name = 'assets'
|
||||||
|
|
||||||
router = BulkRouter()
|
router = BulkRouter()
|
||||||
router.register(r'assets', api.AssetViewSet, 'asset')
|
router.register(r'assets', api.AssetViewSet, 'asset')
|
||||||
|
router.register(r'platforms', api.AssetPlatformViewSet, 'platform')
|
||||||
router.register(r'admin-users', api.AdminUserViewSet, 'admin-user')
|
router.register(r'admin-users', api.AdminUserViewSet, 'admin-user')
|
||||||
router.register(r'system-users', api.SystemUserViewSet, 'system-user')
|
router.register(r'system-users', api.SystemUserViewSet, 'system-user')
|
||||||
router.register(r'labels', api.LabelViewSet, 'label')
|
router.register(r'labels', api.LabelViewSet, 'label')
|
||||||
|
@ -23,6 +24,8 @@ router.register(r'asset-users', api.AssetUserViewSet, 'asset-user')
|
||||||
router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info')
|
router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info')
|
||||||
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
||||||
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
|
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
|
||||||
|
router.register(r'system-users-assets-relations', api.SystemUserAssetRelationViewSet, 'system-users-assets-relation')
|
||||||
|
router.register(r'system-users-nodes-relations', api.SystemUserNodeRelationViewSet, 'system-users-nodes-relation')
|
||||||
|
|
||||||
cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
|
cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
|
||||||
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
||||||
|
@ -35,6 +38,8 @@ urlpatterns = [
|
||||||
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
|
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
|
||||||
path('assets/<uuid:pk>/gateway/',
|
path('assets/<uuid:pk>/gateway/',
|
||||||
api.AssetGatewayApi.as_view(), name='asset-gateway'),
|
api.AssetGatewayApi.as_view(), name='asset-gateway'),
|
||||||
|
path('assets/<uuid:pk>/platform/',
|
||||||
|
api.AssetPlatformRetrieveApi.as_view(), name='asset-platform-detail'),
|
||||||
|
|
||||||
path('asset-users/auth-info/',
|
path('asset-users/auth-info/',
|
||||||
api.AssetUserAuthInfoApi.as_view(), name='asset-user-auth-info'),
|
api.AssetUserAuthInfoApi.as_view(), name='asset-user-auth-info'),
|
||||||
|
|
|
@ -16,6 +16,11 @@ urlpatterns = [
|
||||||
# Asset user view
|
# Asset user view
|
||||||
path('asset/<uuid:pk>/asset-user/', views.AssetUserListView.as_view(), name='asset-user-list'),
|
path('asset/<uuid:pk>/asset-user/', views.AssetUserListView.as_view(), name='asset-user-list'),
|
||||||
|
|
||||||
|
path('platform/', views.PlatformListView.as_view(), name='platform-list'),
|
||||||
|
path('platform/create/', views.PlatformCreateView.as_view(), name='platform-create'),
|
||||||
|
path('platform/<int:pk>/', views.PlatformDetailView.as_view(), name='platform-detail'),
|
||||||
|
path('platform/<int:pk>/update/', views.PlatformUpdateView.as_view(), name='platform-update'),
|
||||||
|
|
||||||
# User asset view
|
# User asset view
|
||||||
path('user-asset/', views.UserAssetListView.as_view(), name='user-asset-list'),
|
path('user-asset/', views.UserAssetListView.as_view(), name='user-asset-list'),
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
from .asset import *
|
from .asset import *
|
||||||
|
from .platform import *
|
||||||
from .system_user import *
|
from .system_user import *
|
||||||
from .admin_user import *
|
from .admin_user import *
|
||||||
from .label import *
|
from .label import *
|
||||||
|
|
|
@ -74,7 +74,7 @@ class UserAssetListView(PermissionsMixin, TemplateView):
|
||||||
|
|
||||||
class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
|
class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
|
||||||
model = Asset
|
model = Asset
|
||||||
form_class = forms.AssetCreateForm
|
form_class = forms.AssetCreateUpdateForm
|
||||||
template_name = 'assets/asset_create.html'
|
template_name = 'assets/asset_create.html'
|
||||||
success_url = reverse_lazy('assets:asset-list')
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
permission_classes = [IsOrgAdmin]
|
permission_classes = [IsOrgAdmin]
|
||||||
|
@ -110,7 +110,7 @@ class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
|
||||||
|
|
||||||
class AssetUpdateView(PermissionsMixin, UpdateView):
|
class AssetUpdateView(PermissionsMixin, UpdateView):
|
||||||
model = Asset
|
model = Asset
|
||||||
form_class = forms.AssetUpdateForm
|
form_class = forms.AssetCreateUpdateForm
|
||||||
template_name = 'assets/asset_update.html'
|
template_name = 'assets/asset_update.html'
|
||||||
success_url = reverse_lazy('assets:asset-list')
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
permission_classes = [IsOrgAdmin]
|
permission_classes = [IsOrgAdmin]
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.views.generic import TemplateView, CreateView, \
|
from django.views.generic import (
|
||||||
UpdateView, DeleteView, DetailView
|
TemplateView, CreateView, UpdateView, DeleteView, DetailView
|
||||||
|
)
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.urls import reverse_lazy, reverse
|
from django.urls import reverse_lazy, reverse
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.views import generic
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from common.permissions import PermissionsMixin, IsSuperUser
|
||||||
|
from ..models import Platform
|
||||||
|
from ..forms import PlatformForm, PlatformMetaForm
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'PlatformListView', 'PlatformUpdateView', 'PlatformCreateView',
|
||||||
|
'PlatformDetailView',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformListView(PermissionsMixin, generic.TemplateView):
|
||||||
|
template_name = 'assets/platform_list.html'
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context.update({
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _("Platform list"),
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformCreateView(PermissionsMixin, generic.CreateView):
|
||||||
|
form_class = PlatformForm
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
template_name = 'assets/platform_create_update.html'
|
||||||
|
model = Platform
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
meta_form = PlatformMetaForm()
|
||||||
|
context.update({
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _("Create platform"),
|
||||||
|
'meta_form': meta_form,
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformUpdateView(generic.UpdateView):
|
||||||
|
form_class = PlatformForm
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
model = Platform
|
||||||
|
template_name = 'assets/platform_create_update.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
meta_form = PlatformMetaForm(initial=self.object.meta)
|
||||||
|
context.update({
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _("Update platform"),
|
||||||
|
'type': 'update',
|
||||||
|
'meta_form': meta_form,
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformDetailView(generic.DetailView):
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
model = Platform
|
||||||
|
template_name = 'assets/platform_detail.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context.update({
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _("Platform detail"),
|
||||||
|
})
|
||||||
|
return context
|
|
@ -11,4 +11,4 @@ class FTPLogViewSet(OrgModelViewSet):
|
||||||
model = FTPLog
|
model = FTPLog
|
||||||
serializer_class = FTPLogSerializer
|
serializer_class = FTPLogSerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
|
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
|
||||||
|
http_method_names = ['get', 'post', 'head', 'options']
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.2.7 on 2019-12-02 02:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('audits', '0006_auto_20190726_1753'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ftplog',
|
||||||
|
name='remote_addr',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Remote addr'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='operatelog',
|
||||||
|
name='remote_addr',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Remote addr'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='passwordchangelog',
|
||||||
|
name='remote_addr',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Remote addr'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -16,7 +16,7 @@ __all__ = [
|
||||||
class FTPLog(OrgModelMixin):
|
class FTPLog(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
user = models.CharField(max_length=128, verbose_name=_('User'))
|
user = models.CharField(max_length=128, verbose_name=_('User'))
|
||||||
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
|
remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
|
||||||
asset = models.CharField(max_length=1024, verbose_name=_("Asset"))
|
asset = models.CharField(max_length=1024, verbose_name=_("Asset"))
|
||||||
system_user = models.CharField(max_length=128, verbose_name=_("System user"))
|
system_user = models.CharField(max_length=128, verbose_name=_("System user"))
|
||||||
operate = models.CharField(max_length=16, verbose_name=_("Operate"))
|
operate = models.CharField(max_length=16, verbose_name=_("Operate"))
|
||||||
|
@ -39,7 +39,7 @@ class OperateLog(OrgModelMixin):
|
||||||
action = models.CharField(max_length=16, choices=ACTION_CHOICES, verbose_name=_("Action"))
|
action = models.CharField(max_length=16, choices=ACTION_CHOICES, verbose_name=_("Action"))
|
||||||
resource_type = models.CharField(max_length=64, verbose_name=_("Resource Type"))
|
resource_type = models.CharField(max_length=64, verbose_name=_("Resource Type"))
|
||||||
resource = models.CharField(max_length=128, verbose_name=_("Resource"))
|
resource = models.CharField(max_length=128, verbose_name=_("Resource"))
|
||||||
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
|
remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
|
||||||
datetime = models.DateTimeField(auto_now=True)
|
datetime = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -50,7 +50,7 @@ class PasswordChangeLog(models.Model):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
user = models.CharField(max_length=128, verbose_name=_('User'))
|
user = models.CharField(max_length=128, verbose_name=_('User'))
|
||||||
change_by = models.CharField(max_length=128, verbose_name=_("Change by"))
|
change_by = models.CharField(max_length=128, verbose_name=_("Change by"))
|
||||||
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
|
remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
|
||||||
datetime = models.DateTimeField(auto_now=True)
|
datetime = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -11,14 +11,15 @@ from rest_framework.request import Request
|
||||||
from jumpserver.utils import current_request
|
from jumpserver.utils import current_request
|
||||||
from common.utils import get_request_ip, get_logger, get_syslogger
|
from common.utils import get_request_ip, get_logger, get_syslogger
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
from users.signals import post_user_change_password
|
||||||
from authentication.signals import post_auth_failed, post_auth_success
|
from authentication.signals import post_auth_failed, post_auth_success
|
||||||
from terminal.models import Session, Command
|
from terminal.models import Session, Command
|
||||||
from terminal.backends.command.serializers import SessionCommandSerializer
|
from common.utils.encode import model_to_json
|
||||||
from . import models, serializers
|
from .utils import write_login_log
|
||||||
from .tasks import write_login_log_async
|
from . import models
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
sys_logger = get_syslogger("audits")
|
sys_logger = get_syslogger(__name__)
|
||||||
json_render = JSONRenderer()
|
json_render = JSONRenderer()
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +27,8 @@ MODELS_NEED_RECORD = (
|
||||||
'User', 'UserGroup', 'Asset', 'Node', 'AdminUser', 'SystemUser',
|
'User', 'UserGroup', 'Asset', 'Node', 'AdminUser', 'SystemUser',
|
||||||
'Domain', 'Gateway', 'Organization', 'AssetPermission', 'CommandFilter',
|
'Domain', 'Gateway', 'Organization', 'AssetPermission', 'CommandFilter',
|
||||||
'CommandFilterRule', 'License', 'Setting', 'Account', 'SyncInstanceTask',
|
'CommandFilterRule', 'License', 'Setting', 'Account', 'SyncInstanceTask',
|
||||||
|
'Platform', 'ChangeAuthPlan', 'GatherUserTask',
|
||||||
|
'RemoteApp', 'RemoteAppPermission', 'DatabaseApp', 'DatabaseAppPermission',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,8 +53,11 @@ def create_operate_log(action, sender, resource):
|
||||||
logger.error("Create operate log error: {}".format(e))
|
logger.error("Create operate log error: {}".format(e))
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, dispatch_uid="my_unique_identifier")
|
@receiver(post_save)
|
||||||
def on_object_created_or_update(sender, instance=None, created=False, **kwargs):
|
def on_object_created_or_update(sender, instance=None, created=False, update_fields=None, **kwargs):
|
||||||
|
if instance._meta.object_name == 'User' and \
|
||||||
|
update_fields and 'last_login' in update_fields:
|
||||||
|
return
|
||||||
if created:
|
if created:
|
||||||
action = models.OperateLog.ACTION_CREATE
|
action = models.OperateLog.ACTION_CREATE
|
||||||
else:
|
else:
|
||||||
|
@ -59,47 +65,46 @@ def on_object_created_or_update(sender, instance=None, created=False, **kwargs):
|
||||||
create_operate_log(action, sender, instance)
|
create_operate_log(action, sender, instance)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, dispatch_uid="my_unique_identifier")
|
@receiver(post_delete)
|
||||||
def on_object_delete(sender, instance=None, **kwargs):
|
def on_object_delete(sender, instance=None, **kwargs):
|
||||||
create_operate_log(models.OperateLog.ACTION_DELETE, sender, instance)
|
create_operate_log(models.OperateLog.ACTION_DELETE, sender, instance)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User, dispatch_uid="my_unique_identifier")
|
@receiver(post_user_change_password, sender=User)
|
||||||
def on_user_change_password(sender, instance=None, **kwargs):
|
def on_user_change_password(sender, user=None, **kwargs):
|
||||||
if hasattr(instance, '_set_password'):
|
if not current_request:
|
||||||
if not current_request or not current_request.user.is_authenticated:
|
remote_addr = '127.0.0.1'
|
||||||
return
|
change_by = 'System'
|
||||||
with transaction.atomic():
|
else:
|
||||||
models.PasswordChangeLog.objects.create(
|
remote_addr = get_request_ip(current_request)
|
||||||
user=instance, change_by=current_request.user,
|
if not current_request.user.is_authenticated:
|
||||||
remote_addr=get_request_ip(current_request),
|
change_by = str(user)
|
||||||
)
|
else:
|
||||||
|
change_by = str(current_request.user)
|
||||||
|
with transaction.atomic():
|
||||||
|
models.PasswordChangeLog.objects.create(
|
||||||
|
user=str(user), change_by=change_by,
|
||||||
|
remote_addr=remote_addr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def on_audits_log_create(sender, instance=None, **kwargs):
|
def on_audits_log_create(sender, instance=None, **kwargs):
|
||||||
if sender == models.UserLoginLog:
|
if sender == models.UserLoginLog:
|
||||||
category = "login_log"
|
category = "login_log"
|
||||||
serializer = serializers.LoginLogSerializer
|
|
||||||
elif sender == models.FTPLog:
|
elif sender == models.FTPLog:
|
||||||
serializer = serializers.FTPLogSerializer
|
|
||||||
category = "ftp_log"
|
category = "ftp_log"
|
||||||
elif sender == models.OperateLog:
|
elif sender == models.OperateLog:
|
||||||
category = "operation_log"
|
category = "operation_log"
|
||||||
serializer = serializers.OperateLogSerializer
|
|
||||||
elif sender == models.PasswordChangeLog:
|
elif sender == models.PasswordChangeLog:
|
||||||
category = "password_change_log"
|
category = "password_change_log"
|
||||||
serializer = serializers.PasswordChangeLogSerializer
|
|
||||||
elif sender == Session:
|
elif sender == Session:
|
||||||
category = "host_session_log"
|
category = "host_session_log"
|
||||||
serializer = serializers.SessionAuditSerializer
|
|
||||||
elif sender == Command:
|
elif sender == Command:
|
||||||
category = "session_command_log"
|
category = "session_command_log"
|
||||||
serializer = SessionCommandSerializer
|
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
s = serializer(instance=instance)
|
data = model_to_json(instance, indent=None)
|
||||||
data = json_render.render(s.data).decode(errors='ignore')
|
|
||||||
msg = "{} - {}".format(category, data)
|
msg = "{} - {}".format(category, data)
|
||||||
sys_logger.info(msg)
|
sys_logger.info(msg)
|
||||||
|
|
||||||
|
@ -129,7 +134,7 @@ def on_user_auth_success(sender, user, request, **kwargs):
|
||||||
logger.debug('User login success: {}'.format(user.username))
|
logger.debug('User login success: {}'.format(user.username))
|
||||||
data = generate_data(user.username, request)
|
data = generate_data(user.username, request)
|
||||||
data.update({'mfa': int(user.mfa_enabled), 'status': True})
|
data.update({'mfa': int(user.mfa_enabled), 'status': True})
|
||||||
write_login_log_async.delay(**data)
|
write_login_log(**data)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_auth_failed)
|
@receiver(post_auth_failed)
|
||||||
|
@ -137,4 +142,4 @@ def on_user_auth_failed(sender, username, request, reason, **kwargs):
|
||||||
logger.debug('User login failed: {}'.format(username))
|
logger.debug('User login failed: {}'.format(username))
|
||||||
data = generate_data(username, request)
|
data = generate_data(username, request)
|
||||||
data.update({'reason': reason, 'status': False})
|
data.update({'reason': reason, 'status': False})
|
||||||
write_login_log_async.delay(**data)
|
write_login_log(**data)
|
||||||
|
|
|
@ -6,8 +6,7 @@ from django.conf import settings
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
|
||||||
from ops.celery.decorator import register_as_period_task
|
from ops.celery.decorator import register_as_period_task
|
||||||
from .models import UserLoginLog
|
from .models import UserLoginLog, OperateLog
|
||||||
from .utils import write_login_log
|
|
||||||
|
|
||||||
|
|
||||||
@register_as_period_task(interval=3600*24)
|
@register_as_period_task(interval=3600*24)
|
||||||
|
@ -22,6 +21,13 @@ def clean_login_log_period():
|
||||||
UserLoginLog.objects.filter(datetime__lt=expired_day).delete()
|
UserLoginLog.objects.filter(datetime__lt=expired_day).delete()
|
||||||
|
|
||||||
|
|
||||||
|
@register_as_period_task(interval=3600*24)
|
||||||
@shared_task
|
@shared_task
|
||||||
def write_login_log_async(*args, **kwargs):
|
def clean_operation_log_period():
|
||||||
write_login_log(*args, **kwargs)
|
now = timezone.now()
|
||||||
|
try:
|
||||||
|
days = int(settings.LOGIN_LOG_KEEP_DAYS)
|
||||||
|
except ValueError:
|
||||||
|
days = 90
|
||||||
|
expired_day = now - datetime.timedelta(days=days)
|
||||||
|
OperateLog.objects.filter(datetime__lt=expired_day).delete()
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
{% load common_tags %}
|
{% load common_tags %}
|
||||||
{% block custom_head_css_js %}
|
{% block custom_head_css_js %}
|
||||||
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
<style>
|
<style>
|
||||||
#search_btn {
|
#search_btn {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -14,6 +12,9 @@
|
||||||
.form-control {
|
.form-control {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
.select2-selection__rendered span.select2-selection, .select2-container .select2-selection--single {
|
||||||
|
height: 30px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -102,47 +102,47 @@
|
||||||
|
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
jumpserver.initStaticTable('#login_log_table');
|
jumpserver.initStaticTable('#login_log_table');
|
||||||
$('#date .input-daterange').datepicker({
|
$('#date .input-daterange').datepicker({
|
||||||
format: "yyyy-mm-dd",
|
format: "yyyy-mm-dd",
|
||||||
todayBtn: "linked",
|
todayBtn: "linked",
|
||||||
keyboardNavigation: false,
|
keyboardNavigation: false,
|
||||||
forceParse: false,
|
forceParse: false,
|
||||||
calendarWeeks: true,
|
calendarWeeks: true,
|
||||||
autoclose: true
|
autoclose: true
|
||||||
|
|
||||||
});
|
});
|
||||||
$('.select2').select2({
|
$('.select2').select2({
|
||||||
dropdownAutoWidth: true,
|
dropdownAutoWidth: true,
|
||||||
width: 'auto'
|
width: 'auto'
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.on('click', '.btn_export', function () {
|
||||||
|
var date_from = $('#id_date_from').val();
|
||||||
|
var date_to = $('#id_date_to').val();
|
||||||
|
var user = $('.select2 option:selected').val();
|
||||||
|
var keyword = $('#search').val();
|
||||||
|
$.ajax({
|
||||||
|
url: "{% url "audits:login-log-export" %}",
|
||||||
|
method: 'POST',
|
||||||
|
data: JSON.stringify({
|
||||||
|
'date_from':date_from,
|
||||||
|
'date_to':date_to,
|
||||||
|
'user':user,
|
||||||
|
'keyword':keyword
|
||||||
|
}),
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data, textStatus) {
|
||||||
|
window.open(data.redirect)
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
toastr.error('Export failed');
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.on('click', '.btn_export', function () {
|
})
|
||||||
var date_from = $('#id_date_from').val();
|
</script>
|
||||||
var date_to = $('#id_date_to').val();
|
|
||||||
var user = $('.select2 option:selected').val();
|
|
||||||
var keyword = $('#search').val();
|
|
||||||
$.ajax({
|
|
||||||
url: "{% url "audits:login-log-export" %}",
|
|
||||||
method: 'POST',
|
|
||||||
data: JSON.stringify({
|
|
||||||
'date_from':date_from,
|
|
||||||
'date_to':date_to,
|
|
||||||
'user':user,
|
|
||||||
'keyword':keyword
|
|
||||||
}),
|
|
||||||
dataType: "json",
|
|
||||||
success: function (data, textStatus) {
|
|
||||||
window.open(data.redirect)
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
toastr.error('Export failed');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
{% load common_tags %}
|
{% load common_tags %}
|
||||||
{% block custom_head_css_js %}
|
{% block custom_head_css_js %}
|
||||||
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
<style>
|
<style>
|
||||||
#search_btn {
|
#search_btn {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -69,7 +67,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block table_head %}
|
{% block table_head %}
|
||||||
<th class="text-center">{% trans 'User' %}</th>
|
<th class="text-center">{% trans 'Handlers' %}</th>
|
||||||
<th class="text-center">{% trans 'Action' %}</th>
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
<th class="text-center">{% trans 'Resource Type' %}</th>
|
<th class="text-center">{% trans 'Resource Type' %}</th>
|
||||||
<th class="text-center">{% trans 'Resource' %}</th>
|
<th class="text-center">{% trans 'Resource' %}</th>
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
{% load common_tags %}
|
{% load common_tags %}
|
||||||
{% block custom_head_css_js %}
|
{% block custom_head_css_js %}
|
||||||
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
|
||||||
<style>
|
<style>
|
||||||
#search_btn {
|
#search_btn {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
|
@ -109,7 +109,7 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
|
||||||
|
|
||||||
class AccessTokenAuthentication(authentication.BaseAuthentication):
|
class AccessTokenAuthentication(authentication.BaseAuthentication):
|
||||||
keyword = 'Bearer'
|
keyword = 'Bearer'
|
||||||
expiration = settings.TOKEN_EXPIRATION or 3600
|
# expiration = settings.TOKEN_EXPIRATION or 3600
|
||||||
model = get_user_model()
|
model = get_user_model()
|
||||||
|
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import warnings
|
||||||
import ldap
|
import ldap
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||||
from django_auth_ldap.backend import _LDAPUser, LDAPBackend
|
from django_auth_ldap.backend import _LDAPUser, LDAPBackend, LDAPSettings
|
||||||
from django_auth_ldap.config import _LDAPConfig, LDAPSearch, LDAPSearchUnion
|
from django_auth_ldap.config import _LDAPConfig, LDAPSearch, LDAPSearchUnion
|
||||||
|
|
||||||
from users.utils import construct_user_email
|
from users.utils import construct_user_email
|
||||||
|
@ -17,7 +18,6 @@ class LDAPAuthorizationBackend(LDAPBackend):
|
||||||
"""
|
"""
|
||||||
Override this class to override _LDAPUser to LDAPUser
|
Override this class to override _LDAPUser to LDAPUser
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def user_can_authenticate(user):
|
def user_can_authenticate(user):
|
||||||
"""
|
"""
|
||||||
|
@ -27,18 +27,26 @@ class LDAPAuthorizationBackend(LDAPBackend):
|
||||||
is_valid = getattr(user, 'is_valid', None)
|
is_valid = getattr(user, 'is_valid', None)
|
||||||
return is_valid or is_valid is None
|
return is_valid or is_valid is None
|
||||||
|
|
||||||
def authenticate(self, request=None, username=None, password=None, **kwargs):
|
def pre_check(self, username):
|
||||||
|
if not settings.AUTH_LDAP:
|
||||||
|
return False
|
||||||
logger.info('Authentication LDAP backend')
|
logger.info('Authentication LDAP backend')
|
||||||
if not username:
|
if not username:
|
||||||
logger.info('Authenticate failed: username is None')
|
logger.info('Authenticate failed: username is None')
|
||||||
return None
|
return False
|
||||||
if settings.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS:
|
if settings.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS:
|
||||||
user_model = self.get_user_model()
|
user_model = self.get_user_model()
|
||||||
exist = user_model.objects.filter(username=username).exists()
|
exist = user_model.objects.filter(username=username).exists()
|
||||||
if not exist:
|
if not exist:
|
||||||
msg = 'Authentication failed: user ({}) is not in the user list'
|
msg = 'Authentication failed: user ({}) is not in the user list'
|
||||||
logger.info(msg.format(username))
|
logger.info(msg.format(username))
|
||||||
return None
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def authenticate(self, request=None, username=None, password=None, **kwargs):
|
||||||
|
match = self.pre_check(username)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
ldap_user = LDAPUser(self, username=username.strip(), request=request)
|
ldap_user = LDAPUser(self, username=username.strip(), request=request)
|
||||||
user = self.authenticate_ldap_user(ldap_user, password)
|
user = self.authenticate_ldap_user(ldap_user, password)
|
||||||
logger.info('Authenticate user: {}'.format(user))
|
logger.info('Authenticate user: {}'.format(user))
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
|
||||||
from keycloak.realm import KeycloakRealm
|
from keycloak.realm import KeycloakRealm
|
||||||
from keycloak.keycloak_openid import KeycloakOpenID
|
from keycloak.keycloak_openid import KeycloakOpenID
|
||||||
|
|
||||||
from .signals import post_create_openid_user
|
from .signals import post_create_or_update_openid_user
|
||||||
from .decorator import ssl_verification
|
from .decorator import ssl_verification
|
||||||
|
|
||||||
OIDT_ACCESS_TOKEN = 'oidt_access_token'
|
OIDT_ACCESS_TOKEN = 'oidt_access_token'
|
||||||
|
@ -155,7 +155,7 @@ class Client(object):
|
||||||
"""
|
"""
|
||||||
userinfo = self.get_userinfo(token=token_response['access_token'])
|
userinfo = self.get_userinfo(token=token_response['access_token'])
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
user, _ = get_user_model().objects.update_or_create(
|
user, created = get_user_model().objects.update_or_create(
|
||||||
username=userinfo.get('preferred_username', ''),
|
username=userinfo.get('preferred_username', ''),
|
||||||
defaults={
|
defaults={
|
||||||
'email': userinfo.get('email', ''),
|
'email': userinfo.get('email', ''),
|
||||||
|
@ -169,7 +169,9 @@ class Client(object):
|
||||||
refresh_token=token_response['refresh_token'],
|
refresh_token=token_response['refresh_token'],
|
||||||
)
|
)
|
||||||
if user:
|
if user:
|
||||||
post_create_openid_user.send(sender=user.__class__, user=user)
|
post_create_or_update_openid_user.send(
|
||||||
|
sender=user.__class__, user=user, created=created
|
||||||
|
)
|
||||||
|
|
||||||
return oidt_profile
|
return oidt_profile
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.dispatch import Signal
|
from django.dispatch import Signal
|
||||||
|
|
||||||
|
|
||||||
post_create_openid_user = Signal(providing_args=('user',))
|
post_create_or_update_openid_user = Signal(providing_args=('user',))
|
||||||
post_openid_login_success = Signal(providing_args=('user', 'request'))
|
post_openid_login_success = Signal(providing_args=('user', 'request'))
|
||||||
|
|
|
@ -25,7 +25,8 @@ __all__ = ['OpenIDLoginView', 'OpenIDLoginCompleteView']
|
||||||
|
|
||||||
class OpenIDLoginView(RedirectView):
|
class OpenIDLoginView(RedirectView):
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
redirect_uri = settings.BASE_SITE_URL + str(settings.LOGIN_COMPLETE_URL)
|
redirect_uri = settings.BASE_SITE_URL + \
|
||||||
|
str(settings.AUTH_OPENID_LOGIN_COMPLETE_URL)
|
||||||
nonce = Nonce(
|
nonce = Nonce(
|
||||||
redirect_uri=redirect_uri,
|
redirect_uri=redirect_uri,
|
||||||
next_path=self.request.GET.get('next')
|
next_path=self.request.GET.get('next')
|
||||||
|
|
|
@ -118,9 +118,9 @@ class MFAFailedError(AuthFailedNeedLogMixin, AuthFailedError):
|
||||||
|
|
||||||
class BlockLoginError(AuthFailedNeedBlockMixin, AuthFailedError):
|
class BlockLoginError(AuthFailedNeedBlockMixin, AuthFailedError):
|
||||||
error = 'block_login'
|
error = 'block_login'
|
||||||
msg = block_login_msg.format(settings.SECURITY_LOGIN_LIMIT_TIME)
|
|
||||||
|
|
||||||
def __init__(self, username, ip):
|
def __init__(self, username, ip):
|
||||||
|
self.msg = block_login_msg.format(settings.SECURITY_LOGIN_LIMIT_TIME)
|
||||||
super().__init__(username=username, ip=ip)
|
super().__init__(username=username, ip=ip)
|
||||||
|
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue