mirror of
				https://github.com/jumpserver/jumpserver.git
				synced 2025-11-03 23:47:27 +00:00 
			
		
		
		
	[Update] 修改session
This commit is contained in:
		
							
								
								
									
										42
									
								
								apps/common/filters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								apps/common/filters.py
									
									
									
									
									
										Normal file
									
								
							@@ -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):
 | 
			
		||||
 | 
			
		||||
    def filter_queryset(self, queryset):
 | 
			
		||||
        queryset = super(IDInCacheFilterMixin, self).filter_queryset(queryset)
 | 
			
		||||
        queryset = super().filter_queryset(queryset)
 | 
			
		||||
        spm = self.request.query_params.get('spm')
 | 
			
		||||
        if not spm:
 | 
			
		||||
            return queryset
 | 
			
		||||
        cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
 | 
			
		||||
        resources_id = cache.get(cache_key)
 | 
			
		||||
        if resources_id and isinstance(resources_id, list):
 | 
			
		||||
 
 | 
			
		||||
@@ -27,13 +27,14 @@ class BulkSerializerMixin(object):
 | 
			
		||||
            if all((isinstance(self.root, BulkListSerializer),
 | 
			
		||||
                    id_attr,
 | 
			
		||||
                    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"):
 | 
			
		||||
                    id_value = id_field.to_internal_value(data.get("id"))
 | 
			
		||||
                else:
 | 
			
		||||
                    id_value = id_field.to_internal_value(data.get("pk"))
 | 
			
		||||
                print(">>>>>>>>>>>>>>>>>>>")
 | 
			
		||||
                print(id_attr)
 | 
			
		||||
                ret[id_attr] = id_value
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
							
								
								
									
										7
									
								
								apps/orgs/mixins/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								apps/orgs/mixins/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from .models import *
 | 
			
		||||
from .serializers import *
 | 
			
		||||
from .forms import *
 | 
			
		||||
from .api import *
 | 
			
		||||
							
								
								
									
										51
									
								
								apps/orgs/mixins/api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								apps/orgs/mixins/api.py
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										16
									
								
								apps/orgs/mixins/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								apps/orgs/mixins/forms.py
									
									
									
									
									
										Normal file
									
								
							@@ -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()
 | 
			
		||||
							
								
								
									
										115
									
								
								apps/orgs/mixins/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								apps/orgs/mixins/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										56
									
								
								apps/orgs/mixins/serializers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								apps/orgs/mixins/serializers.py
									
									
									
									
									
										Normal file
									
								
							@@ -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) {
 | 
			
		||||
    url = url.split("?");
 | 
			
		||||
    let params = "";
 | 
			
		||||
    var params = "";
 | 
			
		||||
    if (url.length === 2){
 | 
			
		||||
        params = url[1];
 | 
			
		||||
    }
 | 
			
		||||
    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 import viewsets
 | 
			
		||||
from rest_framework.response import Response
 | 
			
		||||
from rest_framework_bulk import BulkModelViewSet
 | 
			
		||||
from rest_framework.generics import GenericAPIView
 | 
			
		||||
import jms_storage
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from common.utils import is_uuid, get_logger
 | 
			
		||||
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
 | 
			
		||||
from common.filters import DatetimeRangeFilter
 | 
			
		||||
from orgs.mixins import OrgBulkModelViewSet
 | 
			
		||||
from ..hands import SystemUser
 | 
			
		||||
from ..models import Session
 | 
			
		||||
from .. import serializers
 | 
			
		||||
@@ -24,12 +25,17 @@ __all__ = ['SessionViewSet', 'SessionReplayViewSet',]
 | 
			
		||||
logger = get_logger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SessionViewSet(BulkModelViewSet):
 | 
			
		||||
class SessionViewSet(OrgBulkModelViewSet):
 | 
			
		||||
    queryset = Session.objects.all()
 | 
			
		||||
    serializer_class = serializers.SessionSerializer
 | 
			
		||||
    pagination_class = LimitOffsetPagination
 | 
			
		||||
    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):
 | 
			
		||||
        # 解决guacamole更新session时并发导致幽灵会话的问题
 | 
			
		||||
@@ -38,6 +44,12 @@ class SessionViewSet(BulkModelViewSet):
 | 
			
		||||
            obj = obj.select_for_update()
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def filter_backends(self):
 | 
			
		||||
        backends = list(GenericAPIView.filter_backends)
 | 
			
		||||
        backends.append(DatetimeRangeFilter)
 | 
			
		||||
        return backends
 | 
			
		||||
 | 
			
		||||
    def perform_create(self, serializer):
 | 
			
		||||
        if hasattr(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()
 | 
			
		||||
        return command_store.count(session=str(self.id))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def login_from_display(self):
 | 
			
		||||
        return self.get_login_from_display()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        db_table = "terminal_session"
 | 
			
		||||
        ordering = ["-date_start"]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
#
 | 
			
		||||
from rest_framework import serializers
 | 
			
		||||
 | 
			
		||||
from orgs.mixins import BulkOrgResourceModelSerializer
 | 
			
		||||
from common.mixins import BulkSerializerMixin
 | 
			
		||||
from common.serializers import AdaptedBulkListSerializer
 | 
			
		||||
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()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer):
 | 
			
		||||
class SessionSerializer(BulkOrgResourceModelSerializer):
 | 
			
		||||
    command_amount = serializers.IntegerField(read_only=True)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Session
 | 
			
		||||
        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):
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@
 | 
			
		||||
        .toggle {
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .detail-key {
 | 
			
		||||
            width: 70px;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,65 +7,21 @@
 | 
			
		||||
    <link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
 | 
			
		||||
    <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
 | 
			
		||||
    <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
 | 
			
		||||
    <style>
 | 
			
		||||
        #search_btn {
 | 
			
		||||
            margin-bottom: 0;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content_left_head %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{% 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>
 | 
			
		||||
{% block table_pagination %}
 | 
			
		||||
{% 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">{% trans 'ID' %}</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 'Command' %}</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 '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>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
{% block content_bottom_left %}
 | 
			
		||||
    {% if  request.user.is_org_admin %}
 | 
			
		||||
    <div id="actions" {% if type != "online" %} style="display: none" {% endif %}>
 | 
			
		||||
    <div id="actions" class="hide">
 | 
			
		||||
        {% if type == "online" %}
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <select class="form-control m-b" style="width: auto" id="slct_bulk_update">
 | 
			
		||||
                <option value="terminate">{% trans 'Terminate selected' %}</option>
 | 
			
		||||
@@ -128,13 +53,33 @@
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
        {% 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 %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{% block custom_foot_js %}
 | 
			
		||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
function terminateSession(data) {
 | 
			
		||||
    function success() {
 | 
			
		||||
        window.setTimeout(function () {
 | 
			
		||||
@@ -152,6 +97,75 @@
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 success_message = '{% trans "Finish session success" %}';
 | 
			
		||||
@@ -166,8 +180,9 @@
 | 
			
		||||
        success_message: success_message
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
var table;
 | 
			
		||||
$(document).ready(function() {
 | 
			
		||||
            jumpserver.initStaticTable('table');
 | 
			
		||||
    table = initTable("#session_table");
 | 
			
		||||
    $('.select2').select2({
 | 
			
		||||
        dropdownAutoWidth: true,
 | 
			
		||||
        width: "auto"
 | 
			
		||||
@@ -187,25 +202,28 @@
 | 
			
		||||
        session_id
 | 
			
		||||
    ];
 | 
			
		||||
    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 id_list = [];
 | 
			
		||||
            $(".cbx-term:checked").each(function (index, data) {
 | 
			
		||||
                id_list.push($(data).attr("value"))
 | 
			
		||||
            });
 | 
			
		||||
            if (id_list.length === 0) {
 | 
			
		||||
    var idList = table.selected;
 | 
			
		||||
 | 
			
		||||
    if (idList.length === 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function doTerminate() {
 | 
			
		||||
                terminateSession(id_list)
 | 
			
		||||
        terminateSession(idList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function doFinishSession() {
 | 
			
		||||
       var data = [];
 | 
			
		||||
               $.each(id_list, function (i, v) {
 | 
			
		||||
       $.each(idList, function (i, v) {
 | 
			
		||||
           data.push({
 | 
			
		||||
                       "pk": v,
 | 
			
		||||
               "id": v,
 | 
			
		||||
               "is_finished": true
 | 
			
		||||
           })
 | 
			
		||||
       });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,16 @@
 | 
			
		||||
# -*- 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.http import HttpResponse
 | 
			
		||||
from django.template import loader
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from common.mixins import DatetimeSearchMixin
 | 
			
		||||
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
 | 
			
		||||
from ..backends import get_multi_command_storage
 | 
			
		||||
 | 
			
		||||
__all__ = ['CommandListView']
 | 
			
		||||
common_storage = get_multi_command_storage()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandListView(DatetimeSearchMixin, PermissionsMixin, TemplateView):
 | 
			
		||||
class CommandListView(PermissionsMixin, TemplateView):
 | 
			
		||||
    template_name = "terminal/command_list.html"
 | 
			
		||||
    permission_classes = [IsOrgAdmin | IsAuditor]
 | 
			
		||||
    default_days_ago = 5
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
# -*- 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.utils.translation import ugettext as _
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
@@ -20,68 +20,40 @@ __all__ = [
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SessionListView(PermissionsMixin, DatetimeSearchMixin, ListView):
 | 
			
		||||
class SessionListView(PermissionsMixin, TemplateView):
 | 
			
		||||
    model = Session
 | 
			
		||||
    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
 | 
			
		||||
    permission_classes = [IsOrgAdmin | IsAuditor]
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    default_days_ago = 5
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        now = timezone.now()
 | 
			
		||||
        context = {
 | 
			
		||||
            'asset_list': utils.get_session_asset_list()[:10],
 | 
			
		||||
            'date_from': self.date_from,
 | 
			
		||||
            'date_to': self.date_to,
 | 
			
		||||
            'user': self.user,
 | 
			
		||||
            'asset': self.asset,
 | 
			
		||||
            'system_user': self.system_user,
 | 
			
		||||
            'date_from': now - timezone.timedelta(days=self.default_days_ago),
 | 
			
		||||
            'date_to': now,
 | 
			
		||||
        }
 | 
			
		||||
        kwargs.update(context)
 | 
			
		||||
        return super().get_context_data(**kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SessionOnlineListView(SessionListView):
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        queryset = super().get_queryset().filter(is_finished=False)
 | 
			
		||||
        return queryset
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = {
 | 
			
		||||
            'app': _('Sessions'),
 | 
			
		||||
            'action': _('Session online list'),
 | 
			
		||||
            'type': 'online',
 | 
			
		||||
            'now': timezone.now(),
 | 
			
		||||
        }
 | 
			
		||||
        kwargs.update(context)
 | 
			
		||||
        return super().get_context_data(**kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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):
 | 
			
		||||
        context = {
 | 
			
		||||
            'app': _('Sessions'),
 | 
			
		||||
            'action': _('Session offline'),
 | 
			
		||||
            'now': timezone.now(),
 | 
			
		||||
            'type': 'offline',
 | 
			
		||||
        }
 | 
			
		||||
        kwargs.update(context)
 | 
			
		||||
        return super().get_context_data(**kwargs)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user