mirror of https://github.com/jumpserver/jumpserver
commit
521f4ea86b
|
@ -101,6 +101,15 @@ class CustomMetaDictField(serializers.DictField):
|
||||||
filter_value = {k: v for k, v in value.items() if k in fields_names}
|
filter_value = {k: v for k, v in value.items() if k in fields_names}
|
||||||
return filter_value
|
return filter_value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def strip_value(value):
|
||||||
|
new_value = {}
|
||||||
|
for k, v in value.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
v = v.strip()
|
||||||
|
new_value[k] = v
|
||||||
|
return new_value
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
"""
|
"""
|
||||||
反序列化时调用
|
反序列化时调用
|
||||||
|
@ -108,4 +117,5 @@ class CustomMetaDictField(serializers.DictField):
|
||||||
value = super().get_value(dictionary)
|
value = super().get_value(dictionary)
|
||||||
value = self.convert_value_key(dictionary, value)
|
value = self.convert_value_key(dictionary, value)
|
||||||
value = self.filter_value_key(dictionary, value)
|
value = self.filter_value_key(dictionary, value)
|
||||||
|
value = self.strip_value(value)
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -1256,7 +1256,8 @@ function toSafeDateISOStr(s) {
|
||||||
|
|
||||||
function toSafeLocalDateStr(d) {
|
function toSafeLocalDateStr(d) {
|
||||||
var date = safeDate(d);
|
var date = safeDate(d);
|
||||||
var date_s = date.toLocaleString(getUserLang(), {hour12: false});
|
// var date_s = date.toLocaleString(getUserLang(), {hour12: false});
|
||||||
|
var date_s = date.toLocaleString(getUserLang(), {hourCycle: "h23"});
|
||||||
return date_s.split("/").join('-')
|
return date_s.split("/").join('-')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<script src="{% static "js/plugins/toastr/toastr.min.js" %}"></script>
|
<script src="{% static "js/plugins/toastr/toastr.min.js" %}"></script>
|
||||||
<script src="{% static "js/inspinia.js" %}"></script>
|
<script src="{% static "js/inspinia.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
|
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
|
||||||
<script src="{% static "js/jumpserver.js" %}?v=7"></script>
|
<script src="{% static "js/jumpserver.js" %}?v=8"></script>
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||||
<script src="{% static 'js/plugins/select2/i18n/zh-CN.js' %}"></script>
|
<script src="{% static 'js/plugins/select2/i18n/zh-CN.js' %}"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
from django.shortcuts import get_object_or_404, reverse
|
from django.shortcuts import get_object_or_404, reverse
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets, views
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from common.utils import is_uuid, get_logger
|
from common.utils import is_uuid, get_logger, get_object_or_none
|
||||||
from common.mixins.api import AsyncApiMixin
|
from common.mixins.api import AsyncApiMixin
|
||||||
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
|
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsAppUser
|
||||||
from common.drf.filters import DatetimeRangeFilter
|
from common.drf.filters import DatetimeRangeFilter
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.utils import tmp_to_root_org, tmp_to_org
|
||||||
|
from users.models import User
|
||||||
from ..utils import find_session_replay_local, download_session_replay
|
from ..utils import find_session_replay_local, download_session_replay
|
||||||
from ..hands import SystemUser
|
from ..hands import SystemUser
|
||||||
from ..models import Session
|
from ..models import Session
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['SessionViewSet', 'SessionReplayViewSet',]
|
__all__ = [
|
||||||
|
'SessionViewSet', 'SessionReplayViewSet', 'SessionJoinValidateAPI'
|
||||||
|
]
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,3 +122,36 @@ class SessionReplayViewSet(AsyncApiMixin, viewsets.ViewSet):
|
||||||
return Response({"error": url})
|
return Response({"error": url})
|
||||||
data = self.get_replay_data(session, url)
|
data = self.get_replay_data(session, url)
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
|
class SessionJoinValidateAPI(views.APIView):
|
||||||
|
permission_classes = (IsAppUser, )
|
||||||
|
serializer_class = serializers.SessionJoinValidateSerializer
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
if not serializer.is_valid():
|
||||||
|
msg = str(serializer.errors)
|
||||||
|
return Response({'ok': False, 'msg': msg}, status=401)
|
||||||
|
user_id = serializer.validated_data['user_id']
|
||||||
|
session_id = serializer.validated_data['session_id']
|
||||||
|
|
||||||
|
with tmp_to_root_org():
|
||||||
|
session = get_object_or_none(Session, pk=session_id)
|
||||||
|
if not session:
|
||||||
|
msg = _('Session does not exist: {}'.format(session_id))
|
||||||
|
return Response({'ok': False, 'msg': msg}, status=401)
|
||||||
|
if not session.can_join():
|
||||||
|
msg = _('Session is finished or the protocol not supported')
|
||||||
|
return Response({'ok': False, 'msg': msg}, status=401)
|
||||||
|
|
||||||
|
user = get_object_or_none(User, pk=user_id)
|
||||||
|
if not user:
|
||||||
|
msg = _('User does not exist: {}'.format(user_id))
|
||||||
|
return Response({'ok': False, 'msg': msg}, status=401)
|
||||||
|
with tmp_to_org(session.org):
|
||||||
|
if not user.admin_or_audit_orgs:
|
||||||
|
msg = _('User does not have permission')
|
||||||
|
return Response({'ok': False, 'msg': msg}, status=401)
|
||||||
|
|
||||||
|
return Response({'ok': True, 'msg': ''}, status=200)
|
||||||
|
|
|
@ -243,6 +243,13 @@ class Session(OrgModelMixin):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def can_join(self):
|
||||||
|
if self.is_finished:
|
||||||
|
return False
|
||||||
|
if self.protocol not in ['ssh', 'telnet', 'mysql']:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def save_to_storage(self, f):
|
def save_to_storage(self, f):
|
||||||
local_path = self.get_local_path()
|
local_path = self.get_local_path()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ..models import Session
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'SessionSerializer', 'SessionDisplaySerializer',
|
'SessionSerializer', 'SessionDisplaySerializer',
|
||||||
'ReplaySerializer',
|
'ReplaySerializer', 'SessionJoinValidateSerializer',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class SessionSerializer(BulkOrgResourceModelSerializer):
|
||||||
"user_id", "asset_id", "system_user_id",
|
"user_id", "asset_id", "system_user_id",
|
||||||
"login_from", "login_from_display", "remote_addr",
|
"login_from", "login_from_display", "remote_addr",
|
||||||
"is_success", "is_finished", "has_replay", "can_replay",
|
"is_success", "is_finished", "has_replay", "can_replay",
|
||||||
"protocol", "date_start", "date_end",
|
"can_join", "protocol", "date_start", "date_end",
|
||||||
"terminal",
|
"terminal",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -35,3 +35,8 @@ class SessionDisplaySerializer(SessionSerializer):
|
||||||
|
|
||||||
class ReplaySerializer(serializers.Serializer):
|
class ReplaySerializer(serializers.Serializer):
|
||||||
file = serializers.FileField(allow_empty_file=True)
|
file = serializers.FileField(allow_empty_file=True)
|
||||||
|
|
||||||
|
|
||||||
|
class SessionJoinValidateSerializer(serializers.Serializer):
|
||||||
|
user_id = serializers.UUIDField()
|
||||||
|
session_id = serializers.UUIDField()
|
||||||
|
|
|
@ -146,10 +146,15 @@ function initTable() {
|
||||||
.replace("sessionID", cellData)
|
.replace("sessionID", cellData)
|
||||||
.replace("terminalID", rowData.terminal)
|
.replace("terminalID", rowData.terminal)
|
||||||
}
|
}
|
||||||
|
var joinBtn = ' <a disabled data-session="sessionID" class="btn btn-xs btn-info btn-join" >{% trans "Join" %}</a>';
|
||||||
|
joinBtn = joinBtn.replace("sessionID", rowData.id);
|
||||||
|
if (rowData.can_join){
|
||||||
|
joinBtn = joinBtn.replace("disabled", "")
|
||||||
|
}
|
||||||
if (rowData.is_finished) {
|
if (rowData.is_finished) {
|
||||||
btnGroup += replayBtn + downloadBtn
|
btnGroup += replayBtn + downloadBtn
|
||||||
} else {
|
} else {
|
||||||
btnGroup += termBtn;
|
btnGroup += termBtn + joinBtn;
|
||||||
}
|
}
|
||||||
$(td).html(btnGroup);
|
$(td).html(btnGroup);
|
||||||
}},
|
}},
|
||||||
|
@ -246,6 +251,11 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
window.open(downloadUrl)
|
window.open(downloadUrl)
|
||||||
})
|
})
|
||||||
|
.on('click', '.btn-join', function () {
|
||||||
|
var sessionID = $(this).data("session");
|
||||||
|
var joinUrl = "/luna/join/?shareroom=" + sessionID;
|
||||||
|
window.open(joinUrl, "height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no");
|
||||||
|
})
|
||||||
.on("click", '#session_table_filter input', function (e) {
|
.on("click", '#session_table_filter input', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
|
@ -22,6 +22,7 @@ router.register(r'replay-storages', api.ReplayStorageViewSet, 'replay-storage')
|
||||||
router.register(r'command-storages', api.CommandStorageViewSet, 'command-storage')
|
router.register(r'command-storages', api.CommandStorageViewSet, 'command-storage')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('sessions/join/validate/', api.SessionJoinValidateAPI.as_view(), name='join-session'),
|
||||||
path('sessions/<uuid:pk>/replay/',
|
path('sessions/<uuid:pk>/replay/',
|
||||||
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
||||||
name='session-replay'),
|
name='session-replay'),
|
||||||
|
|
Loading…
Reference in New Issue