jumpserver/apps/terminal/models.py
老广 164f48e131
Dev beta (#3048)
* [Update] 统一url地址

* [Update] 修改api

* [Update] 使用规范的签名

* [Update] 修改url

* [Update] 修改swagger

* [Update] 添加serializer class避免报错

* [Update] 修改token

* [Update] 支持api key

* [Update] 支持生成api key

* [Update] 修改api重定向

* [Update] 修改翻译

* [Update] 添加说明文档

* [Update] 修复浏览器关闭后session不失效的问题

* [Update] 修改一些内容

* [Update] 修改 jms脚本

* [Update] 修改重定向

* [Update] 修改搜索trim

* [Update] 修改搜索trim

* [Update] 添加sys log

* [Bugfix] 修改登陆错误

* [Update] 优化User操作private_token的接口 (#3091)

* [Update] 优化User操作private_token的接口

* [Update] 优化User操作private_token的接口 2

* [Bugfix] 解决授权了一个节点,当移动节点后,被移动的节点下的资产会放到未分组节点下的问题

* [Update] 升级jquery

* [Update] 默认使用page

* [Update] 修改使用Orgmodel view set

* [Update] 支持 nv的硬盘 https://github.com/jumpserver/jumpserver/issues/1804

* [UPdate] 解决命令执行宽度问题

* [Update] 优化节点

* [Update] 修改nodes过多时创建比较麻烦

* [Update] 修改导入

* [Update] 节点获取更新

* [Update] 修改nodes

* [Update] nodes显示full value

* [Update] 统一使用nodes select2 函数

* [Update] 修改磁盘大小小数

* [Update] 修改 Node service

* [Update] 优化授权节点

* [Update] 修改 node permission

* [Update] 修改asset permission

* [Stash]

* [Update] 修改node assets api

* [Update] 修改tree service,支持资产数量

* [Update] 修改暂时完成

* [Update] 修改一些bug
2019-08-21 20:27:21 +08:00

274 lines
9.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.models import OrgModelMixin
from common.utils import get_command_storage_setting, get_replay_storage_setting
from .backends import get_multi_command_storage
from .backends.command.models import AbstractSessionCommand
class Terminal(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=32, verbose_name=_('Name'))
remote_addr = models.CharField(max_length=128, blank=True, verbose_name=_('Remote Address'))
ssh_port = models.IntegerField(verbose_name=_('SSH Port'), default=2222)
http_port = models.IntegerField(verbose_name=_('HTTP Port'), default=5000)
command_storage = models.CharField(max_length=128, verbose_name=_("Command storage"), default='default')
replay_storage = models.CharField(max_length=128, verbose_name=_("Replay storage"), default='default')
user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True, on_delete=models.CASCADE)
is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted')
is_deleted = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
comment = models.TextField(blank=True, verbose_name=_('Comment'))
STATUS_KEY_PREFIX = 'terminal_status_'
@property
def is_alive(self):
key = self.STATUS_KEY_PREFIX + str(self.id)
return bool(cache.get(key))
@is_alive.setter
def is_alive(self, value):
key = self.STATUS_KEY_PREFIX + str(self.id)
cache.set(key, value, 60)
@property
def is_active(self):
if self.user and self.user.is_active:
return True
return False
@is_active.setter
def is_active(self, active):
if self.user:
self.user.is_active = active
self.user.save()
def get_command_storage_setting(self):
storage_all = get_command_storage_setting()
if self.command_storage in storage_all:
storage = storage_all.get(self.command_storage)
else:
storage = storage_all.get('default')
return {"TERMINAL_COMMAND_STORAGE": storage}
def get_replay_storage_setting(self):
storage_all = get_replay_storage_setting()
if self.replay_storage in storage_all:
storage = storage_all.get(self.replay_storage)
else:
storage = storage_all.get('default')
return {"TERMINAL_REPLAY_STORAGE": storage}
@property
def config(self):
configs = {}
for k in dir(settings):
if not k.startswith('TERMINAL'):
continue
configs[k] = getattr(settings, k)
configs.update(self.get_command_storage_setting())
configs.update(self.get_replay_storage_setting())
configs.update({
'SECURITY_MAX_IDLE_TIME': settings.SECURITY_MAX_IDLE_TIME
})
return configs
@property
def service_account(self):
return self.user
def create_app_user(self):
random = uuid.uuid4().hex[:6]
user, access_key = User.create_app_user(
name="{}-{}".format(self.name, random), comment=self.comment
)
self.user = user
self.save()
return user, access_key
def delete(self, using=None, keep_parents=False):
if self.user:
self.user.delete()
self.user = None
self.is_deleted = True
self.save()
return
def __str__(self):
status = "Active"
if not self.is_accepted:
status = "NotAccept"
elif self.is_deleted:
status = "Deleted"
elif not self.is_active:
status = "Disable"
return '%s: %s' % (self.name, status)
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',)