Commit 1e49de79 authored by BaiJiangJie's avatar BaiJiangJie

[Feature] 会话管理:添加数据库应用连接会话Model、API;拆分terminal/models.py文件->目录

parent f2888319
......@@ -17,11 +17,11 @@ 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 ..models import Session, DatabaseSession
from .. import serializers
__all__ = ['SessionViewSet', 'SessionReplayViewSet',]
__all__ = ['SessionViewSet', 'SessionReplayViewSet', 'DatabaseSessionViewSet']
logger = get_logger(__name__)
......@@ -62,6 +62,30 @@ class SessionViewSet(OrgBulkModelViewSet):
return super().perform_create(serializer)
class DatabaseSessionViewSet(OrgBulkModelViewSet):
queryset = DatabaseSession.objects.all()
serializer_class = serializers.DatabaseSessionSerializer
pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
filter_fields = [
'user', 'database', 'db_host', 'db_name', 'db_user'
]
date_range_filter_fields = [
('date_start', ('date_from', 'date_to'))
]
@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
return super().perform_create(serializer)
class SessionReplayViewSet(viewsets.ViewSet):
serializer_class = serializers.ReplaySerializer
permission_classes = (IsOrgAdminOrAppUser | IsAuditor,)
......
# coding: utf-8
#
from .base import *
from .command import *
from .session import *
from .terminal import *
from .status import *
from .task import *
# coding: utf-8
#
from __future__ import unicode_literals
import os
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.core.files.storage import default_storage
from django.core.cache import cache
from ..backends import get_multi_command_storage
from orgs.mixins import OrgModelMixin
from .terminal import Terminal
__all__ = ['BaseSession']
class BaseSession(OrgModelMixin):
LOGIN_FROM_CHOICES = (
('ST', 'SSH Terminal'),
('WT', 'Web Terminal'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
user = models.CharField(max_length=128, verbose_name=_("User"))
login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST")
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
is_finished = models.BooleanField(default=False)
has_replay = models.BooleanField(default=False, verbose_name=_("Replay"))
has_command = models.BooleanField(default=False, verbose_name=_("Command"))
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.SET_NULL)
date_last_active = models.DateTimeField(verbose_name=_("Date last active"), default=timezone.now)
date_start = models.DateTimeField(verbose_name=_("Date start"), db_index=True, default=timezone.now)
date_end = models.DateTimeField(verbose_name=_("Date end"), null=True)
upload_to = 'replay'
ACTIVE_CACHE_KEY_PREFIX = 'SESSION_ACTIVE_{}'
def get_rel_replay_path(self, version=2):
"""
获取session日志的文件路径
:param version: 原来后缀是 .gz,为了统一新版本改为 .replay.gz
:return:
"""
suffix = '.replay.gz'
if version == 1:
suffix = '.gz'
date = self.date_start.strftime('%Y-%m-%d')
return os.path.join(date, str(self.id) + suffix)
def get_local_path(self, version=2):
rel_path = self.get_rel_replay_path(version=version)
if version == 2:
local_path = os.path.join(self.upload_to, rel_path)
else:
local_path = rel_path
return local_path
def save_to_storage(self, f):
local_path = self.get_local_path()
try:
name = default_storage.save(local_path, f)
return name, None
except OSError as e:
return None, e
@classmethod
def set_sessions_active(cls, sessions_id):
data = {cls.ACTIVE_CACHE_KEY_PREFIX.format(i): i for i in sessions_id}
cache.set_many(data, timeout=5*60)
@classmethod
def get_active_sessions(cls):
return cls.objects.filter(is_finished=False)
def is_active(self):
key = self.ACTIVE_CACHE_KEY_PREFIX.format(self.id)
return bool(cache.get(key))
@property
def command_amount(self):
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:
abstract = True
# coding: utf-8
#
from __future__ import unicode_literals
from ..backends.command.models import (
AbstractSessionCommand, AbstractDatabaseSessionCommand
)
__all__ = ['Command', 'DatabaseCommand']
class Command(AbstractSessionCommand):
class Meta:
db_table = "terminal_command"
ordering = ('-timestamp',)
class DatabaseCommand(AbstractDatabaseSessionCommand):
class Meta:
db_table = "terminal_database_command"
ordering = ('-timestamp', )
# coding: utf-8
#
from __future__ import unicode_literals
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.core.cache import cache
from .base import BaseSession
__all__ = ['Session', 'DatabaseSession']
class Session(BaseSession):
"""
AssetSession
"""
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
('rdp', 'rdp'),
('vnc', 'vnc')
)
asset = models.CharField(max_length=1024, verbose_name=_("Asset"))
system_user = models.CharField(max_length=128, verbose_name=_("System user"))
protocol = models.CharField(choices=PROTOCOL_CHOICES, default='ssh', max_length=8)
_DATE_START_FIRST_HAS_REPLAY_RDP_SESSION = None
@property
def _date_start_first_has_replay_rdp_session(self):
if self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION is None:
instance = self.__class__.objects.filter(
protocol='rdp', has_replay=True
).order_by('date_start').first()
if not instance:
date_start = timezone.now() - timezone.timedelta(days=365)
else:
date_start = instance.date_start
self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION = date_start
return self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION
def can_replay(self):
if self.has_replay:
return True
if self.date_start < self._date_start_first_has_replay_rdp_session:
return True
return False
def is_active(self):
if self.protocol in ['ssh', 'telnet']:
key = self.ACTIVE_CACHE_KEY_PREFIX.format(self.id)
return bool(cache.get(key))
return True
class Meta:
db_table = "terminal_session"
ordering = ["-date_start"]
def __str__(self):
return "{0.id} of {0.user} to {0.asset}".format(self)
class DatabaseSession(BaseSession):
user_id = models.CharField(max_length=36, db_index=True, verbose_name=_("User ID"))
database = models.CharField(max_length=128, verbose_name=_('Database'))
database_id = models.CharField(max_length=36, db_index=True, verbose_name=_("Database ID"))
db_host = models.CharField(max_length=128, verbose_name=_('Database host'))
db_name = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Database name'))
db_user = models.CharField(max_length=32, blank=True, null=True, verbose_name=_('Database user'))
class Meta:
db_table = "terminal_database_session"
ordering = ["-date_start"]
# coding: utf-8
#
from __future__ import unicode_literals
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from .terminal import Terminal
__all__ = ['Status']
class Status(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
session_online = models.IntegerField(verbose_name=_("Session Online"), default=0)
cpu_used = models.FloatField(verbose_name=_("CPU Usage"))
memory_used = models.FloatField(verbose_name=_("Memory Used"))
connections = models.IntegerField(verbose_name=_("Connections"))
threads = models.IntegerField(verbose_name=_("Threads"))
boot_time = models.FloatField(verbose_name=_("Boot Time"))
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'terminal_status'
get_latest_by = 'date_created'
def __str__(self):
return self.date_created.strftime("%Y-%m-%d %H:%M:%S")
# coding: utf-8
#
from __future__ import unicode_literals
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from .terminal import Terminal
__all__ = ['Task']
class Task(models.Model):
NAME_CHOICES = (
("kill_session", "Kill Session"),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, choices=NAME_CHOICES, verbose_name=_("Name"))
args = models.CharField(max_length=1024, verbose_name=_("Args"))
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.SET_NULL)
is_finished = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
date_finished = models.DateTimeField(null=True)
class Meta:
db_table = "terminal_task"
# coding: utf-8
#
from __future__ import unicode_literals
import os
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.conf import settings
from django.core.files.storage import default_storage
from django.core.cache import cache
from users.models import User
from orgs.mixins import OrgModelMixin
from common.utils import (
get_command_storage_setting, get_replay_storage_setting, get_validity_of_license
)
from .backends import get_multi_command_storage
from .backends.command.models import AbstractSessionCommand
__all__ = ['Terminal']
class Terminal(models.Model):
......@@ -121,156 +119,3 @@ class Terminal(models.Model):
class Meta:
ordering = ('is_accepted',)
db_table = "terminal"
class Status(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
session_online = models.IntegerField(verbose_name=_("Session Online"), default=0)
cpu_used = models.FloatField(verbose_name=_("CPU Usage"))
memory_used = models.FloatField(verbose_name=_("Memory Used"))
connections = models.IntegerField(verbose_name=_("Connections"))
threads = models.IntegerField(verbose_name=_("Threads"))
boot_time = models.FloatField(verbose_name=_("Boot Time"))
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'terminal_status'
get_latest_by = 'date_created'
def __str__(self):
return self.date_created.strftime("%Y-%m-%d %H:%M:%S")
class Session(OrgModelMixin):
LOGIN_FROM_CHOICES = (
('ST', 'SSH Terminal'),
('WT', 'Web Terminal'),
)
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
('rdp', 'rdp'),
('vnc', 'vnc')
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
user = models.CharField(max_length=128, verbose_name=_("User"))
asset = models.CharField(max_length=1024, verbose_name=_("Asset"))
system_user = models.CharField(max_length=128, verbose_name=_("System user"))
login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST")
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
is_finished = models.BooleanField(default=False)
has_replay = models.BooleanField(default=False, verbose_name=_("Replay"))
has_command = models.BooleanField(default=False, verbose_name=_("Command"))
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.SET_NULL)
protocol = models.CharField(choices=PROTOCOL_CHOICES, default='ssh', max_length=8)
date_last_active = models.DateTimeField(verbose_name=_("Date last active"), default=timezone.now)
date_start = models.DateTimeField(verbose_name=_("Date start"), db_index=True, default=timezone.now)
date_end = models.DateTimeField(verbose_name=_("Date end"), null=True)
upload_to = 'replay'
ACTIVE_CACHE_KEY_PREFIX = 'SESSION_ACTIVE_{}'
_DATE_START_FIRST_HAS_REPLAY_RDP_SESSION = None
def get_rel_replay_path(self, version=2):
"""
获取session日志的文件路径
:param version: 原来后缀是 .gz,为了统一新版本改为 .replay.gz
:return:
"""
suffix = '.replay.gz'
if version == 1:
suffix = '.gz'
date = self.date_start.strftime('%Y-%m-%d')
return os.path.join(date, str(self.id) + suffix)
def get_local_path(self, version=2):
rel_path = self.get_rel_replay_path(version=version)
if version == 2:
local_path = os.path.join(self.upload_to, rel_path)
else:
local_path = rel_path
return local_path
@property
def _date_start_first_has_replay_rdp_session(self):
if self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION is None:
instance = self.__class__.objects.filter(
protocol='rdp', has_replay=True
).order_by('date_start').first()
if not instance:
date_start = timezone.now() - timezone.timedelta(days=365)
else:
date_start = instance.date_start
self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION = date_start
return self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION
def can_replay(self):
if self.has_replay:
return True
if self.date_start < self._date_start_first_has_replay_rdp_session:
return True
return False
def save_to_storage(self, f):
local_path = self.get_local_path()
try:
name = default_storage.save(local_path, f)
return name, None
except OSError as e:
return None, e
@classmethod
def set_sessions_active(cls, sessions_id):
data = {cls.ACTIVE_CACHE_KEY_PREFIX.format(i): i for i in sessions_id}
cache.set_many(data, timeout=5*60)
@classmethod
def get_active_sessions(cls):
return cls.objects.filter(is_finished=False)
def is_active(self):
if self.protocol in ['ssh', 'telnet']:
key = self.ACTIVE_CACHE_KEY_PREFIX.format(self.id)
return bool(cache.get(key))
return True
@property
def command_amount(self):
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"]
def __str__(self):
return "{0.id} of {0.user} to {0.asset}".format(self)
class Task(models.Model):
NAME_CHOICES = (
("kill_session", "Kill Session"),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, choices=NAME_CHOICES, verbose_name=_("Name"))
args = models.CharField(max_length=1024, verbose_name=_("Args"))
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.SET_NULL)
is_finished = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
date_finished = models.DateTimeField(null=True)
class Meta:
db_table = "terminal_task"
class Command(AbstractSessionCommand):
class Meta:
db_table = "terminal_command"
ordering = ('-timestamp',)
......@@ -5,7 +5,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
from ..models import Terminal, Status, Session, Task, DatabaseSession
class TerminalSerializer(serializers.ModelSerializer):
......@@ -40,6 +40,20 @@ class SessionSerializer(BulkOrgResourceModelSerializer):
]
class DatabaseSessionSerializer(BulkOrgResourceModelSerializer):
command_amount = serializers.IntegerField(read_only=True)
org_id = serializers.CharField(allow_blank=True)
class Meta:
model = DatabaseSession
list_serializer_class = AdaptedBulkListSerializer
fields = [
'id', 'user', 'database', 'db_host', 'db_name', 'db_user',
'login_from_display', 'remote_addr', 'is_finished', 'has_replay',
'date_start', 'date_end', 'terminal', 'command_amount'
]
class StatusSerializer(serializers.ModelSerializer):
class Meta:
fields = ['id', 'terminal']
......
......@@ -11,6 +11,7 @@ app_name = 'terminal'
router = BulkRouter()
router.register(r'sessions', api.SessionViewSet, 'session')
router.register(r'database-sessions', api.DatabaseSessionViewSet, 'database-session')
router.register(r'terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status')
router.register(r'terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions')
router.register(r'terminal', api.TerminalViewSet, 'terminal')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment