mirror of https://github.com/jumpserver/jumpserver
Merge branch 'v3' of github.com:jumpserver/jumpserver into v3
commit
4bd913b585
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.2.16 on 2022-12-23 07:36
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0116_alter_automationexecution_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='gateway',
|
||||
options={'verbose_name': 'Gateway'},
|
||||
),
|
||||
]
|
|
@ -37,6 +37,7 @@ class Gateway(Host):
|
|||
|
||||
class Meta:
|
||||
proxy = True
|
||||
verbose_name = _("Gateway")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.platform = self.default_platform()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5c366d6b10c4ce62bd8ed7c69ecaec5533f1a178b3cc7db4e6008769a6c8bb1f
|
||||
size 119897
|
||||
oid sha256:7f83a00d90fe74749386ecd1f64d507b135e8c4d35acea1c5cd56bba3387e834
|
||||
size 119674
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:eaeedc4823f9b7e236b36a169b5587ef5988d3c3e529d6cbede6bae5e2b57ab8
|
||||
size 106349
|
||||
oid sha256:b02c5d36ea6ea96590be9a25dc6d3f1340a5af2aa940764243f85da1c756c732
|
||||
size 104221
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,41 @@
|
|||
# Generated by Django 3.2.16 on 2022-12-23 07:36
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ops', '0032_auto_20221221_1513'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='adhoc',
|
||||
options={'verbose_name': 'AdHoc'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='celerytask',
|
||||
options={'ordering': ('name',), 'verbose_name': 'Celery Task'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='celerytaskexecution',
|
||||
options={'verbose_name': 'Celery Task Execution'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='historicaljob',
|
||||
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Job', 'verbose_name_plural': 'historical Jobs'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='job',
|
||||
options={'ordering': ['date_created'], 'verbose_name': 'Job'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='jobauditlog',
|
||||
options={'verbose_name': 'Job audit log'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='jobexecution',
|
||||
options={'ordering': ['-date_created'], 'verbose_name': 'Job Execution'},
|
||||
),
|
||||
]
|
|
@ -41,3 +41,6 @@ class AdHoc(JMSOrgBaseModel):
|
|||
|
||||
def __str__(self):
|
||||
return "{}: {}".format(self.module, self.args)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("AdHoc")
|
||||
|
|
|
@ -44,6 +44,7 @@ class CeleryTask(models.Model):
|
|||
return "green"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Celery Task")
|
||||
ordering = ('name',)
|
||||
|
||||
|
||||
|
@ -77,3 +78,6 @@ class CeleryTaskExecution(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return "{}: {}".format(self.name, self.id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Celery Task Execution")
|
||||
|
|
|
@ -88,6 +88,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
|
|||
return self.executions.create(job_version=self.version)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Job")
|
||||
ordering = ['date_created']
|
||||
|
||||
|
||||
|
@ -284,6 +285,7 @@ class JobExecution(JMSOrgBaseModel):
|
|||
self.set_error(e)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Job Execution")
|
||||
ordering = ['-date_created']
|
||||
|
||||
|
||||
|
@ -294,3 +296,4 @@ class JobAuditLog(JobExecution):
|
|||
|
||||
class Meta:
|
||||
proxy = True
|
||||
verbose_name = _("Job audit log")
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
from django.core.cache import cache
|
||||
|
||||
from rest_framework.request import Request
|
||||
|
||||
from common.http import is_true
|
||||
from common.utils import lazyproperty
|
||||
from perms.utils import UserPermTreeRefreshUtil
|
||||
from users.models import User
|
||||
|
||||
|
@ -9,8 +12,29 @@ __all__ = ['RebuildTreeMixin']
|
|||
|
||||
class RebuildTreeMixin:
|
||||
user: User
|
||||
request: Request
|
||||
|
||||
def get(self, request: Request, *args, **kwargs):
|
||||
force = is_true(request.query_params.get('rebuild_tree'))
|
||||
UserPermTreeRefreshUtil(self.user).refresh_if_need(force)
|
||||
def get(self, request, *args, **kwargs):
|
||||
UserPermTreeRefreshUtil(self.user).refresh_if_need(force=self.is_force_refresh_tree)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
@lazyproperty
|
||||
def is_force_refresh_tree(self):
|
||||
force = is_true(self.request.query_params.get('rebuild_tree'))
|
||||
if not force:
|
||||
force = self.compute_is_force_refresh()
|
||||
return force
|
||||
|
||||
def compute_is_force_refresh(self):
|
||||
""" 5s 内连续刷新三次转为强制刷新 """
|
||||
force_timeout = 5
|
||||
force_max_count = 3
|
||||
force_cache_key = '{user_id}:{path}'.format(user_id=self.user.id, path=self.request.path)
|
||||
count = cache.get(force_cache_key, 1)
|
||||
if count >= force_max_count:
|
||||
force = True
|
||||
cache.delete(force_cache_key)
|
||||
else:
|
||||
force = False
|
||||
cache.set(force_cache_key, count+1, force_timeout)
|
||||
return force
|
||||
|
|
|
@ -37,7 +37,7 @@ class BaseUserNodeWithAssetAsTreeApi(
|
|||
def list(self, request, *args, **kwargs):
|
||||
nodes, assets = self.get_nodes_assets()
|
||||
tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
||||
tree_assets = self.serialize_assets(assets, node_key=self.node_key_for_serializer_assets)
|
||||
tree_assets = self.serialize_assets(assets, node_key=self.node_key_for_serialize_assets)
|
||||
data = list(tree_nodes) + list(tree_assets)
|
||||
return Response(data=data)
|
||||
|
||||
|
@ -46,7 +46,7 @@ class BaseUserNodeWithAssetAsTreeApi(
|
|||
return [], []
|
||||
|
||||
@lazyproperty
|
||||
def node_key_for_serializer_assets(self):
|
||||
def node_key_for_serialize_assets(self):
|
||||
return None
|
||||
|
||||
|
||||
|
@ -95,19 +95,21 @@ class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
|||
class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
||||
""" 用户授权的节点的子节点与资产树 """
|
||||
|
||||
# 默认展开的节点key
|
||||
default_unfolded_node_key = None
|
||||
|
||||
def get_nodes_assets(self):
|
||||
query_node_util = UserPermNodeUtil(self.user)
|
||||
query_asset_util = UserPermAssetUtil(self.user)
|
||||
node_key = self.query_node_key
|
||||
if not node_key:
|
||||
nodes = query_node_util.get_top_level_nodes()
|
||||
assets = Asset.objects.none()
|
||||
# 获取根节点下的资产
|
||||
for node in nodes:
|
||||
if not node.key.isdigit():
|
||||
continue
|
||||
assets = query_asset_util.get_node_assets(key=node.key)
|
||||
break
|
||||
nodes, unfolded_node = query_node_util.get_top_level_nodes(with_unfolded_node=True)
|
||||
if unfolded_node:
|
||||
""" 默认展开的节点, 获取根节点下的资产 """
|
||||
assets = query_asset_util.get_node_assets(key=unfolded_node.key)
|
||||
self.default_unfolded_node_key = unfolded_node.key
|
||||
else:
|
||||
assets = Asset.objects.none()
|
||||
elif node_key == PermNode.UNGROUPED_NODE_KEY:
|
||||
nodes = PermNode.objects.none()
|
||||
assets = query_asset_util.get_ungroup_assets()
|
||||
|
@ -123,16 +125,15 @@ class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
|||
@lazyproperty
|
||||
def query_node_key(self):
|
||||
node_key = self.request.query_params.get('key', None)
|
||||
if node_key is not None:
|
||||
return node_key
|
||||
node_id = self.request.query_params.get('id', None)
|
||||
node = get_object_or_none(Node, id=node_id)
|
||||
node_key = getattr(node, 'key', None)
|
||||
if node_key is None:
|
||||
node_id = self.request.query_params.get('id', None)
|
||||
node = get_object_or_none(Node, id=node_id)
|
||||
node_key = getattr(node, 'key', None)
|
||||
return node_key
|
||||
|
||||
@lazyproperty
|
||||
def node_key_for_serializer_assets(self):
|
||||
return self.query_node_key
|
||||
def node_key_for_serialize_assets(self):
|
||||
return self.query_node_key or self.default_unfolded_node_key
|
||||
|
||||
|
||||
class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView):
|
||||
|
|
|
@ -148,15 +148,20 @@ class UserPermNodeUtil:
|
|||
assets_amount = UserPermAssetUtil(self.user).get_direct_assets().count()
|
||||
return PermNode.get_favorite_node(assets_amount)
|
||||
|
||||
def get_top_level_nodes(self):
|
||||
def get_top_level_nodes(self, with_unfolded_node=False):
|
||||
# 是否有节点展开, 展开的节点
|
||||
unfolded_node = None
|
||||
nodes = self.get_special_nodes()
|
||||
# 获取组织下的根节点
|
||||
real_nodes = self._get_indirect_perm_node_children(key='')
|
||||
real_nodes = self._get_perm_node_children_from_relation(key='')
|
||||
nodes.extend(real_nodes)
|
||||
if len(real_nodes) == 1:
|
||||
children = self.get_node_children(real_nodes[0].key)
|
||||
unfolded_node = real_nodes[0]
|
||||
children = self.get_node_children(unfolded_node.key)
|
||||
nodes.extend(children)
|
||||
return nodes
|
||||
if with_unfolded_node:
|
||||
return nodes, unfolded_node
|
||||
else:
|
||||
return nodes
|
||||
|
||||
def get_special_nodes(self):
|
||||
nodes = []
|
||||
|
@ -177,16 +182,18 @@ class UserPermNodeUtil:
|
|||
node = PermNode.objects.get(key=key)
|
||||
node.compute_node_from_and_assets_amount(self.user)
|
||||
if node.node_from == node.NodeFrom.granted:
|
||||
""" 直接授权的节点, 直接从完整资产树获取子节点 """
|
||||
children = PermNode.objects.filter(parent_key=key)
|
||||
elif node.node_from in (node.NodeFrom.asset, node.NodeFrom.child):
|
||||
children = self._get_indirect_perm_node_children(key)
|
||||
""" 间接授权的节点, 从 Relation 表中获取子节点 """
|
||||
children = self._get_perm_node_children_from_relation(key)
|
||||
else:
|
||||
children = PermNode.objects.none()
|
||||
children = sorted(children, key=lambda x: x.value)
|
||||
return children
|
||||
|
||||
def _get_indirect_perm_node_children(self, key):
|
||||
""" 获取未直接授权节点的子节点 """
|
||||
def _get_perm_node_children_from_relation(self, key):
|
||||
""" 获取授权节点的子节点, 从用户授权节点关系表中获取 """
|
||||
children = PermNode.objects.filter(granted_node_rels__user=self.user, parent_key=key)
|
||||
children = children.annotate(**PermNode.annotate_granted_node_rel_fields).distinct()
|
||||
for node in children:
|
||||
|
|
|
@ -52,8 +52,8 @@ extra_nodes_data = [
|
|||
{"id": "terminal_node", "name": _("Terminal setting"), "pId": "view_setting"},
|
||||
{'id': "task_center", "name": _("Task Center"), "pId": "view_console"},
|
||||
{'id': "my_assets", "name": _("My assets"), "pId": "view_workbench"},
|
||||
{'id': "operation_center", "name": _('Operation Center'), "pId": "view_workbench"},
|
||||
{'id': "remote_application", "name": _("Remote application"), "pId": "view_setting"},
|
||||
{'id': "operation_center", "name": _('App ops'), "pId": "view_workbench"},
|
||||
{'id': "remote_application", "name": _("Applet"), "pId": "view_setting"},
|
||||
]
|
||||
|
||||
# 将 model 放到其它节点下,而不是本来的 app 中
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.2.16 on 2022-12-23 07:36
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('terminal', '0064_auto_20221220_1956'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='applet',
|
||||
options={'verbose_name': 'Applet'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='applethost',
|
||||
options={'verbose_name': 'Applet host'},
|
||||
),
|
||||
]
|
|
@ -34,6 +34,9 @@ class Applet(JMSBaseModel):
|
|||
to='AppletHost', verbose_name=_('Hosts')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Applet")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ class AppletHost(Host):
|
|||
)
|
||||
LOCKING_ORG = '00000000-0000-0000-0000-000000000004'
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Applet host")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.16 on 2022-12-23 07:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0026_auto_20221220_1956'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='applycommandticket',
|
||||
name='apply_run_account',
|
||||
field=models.CharField(default='', max_length=128, verbose_name='Account'),
|
||||
),
|
||||
]
|
|
@ -10,7 +10,7 @@ class ApplyCommandTicket(Ticket):
|
|||
)
|
||||
apply_run_asset = models.CharField(max_length=128, verbose_name=_('Run asset'))
|
||||
apply_run_command = models.CharField(max_length=4096, verbose_name=_('Run command'))
|
||||
apply_run_account = models.CharField(max_length=128, default='', verbose_name=_('Run account'))
|
||||
apply_run_account = models.CharField(max_length=128, default='', verbose_name=_('Account'))
|
||||
apply_from_session = models.ForeignKey(
|
||||
'terminal.Session', on_delete=models.SET_NULL, null=True, verbose_name=_("Session")
|
||||
)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ "$(ps axu | grep 'celery' | grep -v 'grep' | grep -cv 'defunct')" -gt "2" ]];then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
set -e
|
||||
|
||||
test -e /tmp/worker_ready_ansible
|
||||
test -e /tmp/worker_ready_celery
|
||||
test -e /tmp/worker_heartbeat_ansible && test $(($(date +%s) - $(stat -c %Y /tmp/worker_heartbeat_ansible))) -lt 10
|
||||
test -e /tmp/worker_heartbeat_celery && test $(($(date +%s) - $(stat -c %Y /tmp/worker_heartbeat_celery))) -lt 10
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if grep -q 'source /opt/autoenv/activate.sh' ~/.bashrc; then
|
||||
echo -e "\033[31m 正在自动载入 python 环境 \033[0m"
|
||||
else
|
||||
echo -e "\033[31m 不支持自动升级,请参考 http://docs.jumpserver.org/zh/docs/upgrade.html 手动升级 \033[0m"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
source ~/.bashrc
|
||||
|
||||
cd `dirname $0`/ && cd .. && ./jms stop
|
||||
|
||||
jumpserver_backup=/tmp/jumpserver_backup$(date -d "today" +"%Y%m%d_%H%M%S")
|
||||
mkdir -p $jumpserver_backup
|
||||
cp -r ./* $jumpserver_backup
|
||||
|
||||
echo -e "\033[31m 是否需要备份Jumpserver数据库 \033[0m"
|
||||
stty erase ^H
|
||||
read -p "确认备份请按Y,否则按其他键跳过备份 " a
|
||||
if [ "$a" == y -o "$a" == Y ];then
|
||||
echo -e "\033[31m 正在备份数据库 \033[0m"
|
||||
echo -e "\033[31m 请手动输入数据库信息 \033[0m"
|
||||
read -p '请输入Jumpserver数据库ip:' DB_HOST
|
||||
read -p '请输入Jumpserver数据库端口:' DB_PORT
|
||||
read -p '请输入Jumpserver数据库名称:' DB_NAME
|
||||
read -p '请输入有权限导出数据库的用户:' DB_USER
|
||||
read -p '请输入该用户的密码:' DB_PASSWORD
|
||||
mysqldump -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME > /$jumpserver_backup/$DB_NAME$(date -d "today" +"%Y%m%d_%H%M%S").sql || {
|
||||
echo -e "\033[31m 备份数据库失败,请检查输入是否有误 \033[0m"
|
||||
exit 1
|
||||
}
|
||||
echo -e "\033[31m 备份数据库完成 \033[0m"
|
||||
else
|
||||
echo -e "\033[31m 已取消备份数据库操作 \033[0m"
|
||||
fi
|
||||
|
||||
git pull && pip install -r requirements/requirements.txt && cd utils && sh make_migrations.sh
|
||||
|
||||
cd .. && ./jms start all -d
|
||||
echo -e "\033[31m 请检查jumpserver是否启动成功 \033[0m"
|
||||
echo -e "\033[31m 备份文件存放于$jumpserver_backup目录 \033[0m"
|
||||
stty erase ^?
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue