mirror of https://github.com/jumpserver/jumpserver
commit
a27fb18a17
|
@ -1 +1,2 @@
|
|||
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
|
||||
|
||||
REMOTE_APP_BOOT_PROGRAM_NAME = '||jmservisor'
|
||||
|
||||
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_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
|
||||
|
||||
REMOTE_APP_TYPE_CHROME_FIELDS = [
|
||||
|
@ -60,9 +38,26 @@ REMOTE_APP_TYPE_CUSTOM_FIELDS = [
|
|||
{'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_MYSQL_WORKBENCH: REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS,
|
||||
REMOTE_APP_TYPE_VMWARE_CLIENT: REMOTE_APP_TYPE_VMWARE_CLIENT_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 .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 orgs.mixins.forms import OrgModelForm
|
||||
from assets.models import SystemUser
|
||||
|
||||
from ..models import RemoteApp
|
||||
from .. import const
|
||||
|
||||
|
||||
__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(
|
||||
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(
|
||||
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(
|
||||
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(
|
||||
max_length=128, label=_('Operating parameter'), required=False
|
||||
)
|
||||
|
@ -73,51 +118,3 @@ class RemoteAppTypeCustomForm(forms.ModelForm):
|
|||
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 .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)
|
||||
path = '\"%s\"' % self.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'])
|
||||
if value is None:
|
||||
continue
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
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
|
||||
#
|
||||
|
||||
|
||||
import copy
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.serializers import AdaptedBulkListSerializer
|
||||
from common.fields.serializer import CustomMetaDictField
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
|
||||
from .. import const
|
||||
|
@ -16,72 +17,54 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class RemoteAppParamsDictField(serializers.DictField):
|
||||
"""
|
||||
RemoteApp field => params
|
||||
"""
|
||||
@staticmethod
|
||||
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 RemoteAppParamsDictField(CustomMetaDictField):
|
||||
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
|
||||
default_type = const.REMOTE_APP_TYPE_CHROME
|
||||
convert_key_remove_type_prefix = False
|
||||
convert_key_to_upper = False
|
||||
|
||||
|
||||
class RemoteAppSerializer(BulkOrgResourceModelSerializer):
|
||||
params = RemoteAppParamsDictField()
|
||||
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
|
||||
|
||||
class Meta:
|
||||
model = RemoteApp
|
||||
list_serializer_class = AdaptedBulkListSerializer
|
||||
fields = [
|
||||
'id', 'name', 'asset', 'type', 'path', 'params',
|
||||
'comment', 'created_by', 'date_created', 'asset_info',
|
||||
'get_type_display',
|
||||
'id', 'name', 'asset', 'asset_info', 'type', 'get_type_display',
|
||||
'path', 'params', 'date_created', 'created_by', 'comment',
|
||||
]
|
||||
read_only_fields = [
|
||||
'created_by', 'date_created', 'asset_info',
|
||||
'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):
|
||||
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 %}
|
||||
|
||||
{% block form %}
|
||||
<form id="appForm" method="post" class="form-horizontal">
|
||||
{% if form.non_field_errors %}
|
||||
<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" %}
|
||||
<form id="RemoteAppForm" 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">
|
||||
|
@ -57,93 +14,49 @@
|
|||
<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 }}';
|
||||
var app_path_id = '#' + '{{ form.path.id_for_label }}';
|
||||
var all_type_fields = [
|
||||
'.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(){
|
||||
|
||||
function getFormDataType(){
|
||||
return $(app_type_id+ " option:selected").val();
|
||||
}
|
||||
function initialDefaultValue(){
|
||||
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'];
|
||||
function constructFormDataParams(data){
|
||||
var params = {};
|
||||
$.each(typeList, function(index, value){
|
||||
if (data.type === value){
|
||||
for (var k in data){
|
||||
if (k.startsWith(value)){
|
||||
params[k] = data[k]
|
||||
}
|
||||
}
|
||||
var type =data.type;
|
||||
for (var k in data){
|
||||
if (k.startsWith(type)){
|
||||
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 () {
|
||||
$('.select2').select2({
|
||||
closeOnSelect: true
|
||||
});
|
||||
initialDefaultValue();
|
||||
hiddenFields();
|
||||
setDefaultValue();
|
||||
})
|
||||
.on('change', app_type_id, function(){
|
||||
hiddenFields();
|
||||
setDefaultValue();
|
||||
})
|
||||
.on("submit", "form", function (evt) {
|
||||
}).on("submit", "form", function (evt) {
|
||||
evt.preventDefault();
|
||||
var the_url = '{% url "api-applications:remote-app-list" %}';
|
||||
var redirect_to = '{% url "applications:remote-app-list" %}';
|
||||
var method = "POST";
|
||||
{% if type == "update" %}
|
||||
{% if api_action == "update" %}
|
||||
the_url = '{% url "api-applications:remote-app-detail" object.id %}';
|
||||
method = "PUT";
|
||||
{% endif %}
|
||||
var form = $("form");
|
||||
var data = form.serializeObject();
|
||||
data["params"] = constructParams(data);
|
||||
var data = getFormData(form);
|
||||
var props = {
|
||||
url: the_url,
|
||||
data: data,
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
@ -102,4 +97,4 @@ $(document).ready(function () {
|
|||
objectDelete($this, name, the_url, redirect_url);
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -6,8 +6,16 @@
|
|||
{% endblock %}
|
||||
{% block table_search %}{% endblock %}
|
||||
{% block table_container %}
|
||||
<div class="uc pull-left m-r-5">
|
||||
<a href="{% url 'applications:remote-app-create' %}" class="btn btn-sm btn-primary"> {% trans "Create RemoteApp" %} </a>
|
||||
<div class="btn-group uc pull-left m-r-5">
|
||||
<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>
|
||||
<table class="table table-striped table-bordered table-hover " id="remote_app_list_table" >
|
||||
<thead>
|
||||
|
@ -61,7 +69,7 @@ function initTable() {
|
|||
{data: "get_type_display", orderable: false},
|
||||
{data: "asset_info", orderable: false},
|
||||
{data: "comment"},
|
||||
{data: "id", orderable: false}
|
||||
{data: "id", orderable: false, width: "120px"}
|
||||
],
|
||||
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.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app')
|
||||
router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app')
|
||||
|
||||
urlpatterns = [
|
||||
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
|
||||
]
|
||||
|
||||
old_version_urlpatterns = [
|
||||
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>/', views.RemoteAppDetailView.as_view(), name='remote-app-detail'),
|
||||
# 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 .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
|
||||
#
|
||||
|
||||
from django.http import Http404
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.generic.edit import CreateView, UpdateView
|
||||
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.const import create_success_msg, update_success_msg
|
||||
|
||||
from ..models import RemoteApp
|
||||
from .. import forms
|
||||
from .. import forms, const
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -30,53 +27,79 @@ class RemoteAppListView(PermissionsMixin, TemplateView):
|
|||
context = {
|
||||
'app': _('Applications'),
|
||||
'action': _('RemoteApp list'),
|
||||
'type_choices': const.REMOTE_APP_TYPE_CHOICES,
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class RemoteAppCreateView(PermissionsMixin, SuccessMessageMixin, CreateView):
|
||||
class BaseRemoteAppCreateUpdateView:
|
||||
template_name = 'applications/remote_app_create_update.html'
|
||||
model = RemoteApp
|
||||
form_class = forms.RemoteAppCreateUpdateForm
|
||||
success_url = reverse_lazy('applications:remote-app-list')
|
||||
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):
|
||||
context = {
|
||||
'app': _('Applications'),
|
||||
'action': _('Create RemoteApp'),
|
||||
'type': 'create'
|
||||
'api_action': 'create'
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
return create_success_msg % ({'name': cleaned_data['name']})
|
||||
def get_type(self):
|
||||
tp = self.request.GET.get("type")
|
||||
if tp:
|
||||
return tp.lower()
|
||||
return super().get_type()
|
||||
|
||||
|
||||
class RemoteAppUpdateView(PermissionsMixin, SuccessMessageMixin, UpdateView):
|
||||
template_name = 'applications/remote_app_create_update.html'
|
||||
model = RemoteApp
|
||||
form_class = forms.RemoteAppCreateUpdateForm
|
||||
success_url = reverse_lazy('applications:remote-app-list')
|
||||
permission_classes = [IsOrgAdmin]
|
||||
class RemoteAppUpdateView(BaseRemoteAppCreateUpdateView,
|
||||
PermissionsMixin, UpdateView):
|
||||
|
||||
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):
|
||||
context = {
|
||||
'app': _('Applications'),
|
||||
'action': _('Update RemoteApp'),
|
||||
'type': 'update'
|
||||
'api_action': 'update'
|
||||
}
|
||||
kwargs.update(context)
|
||||
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):
|
||||
template_name = 'applications/remote_app_detail.html'
|
||||
|
|
|
@ -2,6 +2,7 @@ from .admin_user import *
|
|||
from .asset import *
|
||||
from .label import *
|
||||
from .system_user import *
|
||||
from .system_user_relation import *
|
||||
from .node import *
|
||||
from .domain import *
|
||||
from .cmd_filter import *
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import Count
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.response import Response
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
|
@ -44,6 +45,11 @@ class AdminUserViewSet(OrgBulkModelViewSet):
|
|||
serializer_class = serializers.AdminUserSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
queryset = queryset.annotate(_assets_amount=Count('assets'))
|
||||
return queryset
|
||||
|
||||
|
||||
class AdminUserAuthApi(generics.UpdateAPIView):
|
||||
model = AdminUser
|
||||
|
|
|
@ -4,24 +4,27 @@
|
|||
import random
|
||||
|
||||
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 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 import generics
|
||||
from ..models import Asset, Node
|
||||
from ..models import Asset, Node, Platform
|
||||
from .. import serializers
|
||||
from ..tasks import update_asset_hardware_info_manual, \
|
||||
test_asset_connectivity_manual
|
||||
from ..tasks import (
|
||||
update_asset_hardware_info_manual, test_asset_connectivity_manual
|
||||
)
|
||||
from ..filters import AssetByNodeFilterBackend, LabelFilterBackend
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
__all__ = [
|
||||
'AssetViewSet',
|
||||
'AssetViewSet', 'AssetPlatformRetrieveApi',
|
||||
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi',
|
||||
'AssetGatewayApi',
|
||||
'AssetGatewayApi', 'AssetPlatformViewSet',
|
||||
]
|
||||
|
||||
|
||||
|
@ -53,6 +56,34 @@ class AssetViewSet(OrgBulkModelViewSet):
|
|||
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):
|
||||
"""
|
||||
Refresh asset hardware info
|
||||
|
|
|
@ -114,7 +114,7 @@ class AssetUserExportViewSet(AssetUserViewSet):
|
|||
permission_classes = [IsOrgAdminOrAppUser]
|
||||
|
||||
def get_permissions(self):
|
||||
if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
if settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||
return super().get_permissions()
|
||||
|
||||
|
@ -124,7 +124,7 @@ class AssetUserAuthInfoApi(generics.RetrieveAPIView):
|
|||
permission_classes = [IsOrgAdminOrAppUser]
|
||||
|
||||
def get_permissions(self):
|
||||
if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
if settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||
return super().get_permissions()
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ class NodeChildrenAsTreeApi(NodeChildrenApi):
|
|||
if not include_assets:
|
||||
return queryset
|
||||
assets = self.instance.get_assets().only(
|
||||
"id", "hostname", "ip", 'platform', "os",
|
||||
"id", "hostname", "ip", "os",
|
||||
"org_id", "protocols",
|
||||
)
|
||||
for asset in assets:
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
# limitations under the License.
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.conf import settings
|
||||
from rest_framework.response import Response
|
||||
from django.db.models import Count
|
||||
|
||||
from common.serializers import CeleryTaskSerializer
|
||||
from common.utils import get_logger
|
||||
|
@ -50,6 +50,11 @@ class SystemUserViewSet(OrgBulkModelViewSet):
|
|||
serializer_class = serializers.SystemUserSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
queryset = queryset.annotate(_assets_amount=Count('assets'))
|
||||
return queryset
|
||||
|
||||
|
||||
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:
|
||||
in_kwargs[k] = v
|
||||
for k in in_kwargs:
|
||||
kwargs.pop(k)
|
||||
kwargs.pop(k, None)
|
||||
|
||||
if len(in_kwargs) == 0:
|
||||
return self
|
||||
|
@ -56,7 +56,7 @@ class AssetUserQuerySet(list):
|
|||
v = [str(i) for i in v]
|
||||
if isinstance(attr, uuid.UUID):
|
||||
attr = str(attr)
|
||||
if v in attr:
|
||||
if attr in v:
|
||||
matched = True
|
||||
if matched:
|
||||
queryset.append(i)
|
||||
|
@ -68,11 +68,12 @@ class AssetUserQuerySet(list):
|
|||
real = []
|
||||
for k, v in kwargs.items():
|
||||
wanted.append(v)
|
||||
value = getattr(obj, k)
|
||||
value = getattr(obj, k, None)
|
||||
if isinstance(value, uuid.UUID):
|
||||
value = str(value)
|
||||
real.append(value)
|
||||
return wanted == real
|
||||
kwargs = {k: v for k, v in kwargs.items() if k.find('__in') == -1}
|
||||
if len(kwargs) > 0:
|
||||
queryset = AssetUserQuerySet([i for i in self if filter_it(i)])
|
||||
else:
|
||||
|
|
|
@ -5,3 +5,4 @@ from .label import *
|
|||
from .user import *
|
||||
from .domain 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 orgs.mixins.forms import OrgModelForm
|
||||
|
||||
from ..models import Asset, Node
|
||||
from ..models import Asset
|
||||
from ..const import GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_HELP_TEXT
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
__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):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.data:
|
||||
return
|
||||
self.set_platform_to_name()
|
||||
self.set_fields_queryset()
|
||||
|
||||
def set_fields_queryset(self):
|
||||
nodes_field = self.fields['nodes']
|
||||
nodes_choices = []
|
||||
if self.instance:
|
||||
nodes_field.choices = [(n.id, n.full_value) for n in
|
||||
self.instance.nodes.all()]
|
||||
else:
|
||||
nodes_field.choices = []
|
||||
nodes_choices = [
|
||||
(n.id, n.full_value) for n in
|
||||
self.instance.nodes.all()
|
||||
]
|
||||
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):
|
||||
nodes_field = self.fields['nodes']
|
||||
|
@ -49,7 +59,7 @@ class AssetCreateForm(OrgModelForm):
|
|||
fields = [
|
||||
'hostname', 'ip', 'public_ip', 'protocols', 'comment',
|
||||
'nodes', 'is_active', 'admin_user', 'labels', 'platform',
|
||||
'domain',
|
||||
'domain', 'number',
|
||||
]
|
||||
widgets = {
|
||||
'nodes': forms.SelectMultiple(attrs={
|
||||
|
@ -64,52 +74,8 @@ class AssetCreateForm(OrgModelForm):
|
|||
'domain': forms.Select(attrs={
|
||||
'class': 'select2', 'data-placeholder': _('Domain')
|
||||
}),
|
||||
}
|
||||
labels = {
|
||||
'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')
|
||||
'platform': forms.Select(attrs={
|
||||
'class': 'select2', 'data-placeholder': _('Platform')
|
||||
}),
|
||||
}
|
||||
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.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 .utils import Connectivity
|
||||
|
||||
__all__ = ['Asset', 'ProtocolsMixin']
|
||||
__all__ = ['Asset', 'ProtocolsMixin', 'Platform']
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -37,6 +39,13 @@ def default_node():
|
|||
return None
|
||||
|
||||
|
||||
class AssetManager(OrgManager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().annotate(
|
||||
platform_base=models.F('platform__base')
|
||||
)
|
||||
|
||||
|
||||
class AssetQuerySet(models.QuerySet):
|
||||
def active(self):
|
||||
return self.filter(is_active=True)
|
||||
|
@ -119,6 +128,47 @@ class NodesRelationMixin:
|
|||
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):
|
||||
# Important
|
||||
PLATFORM_CHOICES = (
|
||||
|
@ -138,9 +188,8 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
|||
choices=ProtocolsMixin.PROTOCOL_CHOICES,
|
||||
verbose_name=_('Protocol'))
|
||||
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
||||
|
||||
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)
|
||||
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
|
||||
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'))
|
||||
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
|
||||
|
||||
objects = OrgManager.from_queryset(AssetQuerySet)()
|
||||
objects = AssetManager.from_queryset(AssetQuerySet)()
|
||||
_connectivity = None
|
||||
|
||||
def __str__(self):
|
||||
|
@ -190,20 +239,18 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
|||
return False, warning
|
||||
return True, warning
|
||||
|
||||
@lazyproperty
|
||||
def platform_base(self):
|
||||
return self.platform.base
|
||||
|
||||
def is_windows(self):
|
||||
if self.platform in ("Windows", "Windows2016"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return self.platform.is_windows()
|
||||
|
||||
def is_unixlike(self):
|
||||
if self.platform not in ("Windows", "Windows2016", "Other"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return self.platform.is_unixlike()
|
||||
|
||||
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
|
||||
def cpu_info(self):
|
||||
|
@ -264,9 +311,9 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
|||
def as_tree_node(self, parent_node):
|
||||
from common.tree import TreeNode
|
||||
icon_skin = 'file'
|
||||
if self.platform.lower() == 'windows':
|
||||
if self.platform_base.lower() == 'windows':
|
||||
icon_skin = 'windows'
|
||||
elif self.platform.lower() == 'linux':
|
||||
elif self.platform_base.lower() == 'linux':
|
||||
icon_skin = 'linux'
|
||||
data = {
|
||||
'id': str(self.id),
|
||||
|
@ -283,7 +330,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
|
|||
'hostname': self.hostname,
|
||||
'ip': self.ip,
|
||||
'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 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 import fields
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
from .utils import private_key_validator, Connectivity
|
||||
|
||||
signer = get_signer()
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -41,6 +40,7 @@ class AssetUser(OrgModelMixin):
|
|||
ASSET_USER_CACHE_TIME = 3600 * 24
|
||||
|
||||
_prefer = "system_user"
|
||||
_assets_amount = None
|
||||
|
||||
@property
|
||||
def private_key_obj(self):
|
||||
|
@ -143,6 +143,8 @@ class AssetUser(OrgModelMixin):
|
|||
|
||||
@property
|
||||
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)
|
||||
cached = cache.get(cache_key)
|
||||
if not cached:
|
||||
|
|
|
@ -10,14 +10,13 @@ from django.db.models import Q
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
||||
from common.utils import get_signer
|
||||
from common.utils import signer
|
||||
from .base import AssetUser
|
||||
from .asset import Asset
|
||||
|
||||
|
||||
__all__ = ['AdminUser', 'SystemUser']
|
||||
logger = logging.getLogger(__name__)
|
||||
signer = get_signer()
|
||||
|
||||
|
||||
class AdminUser(AssetUser):
|
||||
|
@ -93,11 +92,13 @@ class SystemUser(AssetUser):
|
|||
PROTOCOL_RDP = 'rdp'
|
||||
PROTOCOL_TELNET = 'telnet'
|
||||
PROTOCOL_VNC = 'vnc'
|
||||
PROTOCOL_MYSQL = 'mysql'
|
||||
PROTOCOL_CHOICES = (
|
||||
(PROTOCOL_SSH, 'ssh'),
|
||||
(PROTOCOL_RDP, 'rdp'),
|
||||
(PROTOCOL_TELNET, 'telnet'),
|
||||
(PROTOCOL_VNC, 'vnc'),
|
||||
(PROTOCOL_MYSQL, 'mysql'),
|
||||
)
|
||||
|
||||
LOGIN_AUTO = 'auto'
|
||||
|
@ -134,6 +135,18 @@ class SystemUser(AssetUser):
|
|||
else:
|
||||
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
|
||||
def cmd_filter_rules(self):
|
||||
from .cmd_filter import CommandFilterRule
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from common.serializers import AdaptedBulkListSerializer
|
||||
from ..models import Asset, Node, Label
|
||||
from ..models import Asset, Node, Label, Platform
|
||||
from ..const import (
|
||||
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN,
|
||||
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_ERROR_MSG
|
||||
|
@ -16,7 +16,8 @@ from .base import ConnectivitySerializer
|
|||
|
||||
__all__ = [
|
||||
'AssetSerializer', 'AssetSimpleSerializer',
|
||||
'ProtocolsField',
|
||||
'ProtocolsField', 'PlatformSerializer',
|
||||
'AssetDetailSerializer',
|
||||
]
|
||||
|
||||
|
||||
|
@ -65,6 +66,9 @@ class ProtocolsField(serializers.ListField):
|
|||
|
||||
|
||||
class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||
platform = serializers.SlugRelatedField(
|
||||
slug_field='name', queryset=Platform.objects.all(), label=_("Platform")
|
||||
)
|
||||
protocols = ProtocolsField(label=_('Protocols'), required=False)
|
||||
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
|
||||
|
||||
|
@ -111,7 +115,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
|
|||
queryset = queryset.prefetch_related(
|
||||
Prefetch('nodes', queryset=Node.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
|
||||
|
||||
def compatible_with_old_protocol(self, validated_data):
|
||||
|
@ -139,6 +143,21 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
|
|||
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):
|
||||
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 common.serializers import AdaptedBulkListSerializer
|
||||
from common.mixins.serializers import BulkSerializerMixin
|
||||
from common.utils import ssh_pubkey_gen
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from assets.models import Node
|
||||
from ..models import SystemUser
|
||||
from ..const import (
|
||||
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN,
|
||||
|
@ -13,6 +15,12 @@ from ..const import (
|
|||
)
|
||||
from .base import AuthSerializer, AuthSerializerMixin
|
||||
|
||||
__all__ = [
|
||||
'SystemUserSerializer', 'SystemUserAuthSerializer',
|
||||
'SystemUserSimpleSerializer', 'SystemUserAssetRelationSerializer',
|
||||
'SystemUserNodeRelationSerializer',
|
||||
]
|
||||
|
||||
|
||||
class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||
"""
|
||||
|
@ -95,8 +103,12 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
|
||||
def validate(self, attrs):
|
||||
username = attrs.get("username", "manual")
|
||||
auto_gen_key = attrs.pop("auto_generate_key", False)
|
||||
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:
|
||||
password = SystemUser.gen_password()
|
||||
attrs["password"] = password
|
||||
|
@ -111,7 +123,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
public_key = ssh_pubkey_gen(private_key, password=password,
|
||||
username=username)
|
||||
attrs["public_key"] = public_key
|
||||
attrs.pop("auto_generate_key", None)
|
||||
return attrs
|
||||
|
||||
@classmethod
|
||||
|
@ -143,4 +154,43 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
|||
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="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-content mailbox-content" style="padding-top: 0;padding-left: 1px">
|
||||
<div class="file-manager ">
|
||||
|
@ -37,7 +37,7 @@
|
|||
</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">
|
||||
<table class="table table-striped table-bordered table-hover " id="asset_list_modal_table" style="width: 100%">
|
||||
<thead>
|
||||
|
@ -190,6 +190,16 @@ function setAssetModalOptions(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(){
|
||||
|
||||
|
|
|
@ -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) {
|
||||
var offset = $("#tree-node-id").offset();
|
||||
var scrollTop = document.querySelector('.treebox').scrollTop;
|
||||
x -= offset.left;
|
||||
y -= offset.top;
|
||||
y -= offset.top + scrollTop;
|
||||
x += document.body.scrollLeft;
|
||||
y += document.body.scrollTop + document.documentElement.scrollTop;
|
||||
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -99,7 +95,7 @@ function autoLoginModeProtocol() {
|
|||
// 协议+自动登录模式字段控制
|
||||
$('#auth_title_id').removeClass('hidden');
|
||||
var protocol = $(protocol_id + " option:selected").text();
|
||||
if (protocol === 'rdp') {
|
||||
if (['rdp'].indexOf(protocol) !== -1) {
|
||||
authFieldsDisplay();
|
||||
$(auto_generate_key).closest('.form-group').removeClass('hidden');
|
||||
$(private_key_id).closest('.form-group').addClass('hidden');
|
||||
|
@ -109,7 +105,7 @@ function autoLoginModeProtocol() {
|
|||
$(sudo_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');
|
||||
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
||||
$(private_key_id).closest('.form-group').addClass('hidden');
|
||||
|
@ -145,7 +141,7 @@ function manualLoginModeProtocol() {
|
|||
// 协议+手动登录模式字段控制
|
||||
$('#auth_title_id').addClass('hidden');
|
||||
var protocol = $(protocol_id + " option:selected").text();
|
||||
if (protocol === 'rdp') {
|
||||
if (['rdp'].indexOf(protocol) !== -1) {
|
||||
$('.auth-fields').addClass('hidden');
|
||||
$(auto_generate_key).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');
|
||||
$(shell_id).closest('.form-group').addClass('hidden');
|
||||
}
|
||||
else if (protocol === 'vnc') {
|
||||
else if (['vnc', 'mysql'].indexOf(protocol) !== -1) {
|
||||
$('.auth-fields').addClass('hidden');
|
||||
$(auto_generate_key).closest('.form-group').addClass('hidden');
|
||||
$(password_id).closest('.form-group').addClass('hidden');
|
||||
|
@ -247,4 +243,4 @@ $(document).ready(function () {
|
|||
})
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -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 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -87,4 +83,4 @@ $(document).ready(function () {
|
|||
})
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<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. '%}
|
||||
{% endblock %}
|
||||
{% block table_search %}
|
||||
<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>
|
||||
{% include '_csv_import_export.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block table_container %}
|
||||
|
@ -42,9 +21,6 @@
|
|||
<th class="text-center">{% trans 'Name' %}</th>
|
||||
<th class="text-center">{% trans 'Username' %}</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 'Action' %}</th>
|
||||
</tr>
|
||||
|
@ -52,8 +28,6 @@
|
|||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
{% include 'assets/_admin_user_import_modal.html' %}
|
||||
{% include 'assets/_admin_user_update_modal.html' %}
|
||||
{% endblock %}
|
||||
{% block content_bottom_left %}{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
|
@ -77,17 +51,16 @@ function initTable() {
|
|||
columns: [
|
||||
{data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount", orderable: false},
|
||||
{#{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 admin_user_table
|
||||
return jumpserver.initServerSideDataTable(options);
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
initTable();
|
||||
admin_user_table = initTable();
|
||||
initCsvImportExport(admin_user_table, "{% trans "Admin user" %}")
|
||||
})
|
||||
|
||||
.on('click', '.btn_admin_user_delete', function () {
|
||||
var $this = $(this);
|
||||
var $data_table = $("#admin_user_list_table").DataTable();
|
||||
|
@ -100,69 +73,5 @@ $(document).ready(function(){
|
|||
}, 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>
|
||||
{% endblock %}
|
||||
|
|
|
@ -32,13 +32,7 @@
|
|||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
$("#id_assets").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();
|
||||
}
|
||||
})
|
||||
initAssetTreeModel("#id_assets");
|
||||
}).on('click', '.field-tag', function() {
|
||||
changeField(this);
|
||||
}).on('click', '#change_all', function () {
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% 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">
|
||||
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
||||
<script src='{% static "js/plugins/sweetalert/sweetalert.min.js" %}'></script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -1,132 +1,54 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends '_base_asset_tree_list.html' %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% 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' %}
|
||||
{# </div>#}
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_head_css_js %}
|
||||
{# <link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">#}
|
||||
{# <script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>#}
|
||||
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
||||
<style type="text/css">
|
||||
div#rMenu {
|
||||
position:absolute;
|
||||
visibility:hidden;
|
||||
text-align: left;
|
||||
{#top: 100%;#}
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
{#float: left;#}
|
||||
padding: 0 0;
|
||||
margin: 2px 0 0;
|
||||
list-style: none;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
.dataTables_wrapper .dataTables_processing {
|
||||
opacity: .9;
|
||||
border: none;
|
||||
}
|
||||
div#rMenu li{
|
||||
margin: 1px 0;
|
||||
cursor: pointer;
|
||||
list-style: none outside none;
|
||||
}
|
||||
.dropdown a:hover {
|
||||
background-color: #f1f1f1
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content">
|
||||
<div class="row">
|
||||
<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>
|
||||
{% block table_container %}
|
||||
<div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
|
||||
{% include '_csv_import_export.html' %}
|
||||
<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>
|
||||
|
||||
{% include 'assets/_asset_update_modal.html' %}
|
||||
{% include 'assets/_asset_import_modal.html' %}
|
||||
{% include 'assets/_asset_list_modal.html' %}
|
||||
{% include 'assets/_node_detail_modal.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_foot_js %}
|
||||
|
@ -177,7 +99,7 @@ function initTable() {
|
|||
data: "connectivity",
|
||||
orderable: false,
|
||||
width: '60px'
|
||||
}, {data: "id", orderable: false, width: "100px"}
|
||||
}, {data: "id", orderable: false, width: "120px"}
|
||||
],
|
||||
op_html: $('#actions').html()
|
||||
};
|
||||
|
@ -198,26 +120,12 @@ function initTree() {
|
|||
<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_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) {
|
||||
current_node = treeNode;
|
||||
current_node_id = treeNode.meta.node.id;
|
||||
|
@ -258,7 +166,8 @@ function onAssetModalConfirmAddAssetToNode(table) {
|
|||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
initTable();
|
||||
asset_table = initTable();
|
||||
initCsvImportExport(asset_table, "{% trans "Asset" %}");
|
||||
initTree();
|
||||
|
||||
if(getCookie('show_current_asset') === '1'){
|
||||
|
@ -279,81 +188,6 @@ $(document).ready(function(){
|
|||
$("#asset_list_table_filter input").val(val);
|
||||
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 () {
|
||||
var url = "{% url 'assets:asset-create' %}";
|
||||
if (current_node_id) {
|
||||
|
@ -382,9 +216,6 @@ $(document).ready(function(){
|
|||
var data = {
|
||||
'resources': id_list
|
||||
};
|
||||
function refreshPage() {
|
||||
setTimeout( function () {window.location.reload();}, 300);
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
asset_table.ajax.reload();
|
||||
|
@ -550,6 +381,30 @@ $(document).ready(function(){
|
|||
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>
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
|
|
@ -62,7 +62,7 @@ function initTable() {
|
|||
columns: [
|
||||
{data: "id"}, {data: "name" }, {data: "rules", orderable: false},
|
||||
{data: "system_users", orderable: false}, {data: "comment"},
|
||||
{data: "id", orderable: false, width: "100px"}
|
||||
{data: "id", orderable: false, width: "120px"}
|
||||
],
|
||||
op_html: $('#actions').html()
|
||||
};
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -91,4 +87,4 @@ $(document).ready(function(){
|
|||
formSubmit(props);
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
|
|
@ -25,9 +25,7 @@
|
|||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2().off("select2:open");
|
||||
}).on('click', '.select2-selection__rendered', function (e) {
|
||||
e.preventDefault();
|
||||
$("#asset_list_modal").modal();
|
||||
initAssetTreeModel('#id_assets');
|
||||
})
|
||||
.on("submit", "form", function (evt) {
|
||||
evt.preventDefault();
|
||||
|
@ -51,4 +49,4 @@ $(document).ready(function () {
|
|||
formSubmit(props);
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
@ -138,4 +133,4 @@ $(document).ready(function(){
|
|||
})
|
||||
;
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
|
|
@ -55,7 +55,7 @@ function initTable() {
|
|||
ajax_url: '{% url "api-assets:domain-list" %}',
|
||||
columns: [
|
||||
{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()
|
||||
};
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -125,4 +121,4 @@ $(document).ready(function(){
|
|||
protocolChange();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -29,9 +29,7 @@ $(document).ready(function () {
|
|||
$('.select2').select2({
|
||||
closeOnSelect: false
|
||||
})
|
||||
}).on('click', '.select2-selection__rendered', function (e) {
|
||||
e.preventDefault();
|
||||
$("#asset_list_modal").modal();
|
||||
initAssetTreeModel("#id_assets");
|
||||
})
|
||||
.on("submit", "form", function (evt) {
|
||||
evt.preventDefault();
|
||||
|
@ -55,4 +53,4 @@ $(document).ready(function () {
|
|||
formSubmit(props);
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -45,7 +45,7 @@ function initTable() {
|
|||
columns: [
|
||||
{data: "id"}, {data: "name" }, {data: "value" },
|
||||
{data: "asset_count", orderable: false},
|
||||
{data: "id", orderable: false, width: "100px"}
|
||||
{data: "id", orderable: false, width: "120px"}
|
||||
],
|
||||
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 %}
|
||||
|
||||
{% 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>
|
||||
<style>
|
||||
.table.node_edit {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
@ -98,15 +102,8 @@
|
|||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
|
||||
{% for node in system_user.nodes.all|sort %}
|
||||
<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 %}
|
||||
<table class="table" id="node_list_table">
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -120,91 +117,115 @@
|
|||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
var assetsRelationUrl = "{% url 'api-assets:system-users-assets-relation-list' %}";
|
||||
var nodesRelationUrl = "{% url 'api-assets:system-users-nodes-relation-list' %}";
|
||||
|
||||
function updateSystemUserNode(nodes) {
|
||||
var the_url = "{% url 'api-assets:system-user-detail' pk=system_user.id %}";
|
||||
var body = {
|
||||
nodes: Object.assign([], nodes)
|
||||
};
|
||||
var success = function(data) {
|
||||
// remove all the selected groups from select > option and rendered ul element;
|
||||
$('.select2-selection__rendered').empty();
|
||||
$('#node_selected').val('');
|
||||
$.map(jumpserver.nodes_selected, function(node_name, index) {
|
||||
$('#opt_' + index).remove();
|
||||
// change tr html of user groups.
|
||||
$('.node_edit tbody').append(
|
||||
'<tr>' +
|
||||
'<td><b class="bdg_node" data-gid="' + index + '">' + node_name + '</b></td>' +
|
||||
'<td><button class="btn btn-danger btn-xs pull-right btn-remove-from-node" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||
'</tr>'
|
||||
)
|
||||
});
|
||||
// clear jumpserver.nodes_selected
|
||||
jumpserver.nodes_selected = {};
|
||||
};
|
||||
function getRelationUrl(type) {
|
||||
var theUrl = "";
|
||||
switch (type) {
|
||||
case "asset":
|
||||
theUrl = assetsRelationUrl;
|
||||
break;
|
||||
case "node":
|
||||
theUrl = nodesRelationUrl;
|
||||
break;
|
||||
}
|
||||
return theUrl;
|
||||
}
|
||||
|
||||
function addObjects(objectsId, type) {
|
||||
if (!objectsId || objectsId.length === 0) {
|
||||
return
|
||||
}
|
||||
var theUrl = getRelationUrl(type);
|
||||
var body = [];
|
||||
objectsId.forEach(function (v) {
|
||||
var data = {systemuser: "{{ object.id }}"};
|
||||
data[type] = v;
|
||||
body.push(data)
|
||||
});
|
||||
requestApi({
|
||||
url: the_url,
|
||||
url: theUrl,
|
||||
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
|
||||
});
|
||||
}
|
||||
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 () {
|
||||
$('.select2').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];
|
||||
});
|
||||
$('.select2').select2();
|
||||
nodesSelect2Init(".nodes-select2");
|
||||
assetUserListUrl = setUrlParam(assetUserListUrl, "system_user_id", "{{ system_user.id }}");
|
||||
needPush = true;
|
||||
initAssetUserTable();
|
||||
|
||||
initNodeTable();
|
||||
})
|
||||
.on('click', '.btn-remove-from-node', function() {
|
||||
var $this = $(this);
|
||||
var $tr = $this.closest('tr');
|
||||
var $badge = $tr.find('.bdg_node');
|
||||
var gid = $badge.data('gid');
|
||||
var node_name = $badge.html() || $badge.text();
|
||||
$('#groups_selected').append(
|
||||
'<option value="' + gid + '" id="opt_' + gid + '">' + node_name + '</option>'
|
||||
);
|
||||
$tr.remove();
|
||||
var nodes = $('.bdg_node').map(function () {
|
||||
return $(this).data('gid');
|
||||
}).get();
|
||||
updateSystemUserNode(nodes);
|
||||
var nodeId = $(this).data('uid');
|
||||
var success = function() {
|
||||
var $tr = $this.closest('tr');
|
||||
$tr.remove();
|
||||
};
|
||||
removeObject(nodeId, "node", success)
|
||||
})
|
||||
.on('click', '#btn-add-to-node', function() {
|
||||
if (Object.keys(jumpserver.nodes_selected).length === 0) {
|
||||
return false;
|
||||
}
|
||||
var nodes = $('.bdg_node').map(function() {
|
||||
return $(this).data('gid');
|
||||
}).get();
|
||||
$.map(jumpserver.nodes_selected, function(value, index) {
|
||||
nodes.push(index);
|
||||
});
|
||||
updateSystemUserNode(nodes);
|
||||
var nodes = $("#node_selected").val();
|
||||
addObjects(nodes, "node");
|
||||
})
|
||||
.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) {
|
||||
alert(data)
|
||||
};
|
||||
var success = function (data) {
|
||||
var task_id = data.task;
|
||||
showCeleryTaskLog(task_id);
|
||||
var taskId = data.task;
|
||||
showCeleryTaskLog(taskId);
|
||||
};
|
||||
requestApi({
|
||||
url: the_url,
|
||||
url: theUrl,
|
||||
error: error,
|
||||
method: 'GET',
|
||||
success: success
|
||||
|
@ -213,39 +234,37 @@ $(document).ready(function () {
|
|||
.on('click', '.btn-push-auth', function () {
|
||||
var $this = $(this);
|
||||
var asset_id = $this.data('asset');
|
||||
var the_url = "{% url 'api-assets:system-user-push-to-asset' pk=object.id aid=DEFAULT_PK %}";
|
||||
the_url = the_url.replace("{{ DEFAULT_PK }}", asset_id);
|
||||
var theUrl = "{% url 'api-assets:system-user-push-to-asset' pk=object.id aid=DEFAULT_PK %}";
|
||||
theUrl = theUrl.replace("{{ DEFAULT_PK }}", asset_id);
|
||||
var success = function (data) {
|
||||
var task_id = data.task;
|
||||
showCeleryTaskLog(task_id);
|
||||
var taskId = data.task;
|
||||
showCeleryTaskLog(taskId);
|
||||
};
|
||||
var error = function (data) {
|
||||
alert(data)
|
||||
};
|
||||
requestApi({
|
||||
url: the_url,
|
||||
url: theUrl,
|
||||
method: 'GET',
|
||||
success: success,
|
||||
error: error
|
||||
})
|
||||
})
|
||||
.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) {
|
||||
alert(data)
|
||||
};
|
||||
var success = function (data) {
|
||||
var task_id = data.task;
|
||||
showCeleryTaskLog(task_id);
|
||||
var taskId = data.task;
|
||||
showCeleryTaskLog(taskId);
|
||||
};
|
||||
requestApi({
|
||||
url: the_url,
|
||||
url: theUrl,
|
||||
error: error,
|
||||
method: 'GET',
|
||||
success: success
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
{% load static %}
|
||||
{% 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 %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
|
@ -17,11 +12,13 @@
|
|||
<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>
|
||||
</li>
|
||||
{% if system_user.can_perm_to_asset %}
|
||||
<li>
|
||||
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets' %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<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>
|
||||
</li>
|
||||
|
@ -144,6 +141,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if system_user.is_need_test_asset_connective %}
|
||||
<tr>
|
||||
<td width="50%">{% trans 'Test assets connective' %}:</td>
|
||||
<td>
|
||||
|
@ -152,13 +150,14 @@
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</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="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
|
|
|
@ -8,28 +8,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block table_search %}
|
||||
<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>
|
||||
{% include '_csv_import_export.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block table_container %}
|
||||
|
@ -57,8 +36,6 @@
|
|||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
{% include 'assets/_system_user_import_modal.html' %}
|
||||
{% include 'assets/_system_user_update_modal.html' %}
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
|
@ -85,18 +62,17 @@ function initTable() {
|
|||
columns: [
|
||||
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"},
|
||||
{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()
|
||||
};
|
||||
system_user_table = jumpserver.initServerSideDataTable(options);
|
||||
return system_user_table
|
||||
return jumpserver.initServerSideDataTable(options);
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
initTable();
|
||||
system_user_table = initTable();
|
||||
initCsvImportExport(system_user_table, "{% trans 'System user' %}")
|
||||
})
|
||||
|
||||
.on('click', '.btn_admin_user_delete', function () {
|
||||
var $this = $(this);
|
||||
var $data_table = $('#cluster_list_table').DataTable();
|
||||
|
@ -108,125 +84,6 @@ $(document).ready(function(){
|
|||
$data_table.ajax.reload();
|
||||
}, 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>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ app_name = 'assets'
|
|||
|
||||
router = BulkRouter()
|
||||
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'system-users', api.SystemUserViewSet, 'system-user')
|
||||
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'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
||||
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.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
||||
|
@ -35,6 +38,8 @@ urlpatterns = [
|
|||
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
|
||||
path('assets/<uuid:pk>/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/',
|
||||
api.AssetUserAuthInfoApi.as_view(), name='asset-user-auth-info'),
|
||||
|
|
|
@ -16,6 +16,11 @@ urlpatterns = [
|
|||
# Asset user view
|
||||
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
|
||||
path('user-asset/', views.UserAssetListView.as_view(), name='user-asset-list'),
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# coding:utf-8
|
||||
from .asset import *
|
||||
from .platform import *
|
||||
from .system_user import *
|
||||
from .admin_user import *
|
||||
from .label import *
|
||||
|
|
|
@ -74,7 +74,7 @@ class UserAssetListView(PermissionsMixin, TemplateView):
|
|||
|
||||
class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
|
||||
model = Asset
|
||||
form_class = forms.AssetCreateForm
|
||||
form_class = forms.AssetCreateUpdateForm
|
||||
template_name = 'assets/asset_create.html'
|
||||
success_url = reverse_lazy('assets:asset-list')
|
||||
permission_classes = [IsOrgAdmin]
|
||||
|
@ -110,7 +110,7 @@ class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
|
|||
|
||||
class AssetUpdateView(PermissionsMixin, UpdateView):
|
||||
model = Asset
|
||||
form_class = forms.AssetUpdateForm
|
||||
form_class = forms.AssetCreateUpdateForm
|
||||
template_name = 'assets/asset_update.html'
|
||||
success_url = reverse_lazy('assets:asset-list')
|
||||
permission_classes = [IsOrgAdmin]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.views.generic import TemplateView, CreateView, \
|
||||
UpdateView, DeleteView, DetailView
|
||||
from django.views.generic import (
|
||||
TemplateView, CreateView, UpdateView, DeleteView, DetailView
|
||||
)
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
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
|
||||
serializer_class = FTPLogSerializer
|
||||
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):
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
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"))
|
||||
system_user = models.CharField(max_length=128, verbose_name=_("System user"))
|
||||
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"))
|
||||
resource_type = models.CharField(max_length=64, verbose_name=_("Resource Type"))
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -50,7 +50,7 @@ class PasswordChangeLog(models.Model):
|
|||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
user = models.CharField(max_length=128, verbose_name=_('User'))
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -11,14 +11,15 @@ from rest_framework.request import Request
|
|||
from jumpserver.utils import current_request
|
||||
from common.utils import get_request_ip, get_logger, get_syslogger
|
||||
from users.models import User
|
||||
from users.signals import post_user_change_password
|
||||
from authentication.signals import post_auth_failed, post_auth_success
|
||||
from terminal.models import Session, Command
|
||||
from terminal.backends.command.serializers import SessionCommandSerializer
|
||||
from . import models, serializers
|
||||
from .tasks import write_login_log_async
|
||||
from common.utils.encode import model_to_json
|
||||
from .utils import write_login_log
|
||||
from . import models
|
||||
|
||||
logger = get_logger(__name__)
|
||||
sys_logger = get_syslogger("audits")
|
||||
sys_logger = get_syslogger(__name__)
|
||||
json_render = JSONRenderer()
|
||||
|
||||
|
||||
|
@ -26,6 +27,8 @@ MODELS_NEED_RECORD = (
|
|||
'User', 'UserGroup', 'Asset', 'Node', 'AdminUser', 'SystemUser',
|
||||
'Domain', 'Gateway', 'Organization', 'AssetPermission', 'CommandFilter',
|
||||
'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))
|
||||
|
||||
|
||||
@receiver(post_save, dispatch_uid="my_unique_identifier")
|
||||
def on_object_created_or_update(sender, instance=None, created=False, **kwargs):
|
||||
@receiver(post_save)
|
||||
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:
|
||||
action = models.OperateLog.ACTION_CREATE
|
||||
else:
|
||||
|
@ -59,47 +65,46 @@ def on_object_created_or_update(sender, instance=None, created=False, **kwargs):
|
|||
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):
|
||||
create_operate_log(models.OperateLog.ACTION_DELETE, sender, instance)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User, dispatch_uid="my_unique_identifier")
|
||||
def on_user_change_password(sender, instance=None, **kwargs):
|
||||
if hasattr(instance, '_set_password'):
|
||||
if not current_request or not current_request.user.is_authenticated:
|
||||
return
|
||||
with transaction.atomic():
|
||||
models.PasswordChangeLog.objects.create(
|
||||
user=instance, change_by=current_request.user,
|
||||
remote_addr=get_request_ip(current_request),
|
||||
)
|
||||
@receiver(post_user_change_password, sender=User)
|
||||
def on_user_change_password(sender, user=None, **kwargs):
|
||||
if not current_request:
|
||||
remote_addr = '127.0.0.1'
|
||||
change_by = 'System'
|
||||
else:
|
||||
remote_addr = get_request_ip(current_request)
|
||||
if not current_request.user.is_authenticated:
|
||||
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):
|
||||
if sender == models.UserLoginLog:
|
||||
category = "login_log"
|
||||
serializer = serializers.LoginLogSerializer
|
||||
elif sender == models.FTPLog:
|
||||
serializer = serializers.FTPLogSerializer
|
||||
category = "ftp_log"
|
||||
elif sender == models.OperateLog:
|
||||
category = "operation_log"
|
||||
serializer = serializers.OperateLogSerializer
|
||||
elif sender == models.PasswordChangeLog:
|
||||
category = "password_change_log"
|
||||
serializer = serializers.PasswordChangeLogSerializer
|
||||
elif sender == Session:
|
||||
category = "host_session_log"
|
||||
serializer = serializers.SessionAuditSerializer
|
||||
elif sender == Command:
|
||||
category = "session_command_log"
|
||||
serializer = SessionCommandSerializer
|
||||
else:
|
||||
return
|
||||
|
||||
s = serializer(instance=instance)
|
||||
data = json_render.render(s.data).decode(errors='ignore')
|
||||
data = model_to_json(instance, indent=None)
|
||||
msg = "{} - {}".format(category, data)
|
||||
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))
|
||||
data = generate_data(user.username, request)
|
||||
data.update({'mfa': int(user.mfa_enabled), 'status': True})
|
||||
write_login_log_async.delay(**data)
|
||||
write_login_log(**data)
|
||||
|
||||
|
||||
@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))
|
||||
data = generate_data(username, request)
|
||||
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 ops.celery.decorator import register_as_period_task
|
||||
from .models import UserLoginLog
|
||||
from .utils import write_login_log
|
||||
from .models import UserLoginLog, OperateLog
|
||||
|
||||
|
||||
@register_as_period_task(interval=3600*24)
|
||||
|
@ -22,6 +21,13 @@ def clean_login_log_period():
|
|||
UserLoginLog.objects.filter(datetime__lt=expired_day).delete()
|
||||
|
||||
|
||||
@register_as_period_task(interval=3600*24)
|
||||
@shared_task
|
||||
def write_login_log_async(*args, **kwargs):
|
||||
write_login_log(*args, **kwargs)
|
||||
def clean_operation_log_period():
|
||||
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 %}
|
||||
{% block custom_head_css_js %}
|
||||
<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>
|
||||
#search_btn {
|
||||
margin-bottom: 0;
|
||||
|
@ -14,6 +12,9 @@
|
|||
.form-control {
|
||||
height: 30px;
|
||||
}
|
||||
.select2-selection__rendered span.select2-selection, .select2-container .select2-selection--single {
|
||||
height: 30px !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -102,47 +102,47 @@
|
|||
|
||||
|
||||
{% block custom_foot_js %}
|
||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
jumpserver.initStaticTable('#login_log_table');
|
||||
$('#date .input-daterange').datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
todayBtn: "linked",
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
calendarWeeks: true,
|
||||
autoclose: true
|
||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
jumpserver.initStaticTable('#login_log_table');
|
||||
$('#date .input-daterange').datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
todayBtn: "linked",
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
calendarWeeks: true,
|
||||
autoclose: true
|
||||
|
||||
});
|
||||
$('.select2').select2({
|
||||
dropdownAutoWidth: true,
|
||||
width: 'auto'
|
||||
});
|
||||
});
|
||||
$('.select2').select2({
|
||||
dropdownAutoWidth: true,
|
||||
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();
|
||||
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>
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
{% load common_tags %}
|
||||
{% block custom_head_css_js %}
|
||||
<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>
|
||||
#search_btn {
|
||||
margin-bottom: 0;
|
||||
|
@ -69,7 +67,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% 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 'Resource Type' %}</th>
|
||||
<th class="text-center">{% trans 'Resource' %}</th>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
{% load common_tags %}
|
||||
{% block custom_head_css_js %}
|
||||
<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>
|
||||
#search_btn {
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -109,7 +109,7 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
|
|||
|
||||
class AccessTokenAuthentication(authentication.BaseAuthentication):
|
||||
keyword = 'Bearer'
|
||||
expiration = settings.TOKEN_EXPIRATION or 3600
|
||||
# expiration = settings.TOKEN_EXPIRATION or 3600
|
||||
model = get_user_model()
|
||||
|
||||
def authenticate(self, request):
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# coding:utf-8
|
||||
#
|
||||
|
||||
import warnings
|
||||
import ldap
|
||||
from django.conf import settings
|
||||
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 users.utils import construct_user_email
|
||||
|
@ -17,7 +18,6 @@ class LDAPAuthorizationBackend(LDAPBackend):
|
|||
"""
|
||||
Override this class to override _LDAPUser to LDAPUser
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def user_can_authenticate(user):
|
||||
"""
|
||||
|
@ -27,18 +27,26 @@ class LDAPAuthorizationBackend(LDAPBackend):
|
|||
is_valid = getattr(user, 'is_valid', 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')
|
||||
if not username:
|
||||
logger.info('Authenticate failed: username is None')
|
||||
return None
|
||||
return False
|
||||
if settings.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS:
|
||||
user_model = self.get_user_model()
|
||||
exist = user_model.objects.filter(username=username).exists()
|
||||
if not exist:
|
||||
msg = 'Authentication failed: user ({}) is not in the user list'
|
||||
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)
|
||||
user = self.authenticate_ldap_user(ldap_user, password)
|
||||
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.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
|
||||
|
||||
OIDT_ACCESS_TOKEN = 'oidt_access_token'
|
||||
|
@ -155,7 +155,7 @@ class Client(object):
|
|||
"""
|
||||
userinfo = self.get_userinfo(token=token_response['access_token'])
|
||||
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', ''),
|
||||
defaults={
|
||||
'email': userinfo.get('email', ''),
|
||||
|
@ -169,7 +169,9 @@ class Client(object):
|
|||
refresh_token=token_response['refresh_token'],
|
||||
)
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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'))
|
||||
|
|
|
@ -25,7 +25,8 @@ __all__ = ['OpenIDLoginView', 'OpenIDLoginCompleteView']
|
|||
|
||||
class OpenIDLoginView(RedirectView):
|
||||
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(
|
||||
redirect_uri=redirect_uri,
|
||||
next_path=self.request.GET.get('next')
|
||||
|
|
|
@ -118,9 +118,9 @@ class MFAFailedError(AuthFailedNeedLogMixin, AuthFailedError):
|
|||
|
||||
class BlockLoginError(AuthFailedNeedBlockMixin, AuthFailedError):
|
||||
error = 'block_login'
|
||||
msg = block_login_msg.format(settings.SECURITY_LOGIN_LIMIT_TIME)
|
||||
|
||||
def __init__(self, username, ip):
|
||||
self.msg = block_login_msg.format(settings.SECURITY_LOGIN_LIMIT_TIME)
|
||||
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