mirror of https://github.com/jumpserver/jumpserver
[Update] 修改session
parent
dfcbdb0c35
commit
c3a54a8927
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from rest_framework import filters
|
||||||
|
from rest_framework.fields import DateTimeField
|
||||||
|
from rest_framework.serializers import ValidationError
|
||||||
|
import logging
|
||||||
|
|
||||||
|
__all__ = ["DatetimeRangeFilter"]
|
||||||
|
|
||||||
|
|
||||||
|
class DatetimeRangeFilter(filters.BaseFilterBackend):
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
if not hasattr(view, 'date_range_filter_fields'):
|
||||||
|
return queryset
|
||||||
|
try:
|
||||||
|
fields = dict(view.date_range_filter_fields)
|
||||||
|
except ValueError:
|
||||||
|
msg = "View {} datetime_filter_fields set is error".format(view.name)
|
||||||
|
logging.error(msg)
|
||||||
|
return queryset
|
||||||
|
kwargs = {}
|
||||||
|
for attr, date_range_keyword in fields.items():
|
||||||
|
if len(date_range_keyword) != 2:
|
||||||
|
continue
|
||||||
|
for i, v in enumerate(date_range_keyword):
|
||||||
|
value = request.query_params.get(v)
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
field = DateTimeField()
|
||||||
|
value = field.to_internal_value(value)
|
||||||
|
if i == 0:
|
||||||
|
lookup = "__gte"
|
||||||
|
else:
|
||||||
|
lookup = "__lte"
|
||||||
|
kwargs[attr+lookup] = value
|
||||||
|
except ValidationError as e:
|
||||||
|
print(e)
|
||||||
|
continue
|
||||||
|
if kwargs:
|
||||||
|
queryset = queryset.filter(**kwargs)
|
||||||
|
return queryset
|
|
@ -38,8 +38,10 @@ class IDInFilterMixin(object):
|
||||||
class IDInCacheFilterMixin(object):
|
class IDInCacheFilterMixin(object):
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
def filter_queryset(self, queryset):
|
||||||
queryset = super(IDInCacheFilterMixin, self).filter_queryset(queryset)
|
queryset = super().filter_queryset(queryset)
|
||||||
spm = self.request.query_params.get('spm')
|
spm = self.request.query_params.get('spm')
|
||||||
|
if not spm:
|
||||||
|
return queryset
|
||||||
cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
|
cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
|
||||||
resources_id = cache.get(cache_key)
|
resources_id = cache.get(cache_key)
|
||||||
if resources_id and isinstance(resources_id, list):
|
if resources_id and isinstance(resources_id, list):
|
||||||
|
|
|
@ -27,13 +27,14 @@ class BulkSerializerMixin(object):
|
||||||
if all((isinstance(self.root, BulkListSerializer),
|
if all((isinstance(self.root, BulkListSerializer),
|
||||||
id_attr,
|
id_attr,
|
||||||
request_method in ('PUT', 'PATCH'))):
|
request_method in ('PUT', 'PATCH'))):
|
||||||
id_field = self.fields[id_attr]
|
id_field = self.fields.get("id") or self.fields.get('pk')
|
||||||
if data.get("id"):
|
if data.get("id"):
|
||||||
id_value = id_field.to_internal_value(data.get("id"))
|
id_value = id_field.to_internal_value(data.get("id"))
|
||||||
else:
|
else:
|
||||||
id_value = id_field.to_internal_value(data.get("pk"))
|
id_value = id_field.to_internal_value(data.get("pk"))
|
||||||
|
print(">>>>>>>>>>>>>>>>>>>")
|
||||||
|
print(id_attr)
|
||||||
ret[id_attr] = id_value
|
ret[id_attr] = id_value
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,216 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
import traceback
|
|
||||||
from django.db import models
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.shortcuts import redirect, get_object_or_404
|
|
||||||
from django import forms
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.http.response import HttpResponseForbidden
|
|
||||||
from rest_framework import serializers
|
|
||||||
from rest_framework.validators import UniqueTogetherValidator
|
|
||||||
|
|
||||||
from common.utils import get_logger
|
|
||||||
from common.validators import ProjectUniqueValidator
|
|
||||||
from common.mixins import BulkSerializerMixin
|
|
||||||
from .utils import (
|
|
||||||
set_current_org, set_to_root_org, get_current_org, current_org,
|
|
||||||
get_current_org_id_for_serializer,
|
|
||||||
)
|
|
||||||
from .models import Organization
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm',
|
|
||||||
'RootOrgViewMixin', 'OrgMembershipSerializerMixin',
|
|
||||||
'OrgMembershipModelViewSetMixin', 'OrgResourceSerializerMixin',
|
|
||||||
'BulkOrgResourceSerializerMixin', 'BulkOrgResourceModelSerializer',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OrgManager(models.Manager):
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super(OrgManager, self).get_queryset()
|
|
||||||
kwargs = {}
|
|
||||||
|
|
||||||
_current_org = get_current_org()
|
|
||||||
if _current_org is None:
|
|
||||||
kwargs['id'] = None
|
|
||||||
elif _current_org.is_real():
|
|
||||||
kwargs['org_id'] = _current_org.id
|
|
||||||
elif _current_org.is_default():
|
|
||||||
queryset = queryset.filter(org_id="")
|
|
||||||
|
|
||||||
# lines = traceback.format_stack()
|
|
||||||
# print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
|
||||||
# for line in lines[-10:-5]:
|
|
||||||
# print(line)
|
|
||||||
# print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
|
||||||
|
|
||||||
queryset = queryset.filter(**kwargs)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def all(self):
|
|
||||||
if not current_org:
|
|
||||||
msg = 'You can `objects.set_current_org(org).all()` then run it'
|
|
||||||
return self
|
|
||||||
else:
|
|
||||||
return super(OrgManager, self).all()
|
|
||||||
|
|
||||||
def set_current_org(self, org):
|
|
||||||
if isinstance(org, str):
|
|
||||||
org = Organization.get_instance(org)
|
|
||||||
set_current_org(org)
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
class OrgModelMixin(models.Model):
|
|
||||||
org_id = models.CharField(max_length=36, blank=True, default='',
|
|
||||||
verbose_name=_("Organization"), db_index=True)
|
|
||||||
objects = OrgManager()
|
|
||||||
|
|
||||||
sep = '@'
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
if current_org is not None and current_org.is_real():
|
|
||||||
self.org_id = current_org.id
|
|
||||||
return super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def org(self):
|
|
||||||
from orgs.models import Organization
|
|
||||||
org = Organization.get_instance(self.org_id)
|
|
||||||
return org
|
|
||||||
|
|
||||||
@property
|
|
||||||
def org_name(self):
|
|
||||||
return self.org.name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fullname(self, attr=None):
|
|
||||||
name = ''
|
|
||||||
if attr and hasattr(self, attr):
|
|
||||||
name = getattr(self, attr)
|
|
||||||
elif hasattr(self, 'name'):
|
|
||||||
name = self.name
|
|
||||||
elif hasattr(self, 'hostname'):
|
|
||||||
name = self.hostname
|
|
||||||
if self.org.is_real():
|
|
||||||
return name + self.sep + self.org_name
|
|
||||||
else:
|
|
||||||
return name
|
|
||||||
|
|
||||||
def validate_unique(self, exclude=None):
|
|
||||||
"""
|
|
||||||
Check unique constraints on the model and raise ValidationError if any
|
|
||||||
failed.
|
|
||||||
Form 提交时会使用这个检验
|
|
||||||
"""
|
|
||||||
self.org_id = current_org.id if current_org.is_real() else ''
|
|
||||||
if exclude and 'org_id' in exclude:
|
|
||||||
exclude.remove('org_id')
|
|
||||||
unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
|
|
||||||
|
|
||||||
errors = self._perform_unique_checks(unique_checks)
|
|
||||||
date_errors = self._perform_date_checks(date_checks)
|
|
||||||
|
|
||||||
for k, v in date_errors.items():
|
|
||||||
errors.setdefault(k, []).extend(v)
|
|
||||||
|
|
||||||
if errors:
|
|
||||||
raise ValidationError(errors)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class OrgViewGenericMixin:
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
if current_org is None:
|
|
||||||
return redirect('orgs:switch-a-org')
|
|
||||||
|
|
||||||
if not current_org.can_admin_by(request.user):
|
|
||||||
if request.user.is_org_admin:
|
|
||||||
return redirect('orgs:switch-a-org')
|
|
||||||
return HttpResponseForbidden()
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class RootOrgViewMixin:
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
set_to_root_org()
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class OrgModelForm(forms.ModelForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
for name, field in self.fields.items():
|
|
||||||
if not hasattr(field, 'queryset'):
|
|
||||||
continue
|
|
||||||
model = field.queryset.model
|
|
||||||
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 = get_object_or_404(Organization, 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):
|
|
||||||
queryset = self.membership_class.objects.filter(organization=self.org)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class OrgResourceSerializerMixin(serializers.Serializer):
|
|
||||||
"""
|
|
||||||
通过API批量操作资源时, 自动给每个资源添加所需属性org_id的值为current_org_id
|
|
||||||
(同时为serializer.is_valid()对Model的unique_together校验做准备)
|
|
||||||
由于HiddenField字段不可读,API获取资产信息时获取不到org_id,
|
|
||||||
但是coco需要资产的org_id字段,所以修改为CharField类型
|
|
||||||
"""
|
|
||||||
org_id = serializers.ReadOnlyField(default=get_current_org_id_for_serializer, label=_("Organization"))
|
|
||||||
org_name = serializers.ReadOnlyField(label=_("Org name"))
|
|
||||||
|
|
||||||
def get_validators(self):
|
|
||||||
_validators = super().get_validators()
|
|
||||||
validators = []
|
|
||||||
|
|
||||||
for v in _validators:
|
|
||||||
if isinstance(v, UniqueTogetherValidator) \
|
|
||||||
and "org_id" in v.fields:
|
|
||||||
v = ProjectUniqueValidator(v.queryset, v.fields)
|
|
||||||
validators.append(v)
|
|
||||||
return validators
|
|
||||||
|
|
||||||
def get_field_names(self, declared_fields, info):
|
|
||||||
fields = super().get_field_names(declared_fields, info)
|
|
||||||
fields.extend(["org_id", "org_name"])
|
|
||||||
return fields
|
|
||||||
|
|
||||||
|
|
||||||
class BulkOrgResourceSerializerMixin(OrgResourceSerializerMixin, BulkSerializerMixin):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class BulkOrgResourceModelSerializer(BulkOrgResourceSerializerMixin, serializers.ModelSerializer):
|
|
||||||
pass
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from .models import *
|
||||||
|
from .serializers import *
|
||||||
|
from .forms import *
|
||||||
|
from .api import *
|
|
@ -0,0 +1,51 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
from common.mixins import IDInCacheFilterMixin
|
||||||
|
|
||||||
|
from ..utils import set_to_root_org
|
||||||
|
from ..models import Organization
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'RootOrgViewMixin', 'OrgMembershipModelViewSetMixin', 'OrgModelViewSet',
|
||||||
|
'OrgBulkModelViewSet',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RootOrgViewMixin:
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
set_to_root_org()
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class OrgModelViewSet(IDInCacheFilterMixin, ModelViewSet):
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().all()
|
||||||
|
|
||||||
|
|
||||||
|
class OrgBulkModelViewSet(IDInCacheFilterMixin, BulkModelViewSet):
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().all()
|
||||||
|
|
||||||
|
|
||||||
|
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 = get_object_or_404(Organization, 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):
|
||||||
|
queryset = self.membership_class.objects.filter(organization=self.org)
|
||||||
|
return queryset
|
|
@ -0,0 +1,16 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
__all__ = ['OrgModelForm']
|
||||||
|
|
||||||
|
|
||||||
|
class OrgModelForm(forms.ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
for name, field in self.fields.items():
|
||||||
|
if not hasattr(field, 'queryset'):
|
||||||
|
continue
|
||||||
|
model = field.queryset.model
|
||||||
|
field.queryset = model.objects.all()
|
|
@ -0,0 +1,115 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from common.utils import get_logger
|
||||||
|
from ..utils import (
|
||||||
|
set_current_org, get_current_org, current_org,
|
||||||
|
)
|
||||||
|
from ..models import Organization
|
||||||
|
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'OrgManager', 'OrgModelMixin',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OrgManager(models.Manager):
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super(OrgManager, self).get_queryset()
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
_current_org = get_current_org()
|
||||||
|
if _current_org is None:
|
||||||
|
kwargs['id'] = None
|
||||||
|
elif _current_org.is_real():
|
||||||
|
kwargs['org_id'] = _current_org.id
|
||||||
|
elif _current_org.is_default():
|
||||||
|
queryset = queryset.filter(org_id="")
|
||||||
|
|
||||||
|
# lines = traceback.format_stack()
|
||||||
|
# print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
||||||
|
# for line in lines[-10:-5]:
|
||||||
|
# print(line)
|
||||||
|
# print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
||||||
|
|
||||||
|
queryset = queryset.filter(**kwargs)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
if not current_org:
|
||||||
|
msg = 'You can `objects.set_current_org(org).all()` then run it'
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
return super(OrgManager, self).all()
|
||||||
|
|
||||||
|
def set_current_org(self, org):
|
||||||
|
if isinstance(org, str):
|
||||||
|
org = Organization.get_instance(org)
|
||||||
|
set_current_org(org)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class OrgModelMixin(models.Model):
|
||||||
|
org_id = models.CharField(max_length=36, blank=True, default='',
|
||||||
|
verbose_name=_("Organization"), db_index=True)
|
||||||
|
objects = OrgManager()
|
||||||
|
|
||||||
|
sep = '@'
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if current_org is not None and current_org.is_real():
|
||||||
|
self.org_id = current_org.id
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def org(self):
|
||||||
|
from orgs.models import Organization
|
||||||
|
org = Organization.get_instance(self.org_id)
|
||||||
|
return org
|
||||||
|
|
||||||
|
@property
|
||||||
|
def org_name(self):
|
||||||
|
return self.org.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fullname(self, attr=None):
|
||||||
|
name = ''
|
||||||
|
if attr and hasattr(self, attr):
|
||||||
|
name = getattr(self, attr)
|
||||||
|
elif hasattr(self, 'name'):
|
||||||
|
name = self.name
|
||||||
|
elif hasattr(self, 'hostname'):
|
||||||
|
name = self.hostname
|
||||||
|
if self.org.is_real():
|
||||||
|
return name + self.sep + self.org_name
|
||||||
|
else:
|
||||||
|
return name
|
||||||
|
|
||||||
|
def validate_unique(self, exclude=None):
|
||||||
|
"""
|
||||||
|
Check unique constraints on the model and raise ValidationError if any
|
||||||
|
failed.
|
||||||
|
Form 提交时会使用这个检验
|
||||||
|
"""
|
||||||
|
self.org_id = current_org.id if current_org.is_real() else ''
|
||||||
|
if exclude and 'org_id' in exclude:
|
||||||
|
exclude.remove('org_id')
|
||||||
|
unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
|
||||||
|
|
||||||
|
errors = self._perform_unique_checks(unique_checks)
|
||||||
|
date_errors = self._perform_date_checks(date_checks)
|
||||||
|
|
||||||
|
for k, v in date_errors.items():
|
||||||
|
errors.setdefault(k, []).extend(v)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
|
@ -0,0 +1,56 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework.validators import UniqueTogetherValidator
|
||||||
|
|
||||||
|
from common.validators import ProjectUniqueValidator
|
||||||
|
from common.mixins import BulkSerializerMixin
|
||||||
|
from ..utils import get_current_org_id_for_serializer
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"OrgResourceSerializerMixin", "BulkOrgResourceSerializerMixin",
|
||||||
|
"BulkOrgResourceModelSerializer", "OrgMembershipSerializerMixin"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OrgResourceSerializerMixin(serializers.Serializer):
|
||||||
|
"""
|
||||||
|
通过API批量操作资源时, 自动给每个资源添加所需属性org_id的值为current_org_id
|
||||||
|
(同时为serializer.is_valid()对Model的unique_together校验做准备)
|
||||||
|
由于HiddenField字段不可读,API获取资产信息时获取不到org_id,
|
||||||
|
但是coco需要资产的org_id字段,所以修改为CharField类型
|
||||||
|
"""
|
||||||
|
org_id = serializers.ReadOnlyField(default=get_current_org_id_for_serializer, label=_("Organization"))
|
||||||
|
org_name = serializers.ReadOnlyField(label=_("Org name"))
|
||||||
|
|
||||||
|
def get_validators(self):
|
||||||
|
_validators = super().get_validators()
|
||||||
|
validators = []
|
||||||
|
|
||||||
|
for v in _validators:
|
||||||
|
if isinstance(v, UniqueTogetherValidator) \
|
||||||
|
and "org_id" in v.fields:
|
||||||
|
v = ProjectUniqueValidator(v.queryset, v.fields)
|
||||||
|
validators.append(v)
|
||||||
|
return validators
|
||||||
|
|
||||||
|
def get_field_names(self, declared_fields, info):
|
||||||
|
fields = super().get_field_names(declared_fields, info)
|
||||||
|
fields.extend(["org_id", "org_name"])
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
class BulkOrgResourceSerializerMixin(OrgResourceSerializerMixin, BulkSerializerMixin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BulkOrgResourceModelSerializer(BulkOrgResourceSerializerMixin, serializers.ModelSerializer):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMembershipSerializerMixin:
|
||||||
|
def run_validation(self, initial_data=None):
|
||||||
|
initial_data['organization'] = str(self.context['org'].id)
|
||||||
|
return super().run_validation(initial_data)
|
|
@ -1108,9 +1108,44 @@ function formatDateAsCN(d) {
|
||||||
|
|
||||||
function getUrlParams(url) {
|
function getUrlParams(url) {
|
||||||
url = url.split("?");
|
url = url.split("?");
|
||||||
let params = "";
|
var params = "";
|
||||||
if (url.length === 2){
|
if (url.length === 2){
|
||||||
params = url[1];
|
params = url[1];
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTimeUnits(u) {
|
||||||
|
var units = {
|
||||||
|
"d": "天",
|
||||||
|
"h": "时",
|
||||||
|
"m": "分",
|
||||||
|
"s": "秒",
|
||||||
|
};
|
||||||
|
if (navigator.language || "zh-CN") {
|
||||||
|
return units[u]
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeOffset(a, b) {
|
||||||
|
var start = new Date(a);
|
||||||
|
var end = new Date(b);
|
||||||
|
var offset = (end - start)/1000;
|
||||||
|
|
||||||
|
var days = offset / 3600 / 24;
|
||||||
|
var hours = offset / 3600;
|
||||||
|
var minutes = offset / 60;
|
||||||
|
var seconds = offset;
|
||||||
|
|
||||||
|
if (days > 1) {
|
||||||
|
return days.toFixed(1) + " " + getTimeUnits("d");
|
||||||
|
} else if (hours > 1) {
|
||||||
|
return hours.toFixed(1) + " " + getTimeUnits("h");
|
||||||
|
} else if (minutes > 1) {
|
||||||
|
return minutes.toFixed(1) + " " + getTimeUnits("m")
|
||||||
|
} else if (seconds > 1) {
|
||||||
|
return seconds.toFixed(1) + " " + getTimeUnits("s")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -9,12 +9,13 @@ from django.conf import settings
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework.generics import GenericAPIView
|
||||||
import jms_storage
|
import jms_storage
|
||||||
|
|
||||||
|
|
||||||
from common.utils import is_uuid, get_logger
|
from common.utils import is_uuid, get_logger
|
||||||
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
|
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
|
||||||
|
from common.filters import DatetimeRangeFilter
|
||||||
|
from orgs.mixins import OrgBulkModelViewSet
|
||||||
from ..hands import SystemUser
|
from ..hands import SystemUser
|
||||||
from ..models import Session
|
from ..models import Session
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
@ -24,12 +25,17 @@ __all__ = ['SessionViewSet', 'SessionReplayViewSet',]
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SessionViewSet(BulkModelViewSet):
|
class SessionViewSet(OrgBulkModelViewSet):
|
||||||
queryset = Session.objects.all()
|
queryset = Session.objects.all()
|
||||||
serializer_class = serializers.SessionSerializer
|
serializer_class = serializers.SessionSerializer
|
||||||
pagination_class = LimitOffsetPagination
|
pagination_class = LimitOffsetPagination
|
||||||
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
|
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
|
||||||
filter_fields = ["user", "asset", "system_user", "terminal"]
|
filter_fields = [
|
||||||
|
"user", "asset", "system_user", "terminal", "is_finished",
|
||||||
|
]
|
||||||
|
date_range_filter_fields = [
|
||||||
|
('date_start', ('date_from', 'date_to'))
|
||||||
|
]
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
# 解决guacamole更新session时并发导致幽灵会话的问题
|
# 解决guacamole更新session时并发导致幽灵会话的问题
|
||||||
|
@ -38,6 +44,12 @@ class SessionViewSet(BulkModelViewSet):
|
||||||
obj = obj.select_for_update()
|
obj = obj.select_for_update()
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@property
|
||||||
|
def filter_backends(self):
|
||||||
|
backends = list(GenericAPIView.filter_backends)
|
||||||
|
backends.append(DatetimeRangeFilter)
|
||||||
|
return backends
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
if hasattr(self.request.user, 'terminal'):
|
if hasattr(self.request.user, 'terminal'):
|
||||||
serializer.validated_data["terminal"] = self.request.user.terminal
|
serializer.validated_data["terminal"] = self.request.user.terminal
|
||||||
|
|
|
@ -241,6 +241,10 @@ class Session(OrgModelMixin):
|
||||||
command_store = get_multi_command_storage()
|
command_store = get_multi_command_storage()
|
||||||
return command_store.count(session=str(self.id))
|
return command_store.count(session=str(self.id))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def login_from_display(self):
|
||||||
|
return self.get_login_from_display()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "terminal_session"
|
db_table = "terminal_session"
|
||||||
ordering = ["-date_start"]
|
ordering = ["-date_start"]
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#
|
#
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from orgs.mixins import BulkOrgResourceModelSerializer
|
||||||
from common.mixins import BulkSerializerMixin
|
from common.mixins import BulkSerializerMixin
|
||||||
from common.serializers import AdaptedBulkListSerializer
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
from ..models import Terminal, Status, Session, Task
|
from ..models import Terminal, Status, Session, Task
|
||||||
|
@ -24,13 +25,18 @@ class TerminalSerializer(serializers.ModelSerializer):
|
||||||
return Session.objects.filter(terminal=obj, is_finished=False).count()
|
return Session.objects.filter(terminal=obj, is_finished=False).count()
|
||||||
|
|
||||||
|
|
||||||
class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
class SessionSerializer(BulkOrgResourceModelSerializer):
|
||||||
command_amount = serializers.IntegerField(read_only=True)
|
command_amount = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Session
|
model = Session
|
||||||
list_serializer_class = AdaptedBulkListSerializer
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
fields = '__all__'
|
fields = [
|
||||||
|
"id", "user", "asset", "system_user", "login_from",
|
||||||
|
"login_from_display", "remote_addr", "is_finished",
|
||||||
|
"has_replay", "can_replay", "protocol", "date_start", "date_end",
|
||||||
|
"terminal", "command_amount",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class StatusSerializer(serializers.ModelSerializer):
|
class StatusSerializer(serializers.ModelSerializer):
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
.toggle {
|
.toggle {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-key {
|
.detail-key {
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,65 +7,21 @@
|
||||||
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||||
<style>
|
|
||||||
#search_btn {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content_left_head %}
|
{% block content_left_head %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_pagination %}
|
||||||
{% block table_search %}
|
|
||||||
<form id="search_form" method="get" action="" class="pull-right form-inline">
|
|
||||||
<div class="form-group" id="date">
|
|
||||||
<div class="input-daterange input-group" id="datepicker">
|
|
||||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
|
||||||
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from|date:'Y-m-d' }}">
|
|
||||||
<span class="input-group-addon">to</span>
|
|
||||||
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to|date:'Y-m-d' }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="input-group">
|
|
||||||
<select class="select2 form-control" name="user">
|
|
||||||
<option value="">{% trans 'User' %}</option>
|
|
||||||
{% for u in user_list %}
|
|
||||||
<option value="{{ u }}" {% if u == user %} selected {% endif %}>{{ u }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="input-group">
|
|
||||||
<select class="select2 form-control" name="asset">
|
|
||||||
<option value="">{% trans 'Asset' %}</option>
|
|
||||||
{% for a in asset_list %}
|
|
||||||
<option value="{{ a }}" {% if a == asset %} selected {% endif %}>{{ a }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="input-group">
|
|
||||||
<select class="select2 form-control" name="system_user">
|
|
||||||
<option value="">{% trans 'System user' %}</option>
|
|
||||||
{% for su in system_user_list %}
|
|
||||||
<option value="{{ su }}" {% if su == system_user %} selected {% endif %}>{{ su }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
{# <div class="input-group">#}
|
|
||||||
{# <input type="text" class="form-control input-sm" name="keyword" placeholder="Keyword" value="{{ keyword }}">#}
|
|
||||||
{# </div>#}
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-btn">
|
|
||||||
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
|
||||||
{% trans 'Search' %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block table_head %}
|
{% block table_search %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_container %}
|
||||||
|
<table class="table table-striped table-bordered table-hover" id="session_table" data-page="false" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
<th class="text-center"></th>
|
<th class="text-center"></th>
|
||||||
<th class="text-center">{% trans 'ID' %}</th>
|
<th class="text-center">{% trans 'ID' %}</th>
|
||||||
<th class="text-center">{% trans 'User' %}</th>
|
<th class="text-center">{% trans 'User' %}</th>
|
||||||
|
@ -76,47 +32,16 @@
|
||||||
<th class="text-center">{% trans 'Login from' %}</th>
|
<th class="text-center">{% trans 'Login from' %}</th>
|
||||||
<th class="text-center">{% trans 'Command' %}</th>
|
<th class="text-center">{% trans 'Command' %}</th>
|
||||||
<th class="text-center">{% trans 'Date start' %}</th>
|
<th class="text-center">{% trans 'Date start' %}</th>
|
||||||
{# <th class="text-center">{% trans 'Date last active' %}</th>#}
|
|
||||||
<th class="text-center">{% trans 'Duration' %}</th>
|
<th class="text-center">{% trans 'Duration' %}</th>
|
||||||
<th class="text-center">{% trans 'Action' %}</th>
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block table_body %}
|
|
||||||
{% for session in session_list %}
|
|
||||||
<tr class="gradeX">
|
|
||||||
<td class="text-center"><input type="checkbox" class="cbx-term" value="{{ session.id }}"></td>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href="{% url 'terminal:session-detail' pk=session.id %}">{{ forloop.counter }}</a>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">{{ session.user }}</td>
|
|
||||||
<td class="text-center">{{ session.asset }}</td>
|
|
||||||
<td class="text-center">{{ session.system_user }}</td>
|
|
||||||
<td class="text-center">{{ session.remote_addr|default:"" }}</td>
|
|
||||||
<td class="text-center">{{ session.protocol }}</td>
|
|
||||||
<td class="text-center">{{ session.get_login_from_display }}</td>
|
|
||||||
<td class="text-center">{{ session.command_amount }}</td>
|
|
||||||
|
|
||||||
<td class="text-center">{{ session.date_start }}</td>
|
|
||||||
{# <td class="text-center">{{ session.date_last_active }}</td>#}
|
|
||||||
<td class="text-center">{{ session.date_start|time_util_with_seconds:session.date_end }}</td>
|
|
||||||
<td>
|
|
||||||
{% if session.is_finished %}
|
|
||||||
<a {% if not session.can_replay %} disabled="" {% endif %} onclick="window.open('/luna/replay/{{ session.id }}','luna', 'height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a>
|
|
||||||
{% else %}
|
|
||||||
{% if session.protocol == 'ssh' and request.user.is_org_admin%}
|
|
||||||
<a class="btn btn-xs btn-danger btn-term" value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a>
|
|
||||||
{% else %}
|
|
||||||
<a class="btn btn-xs btn-danger btn-term" disabled value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
</thead>
|
||||||
{% endblock %}
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
{% block content_bottom_left %}
|
<div id="actions" class="hide">
|
||||||
{% if request.user.is_org_admin %}
|
{% if type == "online" %}
|
||||||
<div id="actions" {% if type != "online" %} style="display: none" {% endif %}>
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
|
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
|
||||||
<option value="terminate">{% trans 'Terminate selected' %}</option>
|
<option value="terminate">{% trans 'Terminate selected' %}</option>
|
||||||
|
@ -128,14 +53,34 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hide" id="daterange">
|
||||||
|
<div class="form-group p-l-5" id="date" style="padding-left: 5px">
|
||||||
|
<div class="input-daterange input-group p-l-5" id="datepicker">
|
||||||
|
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" id="date_from" name="date_from" value="{{ date_from|date:'Y-m-d' }}">
|
||||||
|
<span class="input-group-addon">to</span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" id="date_to" name="date_to" value="{{ date_to|date:'Y-m-d' }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="dropdown-menu search-help">
|
||||||
|
<li><a class="search-item" data-value="user">{% trans 'User' %}</a></li>
|
||||||
|
<li><a class="search-item" data-value="asset">{% trans 'Asset' %}</a></li>
|
||||||
|
<li><a class="search-item" data-value="system_user">{% trans 'System user' %}</a></li>
|
||||||
|
<li><a class="search-item" data-value="input">{% trans 'Command' %}</a></li>
|
||||||
|
</ul>
|
||||||
{% 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>
|
<script>
|
||||||
function terminateSession(data) {
|
|
||||||
|
function terminateSession(data) {
|
||||||
function success() {
|
function success() {
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
|
@ -150,9 +95,78 @@
|
||||||
success: success,
|
success: success,
|
||||||
success_message: success_message
|
success_message: success_message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishedSession(data) {
|
var sessionListUrl = "{% url 'api-terminal:session-list' %}?is_finished={% if type == "online" %}0{% else %}1{% endif %}";
|
||||||
|
var dateFrom = "{{ date_from.timestamp }}";
|
||||||
|
var dateTo = "{{ date_to.timestamp }}";
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
dateFrom = new Date(dateFrom * 1000).toISOString();
|
||||||
|
dateTo = new Date(dateTo * 1000).toISOString();
|
||||||
|
sessionListUrl = setUrlParam(sessionListUrl, "date_from", dateFrom);
|
||||||
|
sessionListUrl = setUrlParam(sessionListUrl, "date_to", dateTo);
|
||||||
|
var options = {
|
||||||
|
ele: $('#session_table'),
|
||||||
|
ordering: false,
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData, i) {
|
||||||
|
var index = i + 1;
|
||||||
|
var data = '<a href="{% url 'terminal:session-detail' pk=DEFAULT_PK %}">'
|
||||||
|
+ index + "</a>";
|
||||||
|
data = data.replace("{{ DEFAULT_PK }}", cellData);
|
||||||
|
|
||||||
|
$(td).html(data);
|
||||||
|
}},
|
||||||
|
{targets: 9, createdCell: function (td, cellData) {
|
||||||
|
var data = formatDateAsCN(cellData);
|
||||||
|
$(td).html(data);
|
||||||
|
}},
|
||||||
|
{targets: 10, createdCell: function (td, cellData, rowData) {
|
||||||
|
var data = "";
|
||||||
|
if (cellData && rowData.date_start) {
|
||||||
|
data = timeOffset(rowData.date_start, cellData)
|
||||||
|
}
|
||||||
|
$(td).html(data);
|
||||||
|
}},
|
||||||
|
{targets: 11, createdCell: function (td, cellData, rowData) {
|
||||||
|
var btnGroup = "";
|
||||||
|
var replayBtn = '<a disabled data-session="sessionID" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a>'
|
||||||
|
{#var replayBtn = '<a disabled onclick="window.open("url", "height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no)" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a>'#}
|
||||||
|
replayBtn = replayBtn.replace("sessionID", rowData.id);
|
||||||
|
if (rowData.can_replay) {
|
||||||
|
replayBtn = replayBtn.replace("disabled", "")
|
||||||
|
}
|
||||||
|
var termBtn = '<a class="btn btn-xs btn-danger btn-term" disabled value="sessionID" terminal="terminalID" >{% trans "Terminate" %}</a>';
|
||||||
|
if (rowData.protocol === "ssh" && "{{ request.user.is_org_admin }}" === "True") {
|
||||||
|
termBtn = termBtn.replace("disabled", "")
|
||||||
|
.replace("sessionID", cellData)
|
||||||
|
.replace("terminalID", rowData.terminal)
|
||||||
|
}
|
||||||
|
if (rowData.is_finished) {
|
||||||
|
btnGroup += replayBtn
|
||||||
|
} else {
|
||||||
|
btnGroup += termBtn;
|
||||||
|
}
|
||||||
|
$(td).html(btnGroup);
|
||||||
|
}},
|
||||||
|
],
|
||||||
|
ajax_url: sessionListUrl,
|
||||||
|
columns: [
|
||||||
|
{data: "id"}, {data: "id"}, {data: "user", orderable: false},
|
||||||
|
{data: "asset", orderable: false}, {data: "system_user", orderable: false},
|
||||||
|
{data: "remote_addr"}, {data: "protocol"}, {data: "login_from_display"},
|
||||||
|
{data: "command_amount"}, {data: "date_start"},
|
||||||
|
{data: "date_end"}, {data: "id"},
|
||||||
|
],
|
||||||
|
op_html: $('#actions').html(),
|
||||||
|
fb_html: $("#daterange").html(),
|
||||||
|
};
|
||||||
|
table = jumpserver.initServerSideDataTable(options);
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishedSession(data) {
|
||||||
var the_url = "{% url 'api-terminal:session-list' %}";
|
var the_url = "{% url 'api-terminal:session-list' %}";
|
||||||
var success_message = '{% trans "Finish session success" %}';
|
var success_message = '{% trans "Finish session success" %}';
|
||||||
var success = function() {
|
var success = function() {
|
||||||
|
@ -165,9 +179,10 @@
|
||||||
success: success,
|
success: success,
|
||||||
success_message: success_message
|
success_message: success_message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
var table;
|
||||||
jumpserver.initStaticTable('table');
|
$(document).ready(function() {
|
||||||
|
table = initTable("#session_table");
|
||||||
$('.select2').select2({
|
$('.select2').select2({
|
||||||
dropdownAutoWidth: true,
|
dropdownAutoWidth: true,
|
||||||
width: "auto"
|
width: "auto"
|
||||||
|
@ -180,32 +195,35 @@
|
||||||
calendarWeeks: true,
|
calendarWeeks: true,
|
||||||
autoclose: true
|
autoclose: true
|
||||||
});
|
});
|
||||||
}).on('click', '.btn-term', function () {
|
}).on('click', '.btn-term', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var session_id = $this.attr('value');
|
var session_id = $this.attr('value');
|
||||||
var data = [
|
var data = [
|
||||||
session_id
|
session_id
|
||||||
];
|
];
|
||||||
terminateSession(data)
|
terminateSession(data)
|
||||||
}).on('click', '#btn_bulk_update', function () {
|
}).on('click', '.btn-replay', function () {
|
||||||
|
var sessionID = $(this).data("session");
|
||||||
|
var replayUrl = "/luna/replay/" + sessionID;
|
||||||
|
window.open(replayUrl, "height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no");
|
||||||
|
})
|
||||||
|
.on('click', '#btn_bulk_update', function () {
|
||||||
var action = $('#slct_bulk_update').val();
|
var action = $('#slct_bulk_update').val();
|
||||||
var id_list = [];
|
var idList = table.selected;
|
||||||
$(".cbx-term:checked").each(function (index, data) {
|
|
||||||
id_list.push($(data).attr("value"))
|
if (idList.length === 0) {
|
||||||
});
|
|
||||||
if (id_list.length === 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function doTerminate() {
|
function doTerminate() {
|
||||||
terminateSession(id_list)
|
terminateSession(idList)
|
||||||
}
|
}
|
||||||
|
|
||||||
function doFinishSession() {
|
function doFinishSession() {
|
||||||
var data = [];
|
var data = [];
|
||||||
$.each(id_list, function (i, v) {
|
$.each(idList, function (i, v) {
|
||||||
data.push({
|
data.push({
|
||||||
"pk": v,
|
"id": v,
|
||||||
"is_finished": true
|
"is_finished": true
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -221,7 +239,7 @@
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,16 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.views.generic import View, TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.template import loader
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import time
|
|
||||||
|
|
||||||
from common.mixins import DatetimeSearchMixin
|
|
||||||
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
|
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
|
||||||
from ..backends import get_multi_command_storage
|
|
||||||
|
|
||||||
__all__ = ['CommandListView']
|
__all__ = ['CommandListView']
|
||||||
common_storage = get_multi_command_storage()
|
|
||||||
|
|
||||||
|
|
||||||
class CommandListView(DatetimeSearchMixin, PermissionsMixin, TemplateView):
|
class CommandListView(PermissionsMixin, TemplateView):
|
||||||
template_name = "terminal/command_list.html"
|
template_name = "terminal/command_list.html"
|
||||||
permission_classes = [IsOrgAdmin | IsAuditor]
|
permission_classes = [IsOrgAdmin | IsAuditor]
|
||||||
default_days_ago = 5
|
default_days_ago = 5
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView, TemplateView
|
||||||
from django.views.generic.edit import SingleObjectMixin
|
from django.views.generic.edit import SingleObjectMixin
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -20,68 +20,40 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SessionListView(PermissionsMixin, DatetimeSearchMixin, ListView):
|
class SessionListView(PermissionsMixin, TemplateView):
|
||||||
model = Session
|
model = Session
|
||||||
template_name = 'terminal/session_list.html'
|
template_name = 'terminal/session_list.html'
|
||||||
context_object_name = 'session_list'
|
|
||||||
paginate_by = settings.DISPLAY_PER_PAGE
|
|
||||||
user = asset = system_user = ''
|
|
||||||
date_from = date_to = None
|
date_from = date_to = None
|
||||||
permission_classes = [IsOrgAdmin | IsAuditor]
|
permission_classes = [IsOrgAdmin | IsAuditor]
|
||||||
|
default_days_ago = 5
|
||||||
def get_queryset(self):
|
|
||||||
self.queryset = super().get_queryset()
|
|
||||||
self.user = self.request.GET.get('user')
|
|
||||||
self.asset = self.request.GET.get('asset')
|
|
||||||
self.system_user = self.request.GET.get('system_user')
|
|
||||||
|
|
||||||
filter_kwargs = dict()
|
|
||||||
filter_kwargs['date_start__gt'] = self.date_from
|
|
||||||
filter_kwargs['date_start__lt'] = self.date_to
|
|
||||||
return self.queryset
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
now = timezone.now()
|
||||||
context = {
|
context = {
|
||||||
'asset_list': utils.get_session_asset_list()[:10],
|
'date_from': now - timezone.timedelta(days=self.default_days_ago),
|
||||||
'date_from': self.date_from,
|
'date_to': now,
|
||||||
'date_to': self.date_to,
|
|
||||||
'user': self.user,
|
|
||||||
'asset': self.asset,
|
|
||||||
'system_user': self.system_user,
|
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SessionOnlineListView(SessionListView):
|
class SessionOnlineListView(SessionListView):
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset().filter(is_finished=False)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'app': _('Sessions'),
|
'app': _('Sessions'),
|
||||||
'action': _('Session online list'),
|
'action': _('Session online list'),
|
||||||
'type': 'online',
|
'type': 'online',
|
||||||
'now': timezone.now(),
|
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SessionOfflineListView(SessionListView):
|
class SessionOfflineListView(SessionListView):
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset()
|
|
||||||
queryset = queryset.filter(is_finished=True)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'app': _('Sessions'),
|
'app': _('Sessions'),
|
||||||
'action': _('Session offline'),
|
'action': _('Session offline'),
|
||||||
'now': timezone.now(),
|
'type': 'offline',
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
Loading…
Reference in New Issue