mirror of https://github.com/jumpserver/jumpserver
Merge remote-tracking branch 'github/dev' into dev
commit
e38d089056
|
@ -17,6 +17,7 @@ from django.db import transaction
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from common.mixins import IDInFilterMixin
|
from common.mixins import IDInFilterMixin
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -37,9 +38,17 @@ class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
Admin user api set, for add,delete,update,list,retrieve resource
|
Admin user api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
filter_fields = ("name", "username")
|
||||||
|
search_fields = filter_fields
|
||||||
queryset = AdminUser.objects.all()
|
queryset = AdminUser.objects.all()
|
||||||
serializer_class = serializers.AdminUserSerializer
|
serializer_class = serializers.AdminUserSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset().all()
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class AdminUserAuthApi(generics.UpdateAPIView):
|
class AdminUserAuthApi(generics.UpdateAPIView):
|
||||||
|
|
|
@ -53,14 +53,14 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
|
||||||
if show_current_asset:
|
if show_current_asset:
|
||||||
self.queryset = self.queryset.filter(
|
self.queryset = self.queryset.filter(
|
||||||
Q(nodes=node_id) | Q(nodes__isnull=True)
|
Q(nodes=node_id) | Q(nodes__isnull=True)
|
||||||
).distinct()
|
)
|
||||||
return
|
return
|
||||||
if show_current_asset:
|
if show_current_asset:
|
||||||
self.queryset = self.queryset.filter(nodes=node).distinct()
|
self.queryset = self.queryset.filter(nodes=node)
|
||||||
else:
|
else:
|
||||||
self.queryset = self.queryset.filter(
|
self.queryset = self.queryset.filter(
|
||||||
nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key),
|
nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key),
|
||||||
).distinct()
|
)
|
||||||
|
|
||||||
def filter_admin_user_id(self):
|
def filter_admin_user_id(self):
|
||||||
admin_user_id = self.request.query_params.get('admin_user_id')
|
admin_user_id = self.request.query_params.get('admin_user_id')
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
|
@ -13,14 +14,20 @@ __all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterViewSet(BulkModelViewSet):
|
class CommandFilterViewSet(BulkModelViewSet):
|
||||||
|
filter_fields = ("name",)
|
||||||
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
queryset = CommandFilter.objects.all()
|
queryset = CommandFilter.objects.all()
|
||||||
serializer_class = serializers.CommandFilterSerializer
|
serializer_class = serializers.CommandFilterSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterRuleViewSet(BulkModelViewSet):
|
class CommandFilterRuleViewSet(BulkModelViewSet):
|
||||||
|
filter_fields = ("content",)
|
||||||
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.CommandFilterRuleSerializer
|
serializer_class = serializers.CommandFilterRuleSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
fpk = self.kwargs.get('filter_pk')
|
fpk = self.kwargs.get('filter_pk')
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
|
@ -20,6 +21,11 @@ class DomainViewSet(BulkModelViewSet):
|
||||||
queryset = Domain.objects.all()
|
queryset = Domain.objects.all()
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.DomainSerializer
|
serializer_class = serializers.DomainSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset().all()
|
||||||
|
return queryset
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.request.query_params.get('gateway'):
|
if self.request.query_params.get('gateway'):
|
||||||
|
@ -33,11 +39,12 @@ class DomainViewSet(BulkModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class GatewayViewSet(BulkModelViewSet):
|
class GatewayViewSet(BulkModelViewSet):
|
||||||
filter_fields = ("domain",)
|
filter_fields = ("domain__name", "name", "username", "ip")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = Gateway.objects.all()
|
queryset = Gateway.objects.all()
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.GatewaySerializer
|
serializer_class = serializers.GatewaySerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
|
|
||||||
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -27,8 +28,11 @@ __all__ = ['LabelViewSet']
|
||||||
|
|
||||||
|
|
||||||
class LabelViewSet(BulkModelViewSet):
|
class LabelViewSet(BulkModelViewSet):
|
||||||
|
filter_fields = ("name", "value")
|
||||||
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.LabelSerializer
|
serializer_class = serializers.LabelSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
if request.query_params.get("distinct"):
|
if request.query_params.get("distinct"):
|
||||||
|
|
|
@ -42,9 +42,16 @@ class SystemUserViewSet(BulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
System user api set, for add,delete,update,list,retrieve resource
|
System user api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
|
filter_fields = ("name", "username")
|
||||||
|
search_fields = filter_fields
|
||||||
queryset = SystemUser.objects.all()
|
queryset = SystemUser.objects.all()
|
||||||
serializer_class = serializers.SystemUserSerializer
|
serializer_class = serializers.SystemUserSerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset().all()
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
|
|
@ -121,10 +121,10 @@ class Node(OrgModelMixin):
|
||||||
def get_assets(self):
|
def get_assets(self):
|
||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
if self.is_default_node():
|
if self.is_default_node():
|
||||||
assets = Asset.objects.filter(nodes__isnull=True)
|
assets = Asset.objects.filter(Q(nodes__id=self.id) | Q(nodes__isnull=True))
|
||||||
else:
|
else:
|
||||||
assets = Asset.objects.filter(nodes__id=self.id)
|
assets = Asset.objects.filter(nodes__id=self.id)
|
||||||
return assets
|
return assets.distinct()
|
||||||
|
|
||||||
def get_valid_assets(self):
|
def get_valid_assets(self):
|
||||||
return self.get_assets().valid()
|
return self.get_assets().valid()
|
||||||
|
|
|
@ -93,7 +93,7 @@ $(document).ready(function(){
|
||||||
columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" },
|
columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" },
|
||||||
{data: "reachable_amount"}, {data: "unreachable_amount"}, {data: "id"}, {data: "comment" }, {data: "id" }]
|
{data: "reachable_amount"}, {data: "unreachable_amount"}, {data: "id"}, {data: "comment" }, {data: "id" }]
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options)
|
||||||
})
|
})
|
||||||
|
|
||||||
.on('click', '.btn_admin_user_delete', function () {
|
.on('click', '.btn_admin_user_delete', function () {
|
||||||
|
|
|
@ -66,7 +66,7 @@ function initTable() {
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
initTable();
|
||||||
|
|
|
@ -95,7 +95,7 @@ function initTable() {
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
initTable();
|
||||||
|
|
|
@ -98,7 +98,7 @@ function initTable() {
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
initTable();
|
||||||
|
|
|
@ -62,7 +62,7 @@ function initTable() {
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
initTable();
|
||||||
|
|
|
@ -47,7 +47,7 @@ function initTable() {
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
initTable();
|
||||||
|
|
|
@ -100,7 +100,7 @@ function initTable() {
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var zTree, asset_table;
|
var zTree, asset_table, show=0;
|
||||||
var inited = false;
|
var inited = false;
|
||||||
var url;
|
var url;
|
||||||
function initTable() {
|
function initTable() {
|
||||||
|
@ -102,7 +102,7 @@ function initTable() {
|
||||||
{data: "system_users_granted", orderable: false}
|
{data: "system_users_granted", orderable: false}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
asset_table = jumpserver.initDataTable(options);
|
asset_table = jumpserver.initServerSideDataTable(options);
|
||||||
return asset_table
|
return asset_table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +183,21 @@ $(document).ready(function () {
|
||||||
$('#asset_detail_tbody').html(trs)
|
$('#asset_detail_tbody').html(trs)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -36,6 +36,7 @@ class LabelCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
form_class = LabelForm
|
form_class = LabelForm
|
||||||
success_url = reverse_lazy('assets:label-list')
|
success_url = reverse_lazy('assets:label-list')
|
||||||
success_message = create_success_msg
|
success_message = create_success_msg
|
||||||
|
disable_name = ['draw', 'search', 'limit', 'offset', '_']
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
|
@ -45,6 +46,16 @@ class LabelCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
name = form.cleaned_data.get('name')
|
||||||
|
if name in self.disable_name:
|
||||||
|
msg = _(
|
||||||
|
'Tips: Avoid using label names reserved internally: {}'
|
||||||
|
).format(', '.join(self.disable_name))
|
||||||
|
form.add_error("name", msg)
|
||||||
|
return self.form_invalid(form)
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class LabelUpdateView(AdminUserRequiredMixin, UpdateView):
|
class LabelUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
model = Label
|
model = Label
|
||||||
|
|
|
@ -160,8 +160,12 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
||||||
return users
|
return users
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
users = self.get_org_users()
|
if current_org.is_default():
|
||||||
queryset = super().get_queryset().filter(username__in=users)
|
queryset = super().get_queryset()
|
||||||
|
else:
|
||||||
|
users = self.get_org_users()
|
||||||
|
queryset = super().get_queryset().filter(username__in=users)
|
||||||
|
|
||||||
self.user = self.request.GET.get('user', '')
|
self.user = self.request.GET.get('user', '')
|
||||||
self.keyword = self.request.GET.get("keyword", '')
|
self.keyword = self.request.GET.get("keyword", '')
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
import json
|
import json
|
||||||
|
import jms_storage
|
||||||
|
|
||||||
from rest_framework.views import Response, APIView
|
from rest_framework.views import Response, APIView
|
||||||
from ldap3 import Server, Connection
|
from ldap3 import Server, Connection
|
||||||
|
@ -8,8 +11,9 @@ from django.core.mail import get_connection, send_mail
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from .permissions import IsOrgAdmin
|
from .permissions import IsOrgAdmin, IsSuperUser
|
||||||
from .serializers import MailTestSerializer, LDAPTestSerializer
|
from .serializers import MailTestSerializer, LDAPTestSerializer
|
||||||
|
from .models import Setting
|
||||||
|
|
||||||
|
|
||||||
class MailTestingAPI(APIView):
|
class MailTestingAPI(APIView):
|
||||||
|
@ -85,6 +89,79 @@ class LDAPTestingAPI(APIView):
|
||||||
return Response({"error": str(serializer.errors)}, status=401)
|
return Response({"error": str(serializer.errors)}, status=401)
|
||||||
|
|
||||||
|
|
||||||
|
class ReplayStorageCreateAPI(APIView):
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
storage_data = request.data
|
||||||
|
|
||||||
|
if storage_data.get('TYPE') == 'ceph':
|
||||||
|
port = storage_data.get('PORT')
|
||||||
|
if port.isdigit():
|
||||||
|
storage_data['PORT'] = int(storage_data.get('PORT'))
|
||||||
|
|
||||||
|
storage_name = storage_data.pop('NAME')
|
||||||
|
data = {storage_name: storage_data}
|
||||||
|
|
||||||
|
if not self.is_valid(storage_data):
|
||||||
|
return Response({"error": _("Error: Account invalid")}, status=401)
|
||||||
|
|
||||||
|
Setting.save_storage('TERMINAL_REPLAY_STORAGE', data)
|
||||||
|
return Response({"msg": _('Create succeed')}, status=200)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_valid(storage_data):
|
||||||
|
if storage_data.get('TYPE') == 'server':
|
||||||
|
return True
|
||||||
|
storage = jms_storage.get_object_storage(storage_data)
|
||||||
|
target = 'tests.py'
|
||||||
|
src = os.path.join(settings.BASE_DIR, 'common', target)
|
||||||
|
return storage.is_valid(src, target)
|
||||||
|
|
||||||
|
|
||||||
|
class ReplayStorageDeleteAPI(APIView):
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
storage_name = str(request.data.get('name'))
|
||||||
|
Setting.delete_storage('TERMINAL_REPLAY_STORAGE', storage_name)
|
||||||
|
return Response({"msg": _('Delete succeed')}, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandStorageCreateAPI(APIView):
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
storage_data = request.data
|
||||||
|
storage_name = storage_data.pop('NAME')
|
||||||
|
data = {storage_name: storage_data}
|
||||||
|
if not self.is_valid(storage_data):
|
||||||
|
return Response({"error": _("Error: Account invalid")}, status=401)
|
||||||
|
|
||||||
|
Setting.save_storage('TERMINAL_COMMAND_STORAGE', data)
|
||||||
|
return Response({"msg": _('Create succeed')}, status=200)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_valid(storage_data):
|
||||||
|
if storage_data.get('TYPE') == 'server':
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
storage = jms_storage.get_log_storage(storage_data)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return storage.ping()
|
||||||
|
|
||||||
|
|
||||||
|
class CommandStorageDeleteAPI(APIView):
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
storage_name = str(request.data.get('name'))
|
||||||
|
Setting.delete_storage('TERMINAL_COMMAND_STORAGE', storage_name)
|
||||||
|
return Response({"msg": _('Delete succeed')}, status=200)
|
||||||
|
|
||||||
|
|
||||||
class DjangoSettingsAPI(APIView):
|
class DjangoSettingsAPI(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
if not settings.DEBUG:
|
if not settings.DEBUG:
|
||||||
|
|
|
@ -135,32 +135,24 @@ class TerminalSettingForm(BaseForm):
|
||||||
('hostname', _('Hostname')),
|
('hostname', _('Hostname')),
|
||||||
('ip', _('IP')),
|
('ip', _('IP')),
|
||||||
)
|
)
|
||||||
TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField(
|
|
||||||
choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by")
|
|
||||||
)
|
|
||||||
TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField(
|
|
||||||
initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds")
|
|
||||||
)
|
|
||||||
TERMINAL_PASSWORD_AUTH = forms.BooleanField(
|
TERMINAL_PASSWORD_AUTH = forms.BooleanField(
|
||||||
initial=True, required=False, label=_("Password auth")
|
initial=True, required=False, label=_("Password auth")
|
||||||
)
|
)
|
||||||
TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField(
|
TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField(
|
||||||
initial=True, required=False, label=_("Public key auth")
|
initial=True, required=False, label=_("Public key auth")
|
||||||
)
|
)
|
||||||
TERMINAL_COMMAND_STORAGE = FormEncryptDictField(
|
TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField(
|
||||||
label=_("Command storage"), help_text=_(
|
initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds")
|
||||||
"Set terminal storage setting, `default` is the using as default,"
|
|
||||||
"You can set other storage and some terminal using"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
TERMINAL_REPLAY_STORAGE = FormEncryptDictField(
|
TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField(
|
||||||
label=_("Replay storage"), help_text=_(
|
choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by")
|
||||||
"Set replay storage setting, `default` is the using as default,"
|
|
||||||
"You can set other storage and some terminal using"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalCommandStorage(BaseForm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SecuritySettingForm(BaseForm):
|
class SecuritySettingForm(BaseForm):
|
||||||
# MFA global setting
|
# MFA global setting
|
||||||
SECURITY_MFA_AUTH = forms.BooleanField(
|
SECURITY_MFA_AUTH = forms.BooleanField(
|
||||||
|
|
|
@ -117,6 +117,3 @@ class DatetimeSearchMixin:
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.get_date_range()
|
self.get_date_range()
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,30 @@ class Setting(models.Model):
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
raise ValueError("Json dump error: {}".format(str(e)))
|
raise ValueError("Json dump error: {}".format(str(e)))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def save_storage(cls, name, data):
|
||||||
|
obj = cls.objects.filter(name=name).first()
|
||||||
|
if not obj:
|
||||||
|
obj = cls()
|
||||||
|
obj.name = name
|
||||||
|
obj.encrypted = True
|
||||||
|
obj.cleaned_value = data
|
||||||
|
else:
|
||||||
|
value = obj.cleaned_value
|
||||||
|
value.update(data)
|
||||||
|
obj.cleaned_value = value
|
||||||
|
obj.save()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def delete_storage(cls, name, storage_name):
|
||||||
|
obj = cls.objects.get(name=name)
|
||||||
|
value = obj.cleaned_value
|
||||||
|
value.pop(storage_name, '')
|
||||||
|
obj.cleaned_value = value
|
||||||
|
obj.save()
|
||||||
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh_all_settings(cls):
|
def refresh_all_settings(cls):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.conf import settings
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from .utils import get_logger
|
from .utils import get_logger
|
||||||
from .models import Setting
|
from .models import Setting
|
||||||
|
from common.models import common_settings
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
@ -28,7 +29,7 @@ def send_mail_async(*args, **kwargs):
|
||||||
|
|
||||||
if len(args) == 3:
|
if len(args) == 3:
|
||||||
args = list(args)
|
args = list(args)
|
||||||
args[0] = settings.EMAIL_SUBJECT_PREFIX + args[0]
|
args[0] = common_settings.EMAIL_SUBJECT_PREFIX + args[0]
|
||||||
args.insert(2, settings.EMAIL_HOST_USER)
|
args.insert(2, settings.EMAIL_HOST_USER)
|
||||||
args = tuple(args)
|
args = tuple(args)
|
||||||
|
|
||||||
|
|
|
@ -75,32 +75,6 @@
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
})
|
|
||||||
.on("click", ".btn-test", function () {
|
|
||||||
var data = {};
|
|
||||||
var form = $("form").serializeArray();
|
|
||||||
$.each(form, function (i, field) {
|
|
||||||
data[field.name] = field.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
var the_url = "{% url 'api-common:mail-testing' %}";
|
|
||||||
|
|
||||||
function error(message) {
|
|
||||||
toastr.error(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
function success(message) {
|
|
||||||
toastr.success(message.msg)
|
|
||||||
}
|
|
||||||
APIUpdateAttr({
|
|
||||||
url: the_url,
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
method: "POST",
|
|
||||||
flash_message: false,
|
|
||||||
success: success,
|
|
||||||
error: error
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
{#{% extends 'base.html' %}#}
|
||||||
|
{% extends '_base_create_update.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load common_tags %}
|
||||||
|
|
||||||
|
{% 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="ibox-title">
|
||||||
|
<h5>{{ action }}</h5>
|
||||||
|
<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>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form action="" method="POST" class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="id_type">{% trans "Type" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<select id="id_type" class="selector form-control">
|
||||||
|
<option value ="server" selected="selected">server</option>
|
||||||
|
<option value ="es">es (elasticsearch)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="id_name">{% trans "Name" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_name" class="form-control" type="text" name="NAME" value="">
|
||||||
|
<div class="help-block">* required</div>
|
||||||
|
<div id="id_error" style="color: red;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_hosts">{% trans "Hosts" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_hosts" class="form-control" type="text" name="HOSTS" value="">
|
||||||
|
<div class="help-block">{% trans 'Tips: If there are multiple hosts, separate them with a comma (,)' %}</div>
|
||||||
|
<div class="help-block">eg: http://www.jumpserver.a.com, http://www.jumpserver.b.com</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# <div class="form-group" style="display: none;" >#}
|
||||||
|
{# <label class="col-md-2 control-label" for="id_other">{% trans "Other" %}</label>#}
|
||||||
|
{# <div class="col-md-9">#}
|
||||||
|
{# <input id="id_other" class="form-control" type="text" name="OTHER" value="">#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_bucket">{% trans "Index" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_index" class="form-control" type="text" name="INDEX" value="jumpserver">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_doc_type">{% trans "Doc type" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_doc_type" class="form-control" type="text" name="DOC_TYPE" value="command_store">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
|
||||||
|
<a class="btn btn-primary" type="" id="id_submit_button" >{% trans 'Submit' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var field_of_all, need_get_field_of_server, need_get_field_of_es;
|
||||||
|
|
||||||
|
function showField(field){
|
||||||
|
$.each(field, function(index, value){
|
||||||
|
$(value).parent('div').parent('div').css('display', '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hiddenField(field){
|
||||||
|
$.each(field, function(index, value){
|
||||||
|
$(value).parent('div').parent('div').css('display', 'none');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFieldByType(type){
|
||||||
|
|
||||||
|
if(type === 'server'){
|
||||||
|
return need_get_field_of_server
|
||||||
|
}
|
||||||
|
else if(type === 'es'){
|
||||||
|
return need_get_field_of_es
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ajaxAPI(url, data, success, error){
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
data: data,
|
||||||
|
method: 'POST',
|
||||||
|
contentType: 'application/json; charset=utf-8',
|
||||||
|
success: success,
|
||||||
|
error: error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var name_id = '#id_name';
|
||||||
|
var hosts_id = '#id_hosts';
|
||||||
|
{#var other_id = '#id_other';#}
|
||||||
|
var index_id = '#id_index';
|
||||||
|
var doc_type_id = '#id_doc_type';
|
||||||
|
|
||||||
|
field_of_all = [name_id, hosts_id, index_id, doc_type_id];
|
||||||
|
need_get_field_of_server = [name_id];
|
||||||
|
need_get_field_of_es = [name_id, hosts_id, index_id, doc_type_id];
|
||||||
|
})
|
||||||
|
.on('change', '.selector', function(){
|
||||||
|
var type = $('.selector').val();
|
||||||
|
console.log(type);
|
||||||
|
hiddenField(field_of_all);
|
||||||
|
var field = getFieldByType(type);
|
||||||
|
showField(field)
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '#id_submit_button', function(){
|
||||||
|
var type = $('.selector').val();
|
||||||
|
var field = getFieldByType(type);
|
||||||
|
var data = {'TYPE': type};
|
||||||
|
$.each(field, function(index, id_field){
|
||||||
|
var name = $(id_field).attr('name');
|
||||||
|
var value = $(id_field).val();
|
||||||
|
if(name === 'HOSTS'){
|
||||||
|
data[name] = value.split(',');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
data[name] = value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var url = "{% url 'api-common:command-storage-create' %}";
|
||||||
|
var success = function(data, textStatus) {
|
||||||
|
console.log(data, textStatus);
|
||||||
|
location = "{% url 'common:terminal-setting' %}";
|
||||||
|
};
|
||||||
|
var error = function(data, textStatus) {
|
||||||
|
var error_msg = data.responseJSON.error;
|
||||||
|
$('#id_error').html(error_msg)
|
||||||
|
};
|
||||||
|
ajaxAPI(url, JSON.stringify(data), success, error)
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,248 @@
|
||||||
|
{#{% extends 'base.html' %}#}
|
||||||
|
{% extends '_base_create_update.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load common_tags %}
|
||||||
|
|
||||||
|
{% 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="ibox-title">
|
||||||
|
<h5>{{ action }}</h5>
|
||||||
|
<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>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form action="" method="POST" class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="id_type">{% trans "Type" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<select id="id_type" class="selector form-control">
|
||||||
|
<option value ="server" selected="selected">server</option>
|
||||||
|
<option value ="s3">s3</option>
|
||||||
|
<option value="oss">oss</option>
|
||||||
|
<option value ="azure">azure</option>
|
||||||
|
<option value="ceph">ceph</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="id_name">{% trans "Name" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_name" class="form-control" type="text" name="NAME" value="">
|
||||||
|
<div class="help-block">* required</div>
|
||||||
|
<div id="id_error" style="color: red;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_host">{% trans "Host" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_host" class="form-control" type="text" name="HOSTNAME" value="" placeholder="Host">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_port">{% trans "Port" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_port" class="form-control" type="text" name="PORT" value="" placeholder="7480">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_bucket">{% trans "Bucket" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_bucket" class="form-control" type="text" name="BUCKET" value="" placeholder="Bucket">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_access_key">{% trans "Access key" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_access_key" class="form-control" type="text" name="ACCESS_KEY" value="" placeholder="Access key">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_secret_key">{% trans "Secret key" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_secret_key" class="form-control" type="text" name="SECRET_KEY" value="", placeholder="Secret key">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_container_name">{% trans "Container name" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_container_name" class="form-control" type="text" name="CONTAINER_NAME" value="" placeholder="Container">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_account_name">{% trans "Account name" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_account_name" class="form-control" type="text" name="ACCOUNT_NAME" value="" placeholder="Account name">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_account_key">{% trans "Account key" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_account_key" class="form-control" type="text" name="ACCOUNT_KEY" value="" placeholder="Account key">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_endpoint">{% trans "Endpoint" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_endpoint" class="form-control" type="text" name="ENDPOINT" value="" placeholder="Endpoint">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_endpoint_suffix">{% trans "Endpoint suffix" %}</label>
|
||||||
|
{# <div class="col-md-9">#}
|
||||||
|
{# <input id="id_endpoint_suffix" class="form-control" type="text" name="ENDPOINT_SUFFIX" value="">#}
|
||||||
|
{# <div class="help-block">{% trans '' %}</div>#}
|
||||||
|
{# </div>#}
|
||||||
|
<div class="col-md-9">
|
||||||
|
<select id="id_endpoint_suffix" name="ENDPOINT_SUFFIX" class="endpoint-suffix-selector form-control">
|
||||||
|
<option value="core.chinacloudapi.cn" selected="selected">core.chinacloudapi.cn</option>
|
||||||
|
<option value="core.windows.net">core.windows.net</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="display: none;" >
|
||||||
|
<label class="col-md-2 control-label" for="id_region">{% trans "Region" %}</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input id="id_region" class="form-control" type="text" name="REGION" value="" placeholder="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
|
||||||
|
<a class="btn btn-primary" type="" id="id_submit_button" >{% trans 'Submit' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var field_of_all, need_get_field_of_server, need_get_field_of_s3,
|
||||||
|
need_get_field_of_oss, need_get_field_of_azure, need_get_field_of_ceph;
|
||||||
|
|
||||||
|
function showField(field){
|
||||||
|
$.each(field, function(index, value){
|
||||||
|
$(value).parent('div').parent('div').css('display', '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hiddenField(field){
|
||||||
|
$.each(field, function(index, value){
|
||||||
|
$(value).parent('div').parent('div').css('display', 'none');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFieldByType(type){
|
||||||
|
|
||||||
|
if(type === 'server'){
|
||||||
|
return need_get_field_of_server
|
||||||
|
}
|
||||||
|
else if(type === 's3'){
|
||||||
|
return need_get_field_of_s3
|
||||||
|
}
|
||||||
|
else if(type === 'oss'){
|
||||||
|
return need_get_field_of_oss
|
||||||
|
}
|
||||||
|
else if(type === 'azure'){
|
||||||
|
return need_get_field_of_azure
|
||||||
|
}
|
||||||
|
else if(type === 'ceph'){
|
||||||
|
return need_get_field_of_ceph
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ajaxAPI(url, data, success, error){
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
data: data,
|
||||||
|
method: 'POST',
|
||||||
|
contentType: 'application/json; charset=utf-8',
|
||||||
|
success: success,
|
||||||
|
error: error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var name_id = '#id_name';
|
||||||
|
var host_id = '#id_host';
|
||||||
|
var port_id = '#id_port';
|
||||||
|
var bucket_id = '#id_bucket';
|
||||||
|
var access_key_id = '#id_access_key';
|
||||||
|
var secret_key_id = '#id_secret_key';
|
||||||
|
var container_name_id = '#id_container_name';
|
||||||
|
var account_name_id = '#id_account_name';
|
||||||
|
var account_key_id = '#id_account_key';
|
||||||
|
var endpoint_id = '#id_endpoint';
|
||||||
|
var endpoint_suffix_id = '#id_endpoint_suffix';
|
||||||
|
var region_id = '#id_region';
|
||||||
|
|
||||||
|
field_of_all = [name_id, host_id, port_id, bucket_id, access_key_id, secret_key_id, container_name_id, account_name_id, account_key_id, endpoint_id, endpoint_suffix_id, region_id];
|
||||||
|
need_get_field_of_server = [name_id];
|
||||||
|
need_get_field_of_s3 = [name_id, bucket_id, access_key_id, secret_key_id, region_id];
|
||||||
|
need_get_field_of_oss = [name_id, access_key_id, secret_key_id, endpoint_id];
|
||||||
|
need_get_field_of_azure = [name_id, container_name_id, account_name_id, account_key_id, endpoint_suffix_id];
|
||||||
|
need_get_field_of_ceph = [name_id, host_id, port_id, bucket_id, access_key_id, secret_key_id, region_id];
|
||||||
|
})
|
||||||
|
.on('change', '.selector', function(){
|
||||||
|
var type = $('.selector').val();
|
||||||
|
console.log(type);
|
||||||
|
hiddenField(field_of_all);
|
||||||
|
var field = getFieldByType(type);
|
||||||
|
showField(field)
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '#id_submit_button', function(){
|
||||||
|
var type = $('.selector').val();
|
||||||
|
var field = getFieldByType(type);
|
||||||
|
var data = {'TYPE': type};
|
||||||
|
$.each(field, function(index, id_field){
|
||||||
|
var name = $(id_field).attr('name');
|
||||||
|
data[name] = $(id_field).val();
|
||||||
|
});
|
||||||
|
var url = "{% url 'api-common:replay-storage-create' %}";
|
||||||
|
var success = function(data, textStatus) {
|
||||||
|
location = "{% url 'common:terminal-setting' %}";
|
||||||
|
};
|
||||||
|
var error = function(data, textStatus) {
|
||||||
|
var error_msg = data.responseJSON.error;
|
||||||
|
$('#id_error').html(error_msg)
|
||||||
|
};
|
||||||
|
ajaxAPI(url, JSON.stringify(data), success, error)
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -63,6 +63,14 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
|
|
||||||
<h3>{% trans "Command storage" %}</h3>
|
<h3>{% trans "Command storage" %}</h3>
|
||||||
|
@ -71,6 +79,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans 'Name' %}</th>
|
<th>{% trans 'Name' %}</th>
|
||||||
<th>{% trans 'Type' %}</th>
|
<th>{% trans 'Type' %}</th>
|
||||||
|
<th>{% trans 'Action' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -78,10 +87,13 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ name }}</td>
|
<td>{{ name }}</td>
|
||||||
<td>{{ setting.TYPE }}</td>
|
<td>{{ setting.TYPE }}</td>
|
||||||
|
<td><a class="btn btn-xs btn-danger m-l-xs btn-del-command" data-name="{{ name }}">{% trans 'Delete' %}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<a href="{% url 'common:command-storage-create' %}" class="btn btn-primary">{% trans 'Add' %}</a>
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans "Replay storage" %}</h3>
|
<h3>{% trans "Replay storage" %}</h3>
|
||||||
<table class="table table-hover " id="task-history-list-table">
|
<table class="table table-hover " id="task-history-list-table">
|
||||||
|
@ -89,6 +101,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans 'Name' %}</th>
|
<th>{% trans 'Name' %}</th>
|
||||||
<th>{% trans 'Type' %}</th>
|
<th>{% trans 'Type' %}</th>
|
||||||
|
<th>{% trans 'Action' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -96,18 +109,14 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ name }}</td>
|
<td>{{ name }}</td>
|
||||||
<td>{{ setting.TYPE }}</td>
|
<td>{{ setting.TYPE }}</td>
|
||||||
|
<td><a class="btn btn-xs btn-danger m-l-xs btn-del-replay" data-name="{{ name }}">{% trans 'Delete' %}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<a href="{% url 'common:replay-storage-create' %}" class="btn btn-primary">{% trans 'Add' %}</a>
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-4 col-sm-offset-2">
|
|
||||||
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
|
|
||||||
<button id="submit_button" class="btn btn-primary"
|
|
||||||
type="submit">{% trans 'Submit' %}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -116,40 +125,63 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
|
||||||
})
|
|
||||||
.on("click", ".btn-test", function () {
|
|
||||||
var data = {};
|
|
||||||
var form = $("form").serializeArray();
|
|
||||||
$.each(form, function (i, field) {
|
|
||||||
data[field.name] = field.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
var the_url = "{% url 'api-common:ldap-testing' %}";
|
function ajaxAPI(url, data, success, error, method){
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
data: data,
|
||||||
|
method: method,
|
||||||
|
contentType: 'application/json; charset=utf-8',
|
||||||
|
success: success,
|
||||||
|
error: error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function error(message) {
|
function deleteStorage($this, the_url){
|
||||||
toastr.error(message)
|
var name = $this.data('name');
|
||||||
}
|
function doDelete(){
|
||||||
|
console.log('delete storage');
|
||||||
|
var data = {"name": name};
|
||||||
|
var method = 'POST';
|
||||||
|
var success = function(){
|
||||||
|
$this.parent().parent().remove();
|
||||||
|
toastr.success("{% trans 'Delete succeed' %}");
|
||||||
|
};
|
||||||
|
var error = function(){
|
||||||
|
toastr.error("{% trans 'Delete failed' %}}");
|
||||||
|
};
|
||||||
|
ajaxAPI(the_url, JSON.stringify(data), success, error, method);
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: "{% trans 'Are you sure about deleting it?' %}",
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: "{% trans 'Cancel' %}",
|
||||||
|
confirmButtonColor: "#ed5565",
|
||||||
|
confirmButtonText: "{% trans 'Confirm' %}",
|
||||||
|
closeOnConfirm: true
|
||||||
|
}, function () {
|
||||||
|
doDelete()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function success(message) {
|
$(document).ready(function () {
|
||||||
toastr.success(message.msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
APIUpdateAttr({
|
})
|
||||||
url: the_url,
|
.on('click', '.btn-del-replay', function(){
|
||||||
body: JSON.stringify(data),
|
var $this = $(this);
|
||||||
method: "POST",
|
var the_url = "{% url 'api-common:replay-storage-delete' %}";
|
||||||
flash_message: false,
|
deleteStorage($this, the_url);
|
||||||
success: success,
|
})
|
||||||
error: error
|
.on('click', '.btn-del-command', function() {
|
||||||
});
|
var $this = $(this);
|
||||||
})
|
var the_url = "{% url 'api-common:command-storage-delete' %}";
|
||||||
.on('click', '', function () {
|
deleteStorage($this, the_url)
|
||||||
|
});
|
||||||
|
|
||||||
})
|
</script>
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -9,5 +9,9 @@ app_name = 'common'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'),
|
path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'),
|
||||||
path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
|
path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
|
||||||
|
path('terminal/replay-storage/create/', api.ReplayStorageCreateAPI.as_view(), name='replay-storage-create'),
|
||||||
|
path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'),
|
||||||
|
path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'),
|
||||||
|
path('terminal/command-storage/delete/', api.CommandStorageDeleteAPI.as_view(), name='command-storage-delete'),
|
||||||
# path('django-settings/', api.DjangoSettingsAPI.as_view(), name='django-settings'),
|
# path('django-settings/', api.DjangoSettingsAPI.as_view(), name='django-settings'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,5 +11,7 @@ urlpatterns = [
|
||||||
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
|
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
|
||||||
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
|
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
|
||||||
url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
|
url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
|
||||||
|
url(r'^terminal/replay-storage/create$', views.ReplayStorageCreateView.as_view(), name='replay-storage-create'),
|
||||||
|
url(r'^terminal/command-storage/create$', views.CommandStorageCreateView.as_view(), name='command-storage-create'),
|
||||||
url(r'^security/$', views.SecuritySettingView.as_view(), name='security-setting'),
|
url(r'^security/$', views.SecuritySettingView.as_view(), name='security-setting'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -37,7 +37,8 @@ def reverse(view_name, urlconf=None, args=None, kwargs=None,
|
||||||
kwargs=kwargs, current_app=current_app)
|
kwargs=kwargs, current_app=current_app)
|
||||||
|
|
||||||
if external:
|
if external:
|
||||||
url = settings.SITE_URL.strip('/') + url
|
from common.models import common_settings
|
||||||
|
url = common_settings.SITE_URL.strip('/') + url
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
@ -387,6 +388,49 @@ def get_request_ip(request):
|
||||||
return login_ip
|
return login_ip
|
||||||
|
|
||||||
|
|
||||||
|
def get_command_storage_or_create_default_storage():
|
||||||
|
from common.models import common_settings, Setting
|
||||||
|
name = 'TERMINAL_COMMAND_STORAGE'
|
||||||
|
default = {'default': {'TYPE': 'server'}}
|
||||||
|
command_storage = common_settings.TERMINAL_COMMAND_STORAGE
|
||||||
|
if command_storage is None:
|
||||||
|
obj = Setting()
|
||||||
|
obj.name = name
|
||||||
|
obj.encrypted = True
|
||||||
|
obj.cleaned_value = default
|
||||||
|
obj.save()
|
||||||
|
if isinstance(command_storage, dict) and not command_storage:
|
||||||
|
obj = Setting.objects.get(name=name)
|
||||||
|
value = obj.cleaned_value
|
||||||
|
value.update(default)
|
||||||
|
obj.cleaned_value = value
|
||||||
|
obj.save()
|
||||||
|
command_storage = common_settings.TERMINAL_COMMAND_STORAGE
|
||||||
|
return command_storage
|
||||||
|
|
||||||
|
|
||||||
|
def get_replay_storage_or_create_default_storage():
|
||||||
|
from common.models import common_settings, Setting
|
||||||
|
name = 'TERMINAL_REPLAY_STORAGE'
|
||||||
|
default = {'default': {'TYPE': 'server'}}
|
||||||
|
replay_storage = common_settings.TERMINAL_REPLAY_STORAGE
|
||||||
|
if replay_storage is None:
|
||||||
|
obj = Setting()
|
||||||
|
obj.name = name
|
||||||
|
obj.encrypted = True
|
||||||
|
obj.cleaned_value = default
|
||||||
|
obj.save()
|
||||||
|
replay_storage = common_settings.TERMINAL_REPLAY_STORAGE
|
||||||
|
if isinstance(replay_storage, dict) and not replay_storage:
|
||||||
|
obj = Setting.objects.get(name=name)
|
||||||
|
value = obj.cleaned_value
|
||||||
|
value.update(default)
|
||||||
|
obj.cleaned_value = value
|
||||||
|
obj.save()
|
||||||
|
replay_storage = common_settings.TERMINAL_REPLAY_STORAGE
|
||||||
|
return replay_storage
|
||||||
|
|
||||||
|
|
||||||
class TeeObj:
|
class TeeObj:
|
||||||
origin_stdout = sys.stdout
|
origin_stdout = sys.stdout
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,12 @@ from django.contrib import messages
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from common.models import common_settings
|
||||||
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
|
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
|
||||||
TerminalSettingForm, SecuritySettingForm
|
TerminalSettingForm, SecuritySettingForm
|
||||||
from common.permissions import SuperUserRequiredMixin
|
from common.permissions import SuperUserRequiredMixin
|
||||||
from .signals import ldap_auth_enable
|
from .signals import ldap_auth_enable
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
class BasicSettingView(SuperUserRequiredMixin, TemplateView):
|
class BasicSettingView(SuperUserRequiredMixin, TemplateView):
|
||||||
|
@ -95,14 +97,15 @@ class TerminalSettingView(SuperUserRequiredMixin, TemplateView):
|
||||||
template_name = "common/terminal_setting.html"
|
template_name = "common/terminal_setting.html"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
command_storage = settings.TERMINAL_COMMAND_STORAGE
|
command_storage = utils.get_command_storage_or_create_default_storage()
|
||||||
replay_storage = settings.TERMINAL_REPLAY_STORAGE
|
replay_storage = utils.get_replay_storage_or_create_default_storage()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'app': _('Settings'),
|
'app': _('Settings'),
|
||||||
'action': _('Terminal setting'),
|
'action': _('Terminal setting'),
|
||||||
'form': self.form_class(),
|
'form': self.form_class(),
|
||||||
'replay_storage': replay_storage,
|
'replay_storage': replay_storage,
|
||||||
'command_storage': command_storage,
|
'command_storage': command_storage
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
@ -120,6 +123,30 @@ class TerminalSettingView(SuperUserRequiredMixin, TemplateView):
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
|
||||||
|
class ReplayStorageCreateView(SuperUserRequiredMixin, TemplateView):
|
||||||
|
template_name = 'common/replay_storage_create.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Settings'),
|
||||||
|
'action': _('Create replay storage')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandStorageCreateView(SuperUserRequiredMixin, TemplateView):
|
||||||
|
template_name = 'common/command_storage_create.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Settings'),
|
||||||
|
'action': _('Create command storage')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SecuritySettingView(SuperUserRequiredMixin, TemplateView):
|
class SecuritySettingView(SuperUserRequiredMixin, TemplateView):
|
||||||
form_class = SecuritySettingForm
|
form_class = SecuritySettingForm
|
||||||
template_name = "common/security_setting.html"
|
template_name = "common/security_setting.html"
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -50,7 +50,7 @@ class JMSInventory(BaseInventory):
|
||||||
def convert_to_ansible(self, asset, run_as_admin=False):
|
def convert_to_ansible(self, asset, run_as_admin=False):
|
||||||
info = {
|
info = {
|
||||||
'id': asset.id,
|
'id': asset.id,
|
||||||
'hostname': asset.hostname,
|
'hostname': asset.fullname,
|
||||||
'ip': asset.ip,
|
'ip': asset.ip,
|
||||||
'port': asset.port,
|
'port': asset.port,
|
||||||
'vars': dict(),
|
'vars': dict(),
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.views.generic import ListView, DetailView, TemplateView
|
||||||
|
|
||||||
from common.mixins import DatetimeSearchMixin
|
from common.mixins import DatetimeSearchMixin
|
||||||
from .models import Task, AdHoc, AdHocRunHistory, CeleryTask
|
from .models import Task, AdHoc, AdHocRunHistory, CeleryTask
|
||||||
from common.permissions import SuperUserRequiredMixin
|
from common.permissions import SuperUserRequiredMixin, AdminUserRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
class TaskListView(SuperUserRequiredMixin, DatetimeSearchMixin, ListView):
|
class TaskListView(SuperUserRequiredMixin, DatetimeSearchMixin, ListView):
|
||||||
|
@ -121,6 +121,6 @@ class AdHocHistoryDetailView(SuperUserRequiredMixin, DetailView):
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CeleryTaskLogView(SuperUserRequiredMixin, DetailView):
|
class CeleryTaskLogView(AdminUserRequiredMixin, DetailView):
|
||||||
template_name = 'ops/celery_task_log.html'
|
template_name = 'ops/celery_task_log.html'
|
||||||
model = CeleryTask
|
model = CeleryTask
|
||||||
|
|
|
@ -1,14 +1,68 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import status
|
||||||
|
from rest_framework.views import Response
|
||||||
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
|
||||||
from common.permissions import IsSuperUserOrAppUser
|
from common.permissions import IsSuperUserOrAppUser
|
||||||
from .models import Organization
|
from .models import Organization
|
||||||
from .serializers import OrgSerializer
|
from .serializers import OrgSerializer, OrgReadSerializer, \
|
||||||
|
OrgMembershipUserSerializer, OrgMembershipAdminSerializer
|
||||||
|
from users.models import User, UserGroup
|
||||||
|
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
|
||||||
|
from perms.models import AssetPermission
|
||||||
|
from orgs.utils import current_org
|
||||||
|
from common.utils import get_logger
|
||||||
|
from .mixins import OrgMembershipModelViewSetMixin
|
||||||
|
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class OrgViewSet(viewsets.ModelViewSet):
|
class OrgViewSet(BulkModelViewSet):
|
||||||
queryset = Organization.objects.all()
|
queryset = Organization.objects.all()
|
||||||
serializer_class = OrgSerializer
|
serializer_class = OrgSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsSuperUserOrAppUser,)
|
||||||
|
org = None
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
if self.action in ('list', 'retrieve'):
|
||||||
|
return OrgReadSerializer
|
||||||
|
else:
|
||||||
|
return super().get_serializer_class()
|
||||||
|
|
||||||
|
def get_data_from_model(self, model):
|
||||||
|
if model == User:
|
||||||
|
data = model.objects.filter(orgs__id=self.org.id)
|
||||||
|
else:
|
||||||
|
data = model.objects.filter(org_id=self.org.id)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
self.org = self.get_object()
|
||||||
|
models = [
|
||||||
|
User, UserGroup,
|
||||||
|
Asset, Domain, AdminUser, SystemUser, Label,
|
||||||
|
AssetPermission,
|
||||||
|
]
|
||||||
|
for model in models:
|
||||||
|
data = self.get_data_from_model(model)
|
||||||
|
if data:
|
||||||
|
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
if str(current_org) == str(self.org):
|
||||||
|
return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||||
|
self.org.delete()
|
||||||
|
return Response({'msg': True}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMembershipAdminsViewSet(OrgMembershipModelViewSetMixin, BulkModelViewSet):
|
||||||
|
serializer_class = OrgMembershipAdminSerializer
|
||||||
|
membership_class = Organization.admins.through
|
||||||
|
permission_classes = (IsSuperUserOrAppUser, )
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMembershipUsersViewSet(OrgMembershipModelViewSetMixin, BulkModelViewSet):
|
||||||
|
serializer_class = OrgMembershipUserSerializer
|
||||||
|
membership_class = Organization.users.through
|
||||||
|
permission_classes = (IsSuperUserOrAppUser, )
|
||||||
|
|
|
@ -9,7 +9,6 @@ from django.forms import ModelForm
|
||||||
from django.http.response import HttpResponseForbidden
|
from django.http.response import HttpResponseForbidden
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from .utils import current_org, set_current_org, set_to_root_org
|
from .utils import current_org, set_current_org, set_to_root_org
|
||||||
from .models import Organization
|
from .models import Organization
|
||||||
|
@ -19,7 +18,7 @@ tl = Local()
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm',
|
'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm',
|
||||||
'RootOrgViewMixin',
|
'RootOrgViewMixin', 'OrgMembershipSerializerMixin', 'OrgMembershipModelViewSetMixin'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,3 +175,29 @@ class OrgModelForm(ModelForm):
|
||||||
continue
|
continue
|
||||||
model = field.queryset.model
|
model = field.queryset.model
|
||||||
field.queryset = model.objects.all()
|
field.queryset = model.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMembershipSerializerMixin:
|
||||||
|
def run_validation(self, initial_data=None):
|
||||||
|
initial_data['organization'] = str(self.context['org'].id)
|
||||||
|
return super().run_validation(initial_data)
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMembershipModelViewSetMixin:
|
||||||
|
org = None
|
||||||
|
membership_class = None
|
||||||
|
lookup_field = 'user'
|
||||||
|
lookup_url_kwarg = 'user_id'
|
||||||
|
http_method_names = ['get', 'post', 'delete', 'head', 'options']
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
self.org = Organization.objects.get(pk=kwargs.get('org_id'))
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_serializer_context(self):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context['org'] = self.org
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.membership_class.objects.filter(organization=self.org)
|
||||||
|
|
|
@ -1,10 +1,81 @@
|
||||||
|
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework_bulk import BulkListSerializer
|
||||||
|
|
||||||
|
from users.models import User, UserGroup
|
||||||
|
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
|
||||||
|
from perms.models import AssetPermission
|
||||||
|
from .utils import set_current_org, get_current_org
|
||||||
from .models import Organization
|
from .models import Organization
|
||||||
|
from .mixins import OrgMembershipSerializerMixin
|
||||||
|
|
||||||
|
|
||||||
class OrgSerializer(ModelSerializer):
|
class OrgSerializer(ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Organization
|
model = Organization
|
||||||
|
list_serializer_class = BulkListSerializer
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = ['id', 'created_by', 'date_created']
|
read_only_fields = ['id', 'created_by', 'date_created']
|
||||||
|
|
||||||
|
|
||||||
|
class OrgReadSerializer(ModelSerializer):
|
||||||
|
admins = serializers.SlugRelatedField(slug_field='name', many=True, read_only=True)
|
||||||
|
users = serializers.SlugRelatedField(slug_field='name', many=True, read_only=True)
|
||||||
|
user_groups = serializers.SerializerMethodField()
|
||||||
|
assets = serializers.SerializerMethodField()
|
||||||
|
domains = serializers.SerializerMethodField()
|
||||||
|
admin_users = serializers.SerializerMethodField()
|
||||||
|
system_users = serializers.SerializerMethodField()
|
||||||
|
labels = serializers.SerializerMethodField()
|
||||||
|
perms = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Organization
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_data_from_model(obj, model):
|
||||||
|
current_org = get_current_org()
|
||||||
|
set_current_org(Organization.root())
|
||||||
|
if model == Asset:
|
||||||
|
data = [o.hostname for o in model.objects.filter(org_id=obj.id)]
|
||||||
|
else:
|
||||||
|
data = [o.name for o in model.objects.filter(org_id=obj.id)]
|
||||||
|
set_current_org(current_org)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_user_groups(self, obj):
|
||||||
|
return self.get_data_from_model(obj, UserGroup)
|
||||||
|
|
||||||
|
def get_assets(self, obj):
|
||||||
|
return self.get_data_from_model(obj, Asset)
|
||||||
|
|
||||||
|
def get_domains(self, obj):
|
||||||
|
return self.get_data_from_model(obj, Domain)
|
||||||
|
|
||||||
|
def get_admin_users(self, obj):
|
||||||
|
return self.get_data_from_model(obj, AdminUser)
|
||||||
|
|
||||||
|
def get_system_users(self, obj):
|
||||||
|
return self.get_data_from_model(obj, SystemUser)
|
||||||
|
|
||||||
|
def get_labels(self, obj):
|
||||||
|
return self.get_data_from_model(obj, Label)
|
||||||
|
|
||||||
|
def get_perms(self, obj):
|
||||||
|
return self.get_data_from_model(obj, AssetPermission)
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMembershipAdminSerializer(OrgMembershipSerializerMixin, ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Organization.admins.through
|
||||||
|
list_serializer_class = BulkListSerializer
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMembershipUserSerializer(OrgMembershipSerializerMixin, ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Organization.users.through
|
||||||
|
list_serializer_class = BulkListSerializer
|
||||||
|
fields = '__all__'
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from .. import api
|
from .. import api
|
||||||
|
|
||||||
|
|
||||||
app_name = 'orgs'
|
app_name = 'orgs'
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
|
|
||||||
|
router.register(r'org/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins',
|
||||||
|
api.OrgMembershipAdminsViewSet, 'membership-admins')
|
||||||
|
|
||||||
|
router.register(r'org/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
|
||||||
|
api.OrgMembershipUsersViewSet, 'membership-users'),
|
||||||
|
|
||||||
router.register(r'orgs', api.OrgViewSet, 'org')
|
router.register(r'orgs', api.OrgViewSet, 'org')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveUpdateAPIView
|
from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveUpdateAPIView
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from common.utils import set_or_append_attr_bulk
|
from common.utils import set_or_append_attr_bulk
|
||||||
from common.permissions import IsValidUser, IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsValidUser, IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
|
@ -15,6 +16,7 @@ from .hands import AssetGrantedSerializer, User, UserGroup, Asset, Node, \
|
||||||
NodeGrantedSerializer, SystemUser, NodeSerializer
|
NodeGrantedSerializer, SystemUser, NodeSerializer
|
||||||
from orgs.utils import set_to_root_org
|
from orgs.utils import set_to_root_org
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
from .mixins import AssetsFilterMixin
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -32,6 +34,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
queryset = AssetPermission.objects.all()
|
queryset = AssetPermission.objects.all()
|
||||||
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
|
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
|
@ -40,10 +43,15 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
return self.serializer_class
|
return self.serializer_class
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super().get_queryset()
|
queryset = super().get_queryset().all()
|
||||||
|
search = self.request.query_params.get('search')
|
||||||
asset_id = self.request.query_params.get('asset')
|
asset_id = self.request.query_params.get('asset')
|
||||||
node_id = self.request.query_params.get('node')
|
node_id = self.request.query_params.get('node')
|
||||||
inherit_nodes = set()
|
inherit_nodes = set()
|
||||||
|
|
||||||
|
if search:
|
||||||
|
queryset = queryset.filter(name__icontains=search)
|
||||||
|
|
||||||
if not asset_id and not node_id:
|
if not asset_id and not node_id:
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
@ -62,15 +70,17 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
_permissions = queryset.filter(nodes=n)
|
_permissions = queryset.filter(nodes=n)
|
||||||
set_or_append_attr_bulk(_permissions, "inherit", n.value)
|
set_or_append_attr_bulk(_permissions, "inherit", n.value)
|
||||||
permissions.update(_permissions)
|
permissions.update(_permissions)
|
||||||
return permissions
|
|
||||||
|
return list(permissions)
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedAssetsApi(ListAPIView):
|
class UserGrantedAssetsApi(AssetsFilterMixin, ListAPIView):
|
||||||
"""
|
"""
|
||||||
用户授权的所有资产
|
用户授权的所有资产
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = AssetGrantedSerializer
|
serializer_class = AssetGrantedSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def change_org_if_need(self):
|
def change_org_if_need(self):
|
||||||
if self.request.user.is_superuser or \
|
if self.request.user.is_superuser or \
|
||||||
|
@ -93,6 +103,7 @@ class UserGrantedAssetsApi(ListAPIView):
|
||||||
system_users_granted = [s for s in v if s.protocol == k.protocol]
|
system_users_granted = [s for s in v if s.protocol == k.protocol]
|
||||||
k.system_users_granted = system_users_granted
|
k.system_users_granted = system_users_granted
|
||||||
queryset.append(k)
|
queryset.append(k)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
|
@ -131,7 +142,7 @@ class UserGrantedNodesApi(ListAPIView):
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodesWithAssetsApi(ListAPIView):
|
class UserGrantedNodesWithAssetsApi(AssetsFilterMixin, ListAPIView):
|
||||||
"""
|
"""
|
||||||
用户授权的节点并带着节点下资产的api
|
用户授权的节点并带着节点下资产的api
|
||||||
"""
|
"""
|
||||||
|
@ -166,19 +177,25 @@ class UserGrantedNodesWithAssetsApi(ListAPIView):
|
||||||
queryset.append(node)
|
queryset.append(node)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
def sort_assets(self, queryset):
|
||||||
|
for node in queryset:
|
||||||
|
node.assets_granted = super().sort_assets(node.assets_granted)
|
||||||
|
return queryset
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
if self.kwargs.get('pk') is None:
|
if self.kwargs.get('pk') is None:
|
||||||
self.permission_classes = (IsValidUser,)
|
self.permission_classes = (IsValidUser,)
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodeAssetsApi(ListAPIView):
|
class UserGrantedNodeAssetsApi(AssetsFilterMixin, ListAPIView):
|
||||||
"""
|
"""
|
||||||
查询用户授权的节点下的资产的api, 与上面api不同的是,只返回某个节点下的资产
|
查询用户授权的节点下的资产的api, 与上面api不同的是,只返回某个节点下的资产
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = AssetGrantedSerializer
|
serializer_class = AssetGrantedSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def change_org_if_need(self):
|
def change_org_if_need(self):
|
||||||
if self.request.user.is_superuser or \
|
if self.request.user.is_superuser or \
|
||||||
self.request.user.is_app or \
|
self.request.user.is_app or \
|
||||||
|
@ -200,6 +217,8 @@ class UserGrantedNodeAssetsApi(ListAPIView):
|
||||||
assets = nodes.get(node, [])
|
assets = nodes.get(node, [])
|
||||||
for asset, system_users in assets.items():
|
for asset, system_users in assets.items():
|
||||||
asset.system_users_granted = system_users
|
asset.system_users_granted = system_users
|
||||||
|
|
||||||
|
assets = list(assets.keys())
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class AssetsFilterMixin(object):
|
||||||
|
"""
|
||||||
|
对资产进行过滤(查询,排序)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def filter_queryset(self, queryset):
|
||||||
|
queryset = self.search_assets(queryset)
|
||||||
|
queryset = self.sort_assets(queryset)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def search_assets(self, queryset):
|
||||||
|
from perms.utils import is_obj_attr_has
|
||||||
|
value = self.request.query_params.get('search')
|
||||||
|
if not value:
|
||||||
|
return queryset
|
||||||
|
queryset = [asset for asset in queryset if is_obj_attr_has(asset, value)]
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def sort_assets(self, queryset):
|
||||||
|
from perms.utils import sort_assets
|
||||||
|
order_by = self.request.query_params.get('order')
|
||||||
|
if not order_by:
|
||||||
|
order_by = 'hostname'
|
||||||
|
|
||||||
|
if order_by.startswith('-'):
|
||||||
|
order_by = order_by.lstrip('-')
|
||||||
|
reverse = True
|
||||||
|
else:
|
||||||
|
reverse = False
|
||||||
|
|
||||||
|
queryset = sort_assets(queryset, order_by=order_by, reverse=reverse)
|
||||||
|
return queryset
|
|
@ -24,7 +24,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
|
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
|
||||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a>
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and node' %}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -54,9 +54,9 @@
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-daterange input-group" id="datepicker">
|
<div class="input-daterange input-group" id="datepicker">
|
||||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||||
<input type="text" class="input-sm form-control" name="date_start" value="{{ form.date_start.value|date:'Y-m-d' }}">
|
<input type="text" class="input-sm form-control" id="date_start" name="date_start" value="{{ form.date_start.value|date:'Y-m-d H:i' }}">
|
||||||
<span class="input-group-addon">to</span>
|
<span class="input-group-addon">to</span>
|
||||||
<input type="text" class="input-sm form-control" name="date_expired" value="{{ form.date_expired.value|date:'Y-m-d' }}">
|
<input type="text" class="input-sm form-control" id="date_expired" name="date_expired" value="{{ form.date_expired.value|date:'Y-m-d H:i' }}">
|
||||||
</div>
|
</div>
|
||||||
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
||||||
<span class="help-block ">{{ form.date_start.errors }}</span>
|
<span class="help-block ">{{ form.date_start.errors }}</span>
|
||||||
|
@ -70,6 +70,7 @@
|
||||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,19 +81,27 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||||
|
<script type="text/javascript" src='{% static "js/plugins/daterangepicker/moment.min.js" %}'></script>
|
||||||
|
<script type="text/javascript" src='{% static "js/plugins/daterangepicker/daterangepicker.min.js" %}'></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href={% static "css/plugins/daterangepicker/daterangepicker.css" %} />
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
var dateOptions = {
|
||||||
|
singleDatePicker: true,
|
||||||
|
showDropdowns: true,
|
||||||
|
timePicker: true,
|
||||||
|
timePicker24Hour: true,
|
||||||
|
autoApply: true,
|
||||||
|
locale: {
|
||||||
|
format: 'YYYY-MM-DD HH:mm'
|
||||||
|
}
|
||||||
|
};
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2({
|
$('.select2').select2({
|
||||||
closeOnSelect: false
|
closeOnSelect: false
|
||||||
});
|
});
|
||||||
$('#datepicker').datepicker({
|
$('#date_start').daterangepicker(dateOptions);
|
||||||
format: "yyyy-mm-dd",
|
$('#date_expired').daterangepicker(dateOptions);
|
||||||
todayBtn: "linked",
|
|
||||||
keyboardNavigation: false,
|
|
||||||
forceParse: false,
|
|
||||||
calendarWeeks: true,
|
|
||||||
autoclose: true
|
|
||||||
});
|
|
||||||
$("#id_assets").parent().find(".select2-selection").on('click', function (e) {
|
$("#id_assets").parent().find(".select2-selection").on('click', function (e) {
|
||||||
if ($(e.target).attr('class') !== 'select2-selection__choice__remove'){
|
if ($(e.target).attr('class') !== 'select2-selection__choice__remove'){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -110,6 +119,6 @@ $(document).ready(function () {
|
||||||
$('.select2').val(assets).trigger('change');
|
$('.select2').val(assets).trigger('change');
|
||||||
});
|
});
|
||||||
$("#asset_list_modal").modal('hide');
|
$("#asset_list_modal").modal('hide');
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -24,7 +24,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'perms:asset-permission-asset-list' pk=object.id %}" class="text-center">
|
<a href="{% url 'perms:asset-permission-asset-list' pk=object.id %}" class="text-center">
|
||||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a>
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and node' %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pull-right">
|
<li class="pull-right">
|
||||||
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||||
|
|
|
@ -217,7 +217,7 @@ function initTable() {
|
||||||
select: {},
|
select: {},
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
table = jumpserver.initDataTable(options);
|
table = jumpserver.initServerSideDataTable(options);
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
|
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
|
||||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a>
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and node' %}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -156,3 +156,22 @@ class AssetPermissionUtil:
|
||||||
return tree.nodes
|
return tree.nodes
|
||||||
|
|
||||||
|
|
||||||
|
def is_obj_attr_has(obj, val, attrs=("hostname", "ip", "comment")):
|
||||||
|
if not attrs:
|
||||||
|
vals = [val for val in obj.__dict__.values() if isinstance(val, (str, int))]
|
||||||
|
else:
|
||||||
|
vals = [getattr(obj, attr) for attr in attrs if
|
||||||
|
hasattr(obj, attr) and isinstance(hasattr(obj, attr), (str, int))]
|
||||||
|
|
||||||
|
for v in vals:
|
||||||
|
if str(v).find(val) != -1:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def sort_assets(assets, order_by='hostname', reverse=False):
|
||||||
|
if order_by == 'ip':
|
||||||
|
assets = sorted(assets, key=lambda asset: [int(d) for d in asset.ip.split('.') if d.isdigit()], reverse=reverse)
|
||||||
|
else:
|
||||||
|
assets = sorted(assets, key=lambda asset: getattr(asset, order_by), reverse=reverse)
|
||||||
|
return assets
|
||||||
|
|
|
@ -0,0 +1,388 @@
|
||||||
|
.daterangepicker {
|
||||||
|
position: absolute;
|
||||||
|
color: inherit;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
width: 278px;
|
||||||
|
max-width: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 7px;
|
||||||
|
top: 100px;
|
||||||
|
left: 20px;
|
||||||
|
z-index: 3001;
|
||||||
|
display: none;
|
||||||
|
font-family: arial;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker:before, .daterangepicker:after {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker:before {
|
||||||
|
top: -7px;
|
||||||
|
border-right: 7px solid transparent;
|
||||||
|
border-left: 7px solid transparent;
|
||||||
|
border-bottom: 7px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker:after {
|
||||||
|
top: -6px;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-bottom: 6px solid #fff;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.opensleft:before {
|
||||||
|
right: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.opensleft:after {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.openscenter:before {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.openscenter:after {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.opensright:before {
|
||||||
|
left: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.opensright:after {
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.drop-up {
|
||||||
|
margin-top: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.drop-up:before {
|
||||||
|
top: initial;
|
||||||
|
bottom: -7px;
|
||||||
|
border-bottom: initial;
|
||||||
|
border-top: 7px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.drop-up:after {
|
||||||
|
top: initial;
|
||||||
|
bottom: -6px;
|
||||||
|
border-bottom: initial;
|
||||||
|
border-top: 6px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.single .drp-selected {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.show-calendar .drp-calendar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.show-calendar .drp-buttons {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.auto-apply .drp-buttons {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .drp-calendar {
|
||||||
|
display: none;
|
||||||
|
max-width: 270px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .drp-calendar.left {
|
||||||
|
padding: 8px 0 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .drp-calendar.right {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .drp-calendar.single .calendar-table {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
|
||||||
|
color: #fff;
|
||||||
|
border: solid black;
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
border-radius: 0;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-table .next span {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
-webkit-transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-table .prev span {
|
||||||
|
transform: rotate(135deg);
|
||||||
|
-webkit-transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
min-width: 32px;
|
||||||
|
width: 32px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-table {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-table table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
border-color: transparent;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.week, .daterangepicker th.week {
|
||||||
|
font-size: 80%;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: transparent;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.in-range {
|
||||||
|
background-color: #ebf4f8;
|
||||||
|
border-color: transparent;
|
||||||
|
color: #000;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.start-date {
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.end-date {
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.start-date.end-date {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.active, .daterangepicker td.active:hover {
|
||||||
|
background-color: #357ebd;
|
||||||
|
border-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker th.month {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.disabled, .daterangepicker option.disabled {
|
||||||
|
color: #999;
|
||||||
|
cursor: not-allowed;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 1px;
|
||||||
|
height: auto;
|
||||||
|
margin: 0;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker select.monthselect {
|
||||||
|
margin-right: 2%;
|
||||||
|
width: 56%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker select.yearselect {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
|
||||||
|
width: 50px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #eee;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 2px;
|
||||||
|
outline: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-time {
|
||||||
|
text-align: center;
|
||||||
|
margin: 4px auto 0 auto;
|
||||||
|
line-height: 30px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar-time select.disabled {
|
||||||
|
color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .drp-buttons {
|
||||||
|
clear: both;
|
||||||
|
text-align: right;
|
||||||
|
padding: 8px;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
display: none;
|
||||||
|
line-height: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .drp-selected {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 12px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .drp-buttons .btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.show-ranges .drp-calendar.left {
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .ranges {
|
||||||
|
float: none;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.show-calendar .ranges {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .ranges ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .ranges li {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .ranges li:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .ranges li.active {
|
||||||
|
background-color: #08c;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Larger Screen Styling */
|
||||||
|
@media (min-width: 564px) {
|
||||||
|
.daterangepicker {
|
||||||
|
width: auto; }
|
||||||
|
.daterangepicker .ranges ul {
|
||||||
|
width: 140px; }
|
||||||
|
.daterangepicker.single .ranges ul {
|
||||||
|
width: 100%; }
|
||||||
|
.daterangepicker.single .drp-calendar.left {
|
||||||
|
clear: none; }
|
||||||
|
.daterangepicker.single.ltr .ranges, .daterangepicker.single.ltr .drp-calendar {
|
||||||
|
float: left; }
|
||||||
|
.daterangepicker.single.rtl .ranges, .daterangepicker.single.rtl .drp-calendar {
|
||||||
|
float: right; }
|
||||||
|
.daterangepicker.ltr {
|
||||||
|
direction: ltr;
|
||||||
|
text-align: left; }
|
||||||
|
.daterangepicker.ltr .drp-calendar.left {
|
||||||
|
clear: left;
|
||||||
|
margin-right: 0; }
|
||||||
|
.daterangepicker.ltr .drp-calendar.left .calendar-table {
|
||||||
|
border-right: none;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
.daterangepicker.ltr .drp-calendar.right {
|
||||||
|
margin-left: 0; }
|
||||||
|
.daterangepicker.ltr .drp-calendar.right .calendar-table {
|
||||||
|
border-left: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0; }
|
||||||
|
.daterangepicker.ltr .drp-calendar.left .calendar-table {
|
||||||
|
padding-right: 8px; }
|
||||||
|
.daterangepicker.ltr .ranges, .daterangepicker.ltr .drp-calendar {
|
||||||
|
float: left; }
|
||||||
|
.daterangepicker.rtl {
|
||||||
|
direction: rtl;
|
||||||
|
text-align: right; }
|
||||||
|
.daterangepicker.rtl .drp-calendar.left {
|
||||||
|
clear: right;
|
||||||
|
margin-left: 0; }
|
||||||
|
.daterangepicker.rtl .drp-calendar.left .calendar-table {
|
||||||
|
border-left: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0; }
|
||||||
|
.daterangepicker.rtl .drp-calendar.right {
|
||||||
|
margin-right: 0; }
|
||||||
|
.daterangepicker.rtl .drp-calendar.right .calendar-table {
|
||||||
|
border-right: none;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
.daterangepicker.rtl .drp-calendar.left .calendar-table {
|
||||||
|
padding-left: 12px; }
|
||||||
|
.daterangepicker.rtl .ranges, .daterangepicker.rtl .drp-calendar {
|
||||||
|
text-align: right;
|
||||||
|
float: right; } }
|
||||||
|
@media (min-width: 730px) {
|
||||||
|
.daterangepicker .ranges {
|
||||||
|
width: auto; }
|
||||||
|
.daterangepicker.ltr .ranges {
|
||||||
|
float: left; }
|
||||||
|
.daterangepicker.rtl .ranges {
|
||||||
|
float: right; }
|
||||||
|
.daterangepicker .drp-calendar.left {
|
||||||
|
clear: none !important; } }
|
|
@ -146,12 +146,15 @@ function activeNav() {
|
||||||
if (app === ''){
|
if (app === ''){
|
||||||
$('#index').addClass('active');
|
$('#index').addClass('active');
|
||||||
}
|
}
|
||||||
else if (app === 'xpack') {
|
else if (app === 'xpack' && resource === 'cloud') {
|
||||||
var item = url_array[3];
|
var item = url_array[3];
|
||||||
$("#" + app).addClass('active');
|
$("#" + app).addClass('active');
|
||||||
$('#' + app + ' #' + resource).addClass('active');
|
$('#' + app + ' #' + resource).addClass('active');
|
||||||
$('#' + app + ' #' + resource + ' #' + item + ' a').css('color', '#ffffff');
|
$('#' + app + ' #' + resource + ' #' + item + ' a').css('color', '#ffffff');
|
||||||
}
|
}
|
||||||
|
else if (app === 'settings'){
|
||||||
|
$("#" + app).addClass('active');
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
$("#" + app).addClass('active');
|
$("#" + app).addClass('active');
|
||||||
$('#' + app + ' #' + resource).addClass('active');
|
$('#' + app + ' #' + resource).addClass('active');
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -31,8 +31,8 @@
|
||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
{% if form.errors.all %}
|
{% if form.errors.all %}
|
||||||
<div class="alert alert-danger" style="margin: 20px auto 0px">
|
<div class="alert alert-danger" style="margin: 20px auto 0px">
|
||||||
{{ form.errors.all }}
|
{{ form.errors.all }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% block form %}
|
{% block form %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,6 +2,9 @@ from importlib import import_module
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from .command.serializers import SessionCommandSerializer
|
from .command.serializers import SessionCommandSerializer
|
||||||
|
|
||||||
|
from common import utils
|
||||||
|
from common.models import common_settings, Setting
|
||||||
|
|
||||||
TYPE_ENGINE_MAPPING = {
|
TYPE_ENGINE_MAPPING = {
|
||||||
'elasticsearch': 'terminal.backends.command.es',
|
'elasticsearch': 'terminal.backends.command.es',
|
||||||
}
|
}
|
||||||
|
@ -16,7 +19,9 @@ def get_command_storage():
|
||||||
|
|
||||||
def get_terminal_command_storages():
|
def get_terminal_command_storages():
|
||||||
storage_list = {}
|
storage_list = {}
|
||||||
for name, params in settings.TERMINAL_COMMAND_STORAGE.items():
|
command_storage = utils.get_command_storage_or_create_default_storage()
|
||||||
|
|
||||||
|
for name, params in command_storage.items():
|
||||||
tp = params['TYPE']
|
tp = params['TYPE']
|
||||||
if tp == 'server':
|
if tp == 'server':
|
||||||
storage = get_command_storage()
|
storage = get_command_storage()
|
||||||
|
|
|
@ -2,36 +2,33 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .models import Terminal
|
from .models import Terminal
|
||||||
|
|
||||||
|
|
||||||
def get_all_command_storage():
|
def get_all_command_storage():
|
||||||
# storage_choices = []
|
from common import utils
|
||||||
from common.models import Setting
|
command_storage = utils.get_command_storage_or_create_default_storage()
|
||||||
Setting.refresh_all_settings()
|
for k, v in command_storage.items():
|
||||||
for k, v in settings.TERMINAL_COMMAND_STORAGE.items():
|
|
||||||
yield (k, k)
|
yield (k, k)
|
||||||
|
|
||||||
|
|
||||||
def get_all_replay_storage():
|
def get_all_replay_storage():
|
||||||
# storage_choices = []
|
from common import utils
|
||||||
from common.models import Setting
|
replay_storage = utils.get_replay_storage_or_create_default_storage()
|
||||||
Setting.refresh_all_settings()
|
for k, v in replay_storage.items():
|
||||||
for k, v in settings.TERMINAL_REPLAY_STORAGE.items():
|
|
||||||
yield (k, k)
|
yield (k, k)
|
||||||
|
|
||||||
|
|
||||||
class TerminalForm(forms.ModelForm):
|
class TerminalForm(forms.ModelForm):
|
||||||
command_storage = forms.ChoiceField(
|
command_storage = forms.ChoiceField(
|
||||||
choices=get_all_command_storage(),
|
choices=get_all_command_storage,
|
||||||
label=_("Command storage"),
|
label=_("Command storage"),
|
||||||
help_text=_("Command can store in server db or ES, default to server, more see docs"),
|
help_text=_("Command can store in server db or ES, default to server, more see docs"),
|
||||||
)
|
)
|
||||||
replay_storage = forms.ChoiceField(
|
replay_storage = forms.ChoiceField(
|
||||||
choices=get_all_replay_storage(),
|
choices=get_all_replay_storage,
|
||||||
label=_("Replay storage"),
|
label=_("Replay storage"),
|
||||||
help_text=_("Replay file can store in server disk, AWS S3, Aliyun OSS, default to server, more see docs"),
|
help_text=_("Replay file can store in server disk, AWS S3, Aliyun OSS, default to server, more see docs"),
|
||||||
)
|
)
|
||||||
|
|
|
@ -43,10 +43,13 @@ class UserAuthApi(RootOrgViewMixin, APIView):
|
||||||
|
|
||||||
user, msg = self.check_user_valid(request)
|
user, msg = self.check_user_valid(request)
|
||||||
if not user:
|
if not user:
|
||||||
|
username = request.data.get('username', '')
|
||||||
|
exist = User.objects.filter(username=username).first()
|
||||||
|
reason = LoginLog.REASON_PASSWORD if exist else LoginLog.REASON_NOT_EXIST
|
||||||
data = {
|
data = {
|
||||||
'username': request.data.get('username', ''),
|
'username': username,
|
||||||
'mfa': LoginLog.MFA_UNKNOWN,
|
'mfa': LoginLog.MFA_UNKNOWN,
|
||||||
'reason': LoginLog.REASON_PASSWORD,
|
'reason': reason,
|
||||||
'status': False
|
'status': False
|
||||||
}
|
}
|
||||||
self.write_login_log(request, data)
|
self.write_login_log(request, data)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from ..serializers import UserGroupSerializer, \
|
from ..serializers import UserGroupSerializer, \
|
||||||
UserGroupUpdateMemeberSerializer
|
UserGroupUpdateMemeberSerializer
|
||||||
|
@ -15,9 +16,12 @@ __all__ = ['UserGroupViewSet', 'UserGroupUpdateUserApi']
|
||||||
|
|
||||||
|
|
||||||
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
|
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||||
|
filter_fields = ("name",)
|
||||||
|
search_fields = filter_fields
|
||||||
queryset = UserGroup.objects.all()
|
queryset = UserGroup.objects.all()
|
||||||
serializer_class = UserGroupSerializer
|
serializer_class = UserGroupSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
|
|
||||||
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
||||||
|
|
|
@ -9,6 +9,7 @@ from rest_framework import generics
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from ..serializers import UserSerializer, UserPKUpdateSerializer, \
|
from ..serializers import UserSerializer, UserPKUpdateSerializer, \
|
||||||
UserUpdateGroupSerializer, ChangeUserPasswordSerializer
|
UserUpdateGroupSerializer, ChangeUserPasswordSerializer
|
||||||
|
@ -28,10 +29,12 @@ __all__ = [
|
||||||
|
|
||||||
|
|
||||||
class UserViewSet(IDInFilterMixin, BulkModelViewSet):
|
class UserViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||||
|
filter_fields = ('username', 'email', 'name', 'id')
|
||||||
|
search_fields = filter_fields
|
||||||
queryset = User.objects.exclude(role="App")
|
queryset = User.objects.exclude(role="App")
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
filter_fields = ('username', 'email', 'name', 'id')
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super().get_queryset()
|
queryset = super().get_queryset()
|
||||||
|
|
|
@ -55,11 +55,13 @@ class LoginLog(models.Model):
|
||||||
REASON_NOTHING = 0
|
REASON_NOTHING = 0
|
||||||
REASON_PASSWORD = 1
|
REASON_PASSWORD = 1
|
||||||
REASON_MFA = 2
|
REASON_MFA = 2
|
||||||
|
REASON_NOT_EXIST = 3
|
||||||
|
|
||||||
REASON_CHOICE = (
|
REASON_CHOICE = (
|
||||||
(REASON_NOTHING, _('-')),
|
(REASON_NOTHING, _('-')),
|
||||||
(REASON_PASSWORD, _('Username/password check failed')),
|
(REASON_PASSWORD, _('Username/password check failed')),
|
||||||
(REASON_MFA, _('MFA authentication failed')),
|
(REASON_MFA, _('MFA authentication failed')),
|
||||||
|
(REASON_NOT_EXIST, _("Username does not exist")),
|
||||||
)
|
)
|
||||||
|
|
||||||
STATUS_CHOICE = (
|
STATUS_CHOICE = (
|
||||||
|
@ -67,7 +69,7 @@ class LoginLog(models.Model):
|
||||||
(False, _('Failed'))
|
(False, _('Failed'))
|
||||||
)
|
)
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
username = models.CharField(max_length=20, verbose_name=_('Username'))
|
username = models.CharField(max_length=128, verbose_name=_('Username'))
|
||||||
type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type'))
|
type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type'))
|
||||||
ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
|
ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
|
||||||
city = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('Login city'))
|
city = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('Login city'))
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group date">
|
<div class="input-group date">
|
||||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||||
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
|
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d H:i' }}">
|
||||||
</div>
|
</div>
|
||||||
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,18 +52,24 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||||
|
<script type="text/javascript" src='{% static "js/plugins/daterangepicker/moment.min.js" %}'></script>
|
||||||
|
<script type="text/javascript" src='{% static "js/plugins/daterangepicker/daterangepicker.min.js" %}'></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href={% static "css/plugins/daterangepicker/daterangepicker.css" %} />
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
var dateOptions = {
|
||||||
|
singleDatePicker: true,
|
||||||
|
showDropdowns: true,
|
||||||
|
timePicker: true,
|
||||||
|
timePicker24Hour: true,
|
||||||
|
autoApply: true,
|
||||||
|
locale: {
|
||||||
|
format: 'YYYY-MM-DD HH:mm'
|
||||||
|
}
|
||||||
|
};
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2();
|
$('.select2').select2();
|
||||||
|
$('#id_date_expired').daterangepicker(dateOptions);
|
||||||
$('.input-group.date').datepicker({
|
|
||||||
format: "yyyy-mm-dd",
|
|
||||||
todayBtn: "linked",
|
|
||||||
keyboardNavigation: false,
|
|
||||||
forceParse: false,
|
|
||||||
calendarWeeks: true,
|
|
||||||
autoclose: true
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -103,7 +103,7 @@ function initTable() {
|
||||||
{data: "system_users_granted", orderable: false}
|
{data: "system_users_granted", orderable: false}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
asset_table = jumpserver.initDataTable(options);
|
asset_table = jumpserver.initServerSideDataTable(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
function onSelected(event, treeNode) {
|
||||||
|
|
|
@ -58,7 +58,8 @@ $(document).ready(function() {
|
||||||
order: [],
|
order: [],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
|
|
||||||
}).on('click', '.btn_delete_user_group', function(){
|
}).on('click', '.btn_delete_user_group', function(){
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var group_id = $this.data('gid');
|
var group_id = $this.data('gid');
|
||||||
|
|
|
@ -95,7 +95,7 @@ function initTable() {
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
table = jumpserver.initDataTable(options);
|
var table = jumpserver.initServerSideDataTable(options);
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ from formtools.wizard.views import SessionWizardView
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from common.utils import get_object_or_none, get_request_ip
|
from common.utils import get_object_or_none, get_request_ip
|
||||||
|
from common.models import common_settings
|
||||||
from ..models import User, LoginLog
|
from ..models import User, LoginLog
|
||||||
from ..utils import send_reset_password_mail, check_otp_code, \
|
from ..utils import send_reset_password_mail, check_otp_code, \
|
||||||
redirect_user_first_login_or_index, get_user_or_tmp_user, \
|
redirect_user_first_login_or_index, get_user_or_tmp_user, \
|
||||||
|
@ -78,12 +79,15 @@ class UserLoginView(FormView):
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
# write login failed log
|
# write login failed log
|
||||||
username = form.cleaned_data.get('username')
|
username = form.cleaned_data.get('username')
|
||||||
|
exist = User.objects.filter(username=username).first()
|
||||||
|
reason = LoginLog.REASON_PASSWORD if exist else LoginLog.REASON_NOT_EXIST
|
||||||
data = {
|
data = {
|
||||||
'username': username,
|
'username': username,
|
||||||
'mfa': LoginLog.MFA_UNKNOWN,
|
'mfa': LoginLog.MFA_UNKNOWN,
|
||||||
'reason': LoginLog.REASON_PASSWORD,
|
'reason': reason,
|
||||||
'status': False
|
'status': False
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_login_log(data)
|
self.write_login_log(data)
|
||||||
|
|
||||||
# limit user login failed count
|
# limit user login failed count
|
||||||
|
@ -318,7 +322,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
|
||||||
user.is_public_key_valid = True
|
user.is_public_key_valid = True
|
||||||
user.save()
|
user.save()
|
||||||
context = {
|
context = {
|
||||||
'user_guide_url': settings.USER_GUIDE_URL
|
'user_guide_url': common_settings.USER_GUIDE_URL
|
||||||
}
|
}
|
||||||
return render(self.request, 'users/first_login_done.html', context)
|
return render(self.request, 'users/first_login_done.html', context)
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite gcc automake libkrb5-dev
|
libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite gcc automake libkrb5-dev sshpass
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
libtiff-devel libjpeg-devel libzip-devel freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel sshpass openldap-devel mysql-devel libffi-devel openssh-clients
|
libtiff-devel libjpeg-devel libzip-devel freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel sshpass openldap-devel mariadb-devel libffi-devel openssh-clients
|
||||||
|
|
Loading…
Reference in New Issue