mirror of https://github.com/jumpserver/jumpserver
commit
e6d50cc8b4
|
@ -8,3 +8,4 @@ celerybeat.pid
|
|||
### Vagrant ###
|
||||
.vagrant/
|
||||
apps/xpack/.git
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
*.mmdb filter=lfs diff=lfs merge=lfs -text
|
||||
*.mo filter=lfs diff=lfs merge=lfs -text
|
||||
*.ipdb filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
|
|
|
@ -42,3 +42,4 @@ version-resolver:
|
|||
template: |
|
||||
## 版本变化 What’s Changed
|
||||
$CHANGES
|
||||
|
||||
|
|
|
@ -41,3 +41,4 @@ release/*
|
|||
releashe
|
||||
/apps/script.py
|
||||
data/*
|
||||
|
||||
|
|
|
@ -126,3 +126,4 @@ enforcement ladder](https://github.com/mozilla/diversity).
|
|||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
||||
|
|
|
@ -23,3 +23,4 @@ When reporting issues, always include:
|
|||
|
||||
Because the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can
|
||||
replace those parts with "REDACTED" or other strings like "****".
|
||||
|
||||
|
|
45
Dockerfile
45
Dockerfile
|
@ -1,4 +1,15 @@
|
|||
FROM python:3.8-slim as stage-build
|
||||
ARG TARGETARCH
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION=$VERSION
|
||||
|
||||
WORKDIR /opt/jumpserver
|
||||
ADD . .
|
||||
RUN cd utils && bash -ixeu build.sh
|
||||
|
||||
FROM python:3.8-slim
|
||||
ARG TARGETARCH
|
||||
MAINTAINER JumpServer Team <ibuler@qq.com>
|
||||
|
||||
ARG BUILD_DEPENDENCIES=" \
|
||||
|
@ -21,9 +32,9 @@ ARG DEPENDENCIES=" \
|
|||
sshpass"
|
||||
|
||||
ARG TOOLS=" \
|
||||
ca-certificates \
|
||||
curl \
|
||||
default-mysql-client \
|
||||
iproute2 \
|
||||
iputils-ping \
|
||||
locales \
|
||||
procps \
|
||||
|
@ -33,21 +44,22 @@ ARG TOOLS=" \
|
|||
unzip \
|
||||
wget"
|
||||
|
||||
RUN sed -i 's@http://.*.debian.org@http://mirrors.ustc.edu.cn@g' /etc/apt/sources.list \
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
|
||||
sed -i 's@http://.*.debian.org@http://mirrors.ustc.edu.cn@g' /etc/apt/sources.list \
|
||||
&& rm -f /etc/apt/apt.conf.d/docker-clean \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& apt-get update \
|
||||
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
|
||||
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
|
||||
&& apt-get -y install --no-install-recommends ${TOOLS} \
|
||||
&& localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 \
|
||||
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& mkdir -p /root/.ssh/ \
|
||||
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
|
||||
&& sed -i "s@# alias l@alias l@g" ~/.bashrc \
|
||||
&& echo "set mouse-=a" > ~/.vimrc \
|
||||
&& echo "no" | dpkg-reconfigure dash \
|
||||
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG ORACLE_LIB_MAJOR=19
|
||||
ARG ORACLE_LIB_MINOR=10
|
||||
ENV ORACLE_FILE="instantclient-basiclite-linux.${TARGETARCH:-amd64}-${ORACLE_LIB_MAJOR}.${ORACLE_LIB_MINOR}.0.0.0dbru.zip"
|
||||
|
@ -68,21 +80,18 @@ ARG PIP_MIRROR=https://pypi.douban.com/simple
|
|||
ENV PIP_MIRROR=$PIP_MIRROR
|
||||
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
|
||||
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
|
||||
# 因为以 jms 或者 jumpserver 开头的 mirror 上可能没有
|
||||
RUN pip install --upgrade pip==20.2.4 setuptools==49.6.0 wheel==0.34.2 -i ${PIP_MIRROR} \
|
||||
&& pip install --no-cache-dir $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
|
||||
&& pip install --no-cache-dir -r requirements/requirements.txt -i ${PIP_MIRROR} \
|
||||
&& rm -rf ~/.cache/pip
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION=$VERSION
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
set -ex \
|
||||
&& pip config set global.index-url ${PIP_MIRROR} \
|
||||
&& pip install --upgrade pip \
|
||||
&& pip install --upgrade setuptools wheel \
|
||||
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
|
||||
&& pip install -r requirements/requirements.txt
|
||||
|
||||
ADD . .
|
||||
RUN cd utils \
|
||||
&& bash -ixeu build.sh \
|
||||
&& mv ../release/jumpserver /opt/jumpserver \
|
||||
&& rm -rf /tmp/build \
|
||||
&& echo > /opt/jumpserver/config.yml
|
||||
COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
|
||||
RUN echo > /opt/jumpserver/config.yml \
|
||||
&& rm -rf /tmp/build
|
||||
|
||||
WORKDIR /opt/jumpserver
|
||||
VOLUME /opt/jumpserver/data
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
FROM python:3.8-slim as stage-build
|
||||
ARG TARGETARCH
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION=$VERSION
|
||||
|
||||
WORKDIR /opt/jumpserver
|
||||
ADD . .
|
||||
RUN cd utils && bash -ixeu build.sh
|
||||
|
||||
FROM python:3.8-slim
|
||||
ARG TARGETARCH
|
||||
MAINTAINER JumpServer Team <ibuler@qq.com>
|
||||
|
||||
ARG BUILD_DEPENDENCIES=" \
|
||||
g++ \
|
||||
make \
|
||||
pkg-config"
|
||||
|
||||
ARG DEPENDENCIES=" \
|
||||
default-libmysqlclient-dev \
|
||||
freetds-dev \
|
||||
libpq-dev \
|
||||
libffi-dev \
|
||||
libldap2-dev \
|
||||
libsasl2-dev \
|
||||
libxml2-dev \
|
||||
libxmlsec1-dev \
|
||||
libxmlsec1-openssl \
|
||||
libaio-dev \
|
||||
openssh-client \
|
||||
sshpass"
|
||||
|
||||
ARG TOOLS=" \
|
||||
ca-certificates \
|
||||
curl \
|
||||
default-mysql-client \
|
||||
iputils-ping \
|
||||
locales \
|
||||
netcat \
|
||||
redis-server \
|
||||
telnet \
|
||||
vim \
|
||||
unzip \
|
||||
wget"
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
|
||||
set -ex \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& apt-get update \
|
||||
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
|
||||
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
|
||||
&& apt-get -y install --no-install-recommends ${TOOLS} \
|
||||
&& mkdir -p /root/.ssh/ \
|
||||
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
|
||||
&& sed -i "s@# alias l@alias l@g" ~/.bashrc \
|
||||
&& echo "set mouse-=a" > ~/.vimrc \
|
||||
&& echo "no" | dpkg-reconfigure dash \
|
||||
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /tmp/build
|
||||
COPY ./requirements ./requirements
|
||||
|
||||
ARG PIP_MIRROR=https://pypi.douban.com/simple
|
||||
ENV PIP_MIRROR=$PIP_MIRROR
|
||||
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
|
||||
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
set -ex \
|
||||
&& pip config set global.index-url ${PIP_MIRROR} \
|
||||
&& pip install --upgrade pip \
|
||||
&& pip install --upgrade setuptools wheel \
|
||||
&& pip install https://download.jumpserver.org/pypi/simple/cryptography/cryptography-36.0.1-cp38-cp38-linux_loongarch64.whl \
|
||||
&& pip install https://download.jumpserver.org/pypi/simple/greenlet/greenlet-1.1.2-cp38-cp38-linux_loongarch64.whl \
|
||||
&& pip install $(grep 'PyNaCl' requirements/requirements.txt) \
|
||||
&& GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=true pip install grpcio \
|
||||
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
|
||||
&& pip install -r requirements/requirements.txt
|
||||
|
||||
COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
|
||||
RUN echo > /opt/jumpserver/config.yml \
|
||||
&& rm -rf /tmp/build
|
||||
|
||||
WORKDIR /opt/jumpserver
|
||||
VOLUME /opt/jumpserver/data
|
||||
VOLUME /opt/jumpserver/logs
|
||||
|
||||
ENV LANG=zh_CN.UTF-8
|
||||
|
||||
EXPOSE 8070
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
1
LICENSE
1
LICENSE
|
@ -672,3 +672,4 @@ may consider it more useful to permit linking proprietary applications with
|
|||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
|
|
|
@ -22,9 +22,7 @@ JumpServer 使用 Python 开发,配备了业界领先的 Web Terminal 方案
|
|||
|
||||
JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向扩展,无资产数量及并发限制。
|
||||
|
||||
改变世界,从一点点开始 ...
|
||||
|
||||
> 如需进一步了解 JumpServer 开源项目,推荐阅读 [JumpServer 的初心和使命](https://mp.weixin.qq.com/s/S6q_2rP_9MwaVwyqLQnXzA)
|
||||
|
||||
### 特色优势
|
||||
|
||||
|
@ -102,7 +100,7 @@ JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向
|
|||
- [沐瞳游戏:通过JumpServer管控多项目分布式资产](https://blog.fit2cloud.com/?p=3213)
|
||||
- [携程:JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
|
||||
- [大智慧:JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
|
||||
- [小红书:的JumpServer堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
|
||||
- [小红书:JumpServer 堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
|
||||
- [中手游:JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
|
||||
- [中通快递:JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
|
||||
- [东方明珠:JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
|
||||
|
|
|
@ -92,4 +92,3 @@ Licensed under The GNU General Public License version 3 (GPLv3) (the "License")
|
|||
https://www.gnu.org/licenses/gpl-3.0.htmll
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
|
|
|
@ -18,3 +18,4 @@ All security bugs should be reported to the contact as below:
|
|||
- ibuler@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
# The most common configuration options are documented and commented below.
|
||||
# For a complete reference, please see the online documentation at
|
||||
# https://docs.vagrantup.com.
|
||||
|
||||
# Every Vagrant development environment requires a box. You can search for
|
||||
# boxes at https://vagrantcloud.com/search.
|
||||
config.vm.box_check_update = false
|
||||
config.vm.box = "centos/7"
|
||||
config.vm.hostname = "jumpserver"
|
||||
config.vm.network "private_network", ip: "172.17.8.101"
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.memory = "4096"
|
||||
vb.cpus = 2
|
||||
vb.name = "jumpserver"
|
||||
end
|
||||
|
||||
config.vm.synced_folder ".", "/vagrant", type: "rsync",
|
||||
rsync__verbose: true,
|
||||
rsync__exclude: ['.git*', 'node_modules*','*.log','*.box','Vagrantfile']
|
||||
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
## 设置yum的阿里云源
|
||||
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
|
||||
sudo sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
|
||||
sudo curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
|
||||
sudo yum makecache
|
||||
|
||||
## 安装依赖包
|
||||
sudo yum install -y python36 python36-devel python36-pip \
|
||||
libtiff-devel libjpeg-devel libzip-devel freetype-devel \
|
||||
lcms2-devel libwebp-devel tcl-devel tk-devel sshpass \
|
||||
openldap-devel mariadb-devel mysql-devel libffi-devel \
|
||||
openssh-clients telnet openldap-clients gcc
|
||||
|
||||
## 配置pip阿里云源
|
||||
mkdir /home/vagrant/.pip
|
||||
cat << EOF | sudo tee -a /home/vagrant/.pip/pip.conf
|
||||
[global]
|
||||
timeout = 6000
|
||||
index-url = https://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
[install]
|
||||
use-mirrors = true
|
||||
mirrors = https://mirrors.aliyun.com/pypi/simple/
|
||||
trusted-host=mirrors.aliyun.com
|
||||
EOF
|
||||
|
||||
python3.6 -m venv /home/vagrant/venv
|
||||
source /home/vagrant/venv/bin/activate
|
||||
echo 'source /home/vagrant/venv/bin/activate' >> /home/vagrant/.bash_profile
|
||||
SHELL
|
||||
end
|
|
@ -4,6 +4,7 @@ from orgs.mixins.api import OrgBulkModelViewSet
|
|||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
from common.tree import TreeNodeSerializer
|
||||
from common.mixins.api import SuggestionMixin
|
||||
from .. import serializers
|
||||
|
|
|
@ -7,3 +7,7 @@ from django.apps import AppConfig
|
|||
class ApplicationsConfig(AppConfig):
|
||||
name = 'applications'
|
||||
verbose_name = _('Applications')
|
||||
|
||||
def ready(self):
|
||||
from . import signal_handlers
|
||||
super().ready()
|
||||
|
|
|
@ -83,9 +83,3 @@ class AppType(models.TextChoices):
|
|||
if AppCategory.is_xpack(category):
|
||||
return True
|
||||
return tp in ['oracle', 'postgresql', 'sqlserver']
|
||||
|
||||
|
||||
class OracleVersion(models.TextChoices):
|
||||
version_11g = '11g', '11g'
|
||||
version_12c = '12c', '12c'
|
||||
version_other = 'other', _('Other')
|
||||
|
|
|
@ -10,9 +10,7 @@ from common.mixins import CommonModelMixin
|
|||
from common.tree import TreeNode
|
||||
from common.utils import is_uuid
|
||||
from assets.models import Asset, SystemUser
|
||||
from ..const import OracleVersion
|
||||
|
||||
from ..utils import KubernetesTree
|
||||
from .. import const
|
||||
|
||||
|
||||
|
@ -175,6 +173,7 @@ class ApplicationTreeNodeMixin:
|
|||
return pid
|
||||
|
||||
def as_tree_node(self, pid, k8s_as_tree=False):
|
||||
from ..utils import KubernetesTree
|
||||
if self.type == const.AppType.k8s and k8s_as_tree:
|
||||
node = KubernetesTree(pid).as_tree_node(self)
|
||||
else:
|
||||
|
@ -304,15 +303,6 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
|
|||
target_ip = self.attrs.get('host')
|
||||
return target_ip
|
||||
|
||||
def get_target_protocol_for_oracle(self):
|
||||
""" Oracle 类型需要单独处理,因为要携带版本号 """
|
||||
if not self.is_type(self.APP_TYPE.oracle):
|
||||
return
|
||||
version = self.attrs.get('version', OracleVersion.version_12c)
|
||||
if version == OracleVersion.version_other:
|
||||
return
|
||||
return 'oracle_%s' % version
|
||||
|
||||
|
||||
class ApplicationUser(SystemUser):
|
||||
class Meta:
|
||||
|
|
|
@ -2,15 +2,9 @@ from rest_framework import serializers
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from ..application_category import DBSerializer
|
||||
from applications.const import OracleVersion
|
||||
|
||||
__all__ = ['OracleSerializer']
|
||||
|
||||
|
||||
class OracleSerializer(DBSerializer):
|
||||
version = serializers.ChoiceField(
|
||||
choices=OracleVersion.choices, default=OracleVersion.version_12c,
|
||||
allow_null=True, label=_('Version'),
|
||||
help_text=_('Magnus currently supports only 11g and 12c connections')
|
||||
)
|
||||
port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
|
@ -208,7 +208,8 @@ class SystemUserTaskApi(generics.CreateAPIView):
|
|||
|
||||
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
|
||||
rbac_perms = {
|
||||
'list': 'assets.view_commandfilterule'
|
||||
'list': 'assets.view_commandfilterule',
|
||||
'GET': 'assets.view_commandfilterule',
|
||||
}
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
@ -223,12 +224,14 @@ class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
|
|||
if not system_user:
|
||||
system_user_id = self.request.query_params.get('system_user_id')
|
||||
asset_id = self.request.query_params.get('asset_id')
|
||||
node_id = self.request.query_params.get('node_id')
|
||||
application_id = self.request.query_params.get('application_id')
|
||||
rules = CommandFilterRule.get_queryset(
|
||||
user_id=user_id,
|
||||
user_group_id=user_group_id,
|
||||
system_user_id=system_user_id,
|
||||
asset_id=asset_id,
|
||||
node_id=node_id,
|
||||
application_id=application_id
|
||||
)
|
||||
return rules
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.15 on 2022-10-09 09:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0091_auto_20220629_1826'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='commandfilter',
|
||||
name='nodes',
|
||||
field=models.ManyToManyField(blank=True, related_name='cmd_filters', to='assets.Node', verbose_name='Nodes'),
|
||||
),
|
||||
]
|
|
@ -116,7 +116,7 @@ class NodesRelationMixin:
|
|||
nodes = []
|
||||
for node in self.get_nodes():
|
||||
_nodes = node.get_ancestors(with_self=True)
|
||||
nodes.append(_nodes)
|
||||
nodes.extend(list(_nodes))
|
||||
if flat:
|
||||
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
|
||||
return nodes
|
||||
|
|
|
@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from users.models import User, UserGroup
|
||||
from applications.models import Application
|
||||
from ..models import SystemUser, Asset
|
||||
from ..models import SystemUser, Asset, Node
|
||||
|
||||
from common.utils import lazyproperty, get_logger, get_object_or_none
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
|
@ -33,6 +33,10 @@ class CommandFilter(OrgModelMixin):
|
|||
'users.UserGroup', related_name='cmd_filters', blank=True,
|
||||
verbose_name=_("User group"),
|
||||
)
|
||||
nodes = models.ManyToManyField(
|
||||
'assets.Node', related_name='cmd_filters', blank=True,
|
||||
verbose_name=_("Nodes")
|
||||
)
|
||||
assets = models.ManyToManyField(
|
||||
'assets.Asset', related_name='cmd_filters', blank=True,
|
||||
verbose_name=_("Asset")
|
||||
|
@ -189,7 +193,8 @@ class CommandFilterRule(OrgModelMixin):
|
|||
|
||||
@classmethod
|
||||
def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None,
|
||||
asset_id=None, application_id=None, org_id=None):
|
||||
asset_id=None, node_id=None, application_id=None, org_id=None):
|
||||
# user & user_group
|
||||
user_groups = []
|
||||
user = get_object_or_none(User, pk=user_id)
|
||||
if user:
|
||||
|
@ -198,8 +203,18 @@ class CommandFilterRule(OrgModelMixin):
|
|||
if user_group:
|
||||
org_id = user_group.org_id
|
||||
user_groups.append(user_group)
|
||||
system_user = get_object_or_none(SystemUser, pk=system_user_id)
|
||||
|
||||
# asset & node
|
||||
nodes = []
|
||||
asset = get_object_or_none(Asset, pk=asset_id)
|
||||
if asset:
|
||||
nodes.extend(asset.get_all_nodes())
|
||||
node = get_object_or_none(Node, pk=node_id)
|
||||
if node:
|
||||
org_id = node.org_id
|
||||
nodes.extend(list(node.get_ancestors(with_self=True)))
|
||||
|
||||
system_user = get_object_or_none(SystemUser, pk=system_user_id)
|
||||
application = get_object_or_none(Application, pk=application_id)
|
||||
q = Q()
|
||||
if user:
|
||||
|
@ -212,6 +227,8 @@ class CommandFilterRule(OrgModelMixin):
|
|||
if asset:
|
||||
org_id = asset.org_id
|
||||
q |= Q(assets=asset)
|
||||
if nodes:
|
||||
q |= Q(nodes__in=set(nodes))
|
||||
if application:
|
||||
org_id = application.org_id
|
||||
q |= Q(applications=application)
|
||||
|
|
|
@ -21,7 +21,7 @@ class CommandFilterSerializer(BulkOrgResourceModelSerializer):
|
|||
'comment', 'created_by',
|
||||
]
|
||||
fields_fk = ['rules']
|
||||
fields_m2m = ['users', 'user_groups', 'system_users', 'assets', 'applications']
|
||||
fields_m2m = ['users', 'user_groups', 'system_users', 'nodes', 'assets', 'applications']
|
||||
fields = fields_small + fields_fk + fields_m2m
|
||||
extra_kwargs = {
|
||||
'rules': {'read_only': True},
|
||||
|
|
|
@ -62,12 +62,15 @@ class ConnectionTokenMixin:
|
|||
|
||||
def get_smart_endpoint(self, protocol, asset=None, application=None):
|
||||
if asset:
|
||||
target_instance = asset
|
||||
target_ip = asset.get_target_ip()
|
||||
elif application:
|
||||
target_instance = application
|
||||
target_ip = application.get_target_ip()
|
||||
else:
|
||||
target_instance = None
|
||||
target_ip = ''
|
||||
endpoint = EndpointRule.match_endpoint(target_ip, protocol, self.request)
|
||||
endpoint = EndpointRule.match_endpoint(target_instance, target_ip, protocol, self.request)
|
||||
return endpoint
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -55,7 +55,9 @@ class OAuth2AuthCallbackView(View):
|
|||
)
|
||||
|
||||
logger.debug(log_prompt.format('Redirect'))
|
||||
return HttpResponseRedirect(settings.AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI)
|
||||
# OAuth2 服务端认证成功, 但是用户被禁用了, 这时候需要调用服务端的logout
|
||||
redirect_url = settings.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
|
||||
class OAuth2EndSessionView(View):
|
||||
|
|
|
@ -271,7 +271,10 @@ class Saml2AuthCallbackView(View, PrepareRequestMixin):
|
|||
auth.login(self.request, user)
|
||||
|
||||
logger.debug(log_prompt.format('Redirect'))
|
||||
next_url = saml_instance.redirect_to(post_data.get('RelayState', '/'))
|
||||
redir = post_data.get('RelayState')
|
||||
if not redir or len(redir) == 0:
|
||||
redir = "/"
|
||||
next_url = saml_instance.redirect_to(redir)
|
||||
return HttpResponseRedirect(next_url)
|
||||
|
||||
@csrf_exempt
|
||||
|
|
|
@ -30,7 +30,9 @@ class CeleryBaseService(BaseService):
|
|||
'-l', 'INFO',
|
||||
'-c', str(self.num),
|
||||
'-Q', self.queue,
|
||||
'-n', f'{self.queue}@{server_hostname}'
|
||||
'--heartbeat-interval', '10',
|
||||
'-n', f'{self.queue}@{server_hostname}',
|
||||
'--without-mingle',
|
||||
]
|
||||
return cmd
|
||||
|
||||
|
|
|
@ -19,11 +19,13 @@ class FlowerService(BaseService):
|
|||
'celery',
|
||||
'-A', 'ops',
|
||||
'flower',
|
||||
'-l', 'INFO',
|
||||
'-logging=info',
|
||||
'--url_prefix=/core/flower',
|
||||
'--auto_refresh=False',
|
||||
'--max_tasks=1000',
|
||||
'--tasks_columns=uuid,name,args,state,received,started,runtime,worker'
|
||||
'--persistent=True',
|
||||
'-db=/opt/jumpserver/data/flower.db',
|
||||
'--state_save_interval=600000'
|
||||
]
|
||||
return cmd
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class EBCCipher:
|
|||
def __padding(val):
|
||||
# padding
|
||||
val = bytes(val)
|
||||
while len(val) % 16 != 0:
|
||||
while len(val) == 0 or len(val) % 16 != 0:
|
||||
val += b'\0'
|
||||
return val
|
||||
|
||||
|
|
|
@ -53,7 +53,9 @@ class Subscription:
|
|||
error(msg, item)
|
||||
logger.error('Subscribe handler handle msg error: {}'.format(e))
|
||||
except Exception as e:
|
||||
logger.error('Consume msg error: {}'.format(e))
|
||||
# 正常的 websocket 断开时, redis 会断开连接,避免日志太多
|
||||
# logger.error('Consume msg error: {}'.format(e))
|
||||
pass
|
||||
|
||||
try:
|
||||
complete()
|
||||
|
|
|
@ -82,7 +82,8 @@ class PiicoSM4EcbCrypto(BaseCrypto):
|
|||
return self.cipher.encrypt(self.to_16(data))
|
||||
|
||||
def _decrypt(self, data: bytes) -> bytes:
|
||||
return self.cipher.decrypt(data)
|
||||
bs = self.cipher.decrypt(data)
|
||||
return bs.rstrip(b'\0')
|
||||
|
||||
|
||||
class AESCrypto:
|
||||
|
@ -232,6 +233,8 @@ class Crypto:
|
|||
return self.cryptos[0]
|
||||
|
||||
def encrypt(self, text):
|
||||
if text is None:
|
||||
return text
|
||||
return self.encryptor.encrypt(text)
|
||||
|
||||
def decrypt(self, text):
|
||||
|
@ -241,7 +244,7 @@ class Crypto:
|
|||
if origin_text:
|
||||
# 有时不同算法解密不报错,但是返回空字符串
|
||||
return origin_text
|
||||
except (TypeError, ValueError, UnicodeDecodeError, IndexError):
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ def random_string(length, lower=True, upper=True, digit=True, special_char=False
|
|||
|
||||
if special_char:
|
||||
spc = random.choice(string_punctuation)
|
||||
i = random.choice(range(len(password)))
|
||||
i = random.choice(range(1, len(password)))
|
||||
password[i] = spc
|
||||
|
||||
password = ''.join(password)
|
||||
|
|
|
@ -484,6 +484,9 @@ class Config(dict):
|
|||
'SERVER_REPLAY_STORAGE': {},
|
||||
'SECURITY_DATA_CRYPTO_ALGO': None,
|
||||
'GMSSL_ENABLED': False,
|
||||
# Magnus 组件需要监听的端口范围
|
||||
'MAGNUS_DB_PORTS_START': 30000,
|
||||
'MAGNUS_DB_PORTS_LIMIT_COUNT': 1000,
|
||||
|
||||
# 记录清理清理
|
||||
'LOGIN_LOG_KEEP_DAYS': 200,
|
||||
|
|
|
@ -161,6 +161,8 @@ AUTH_OAUTH2_CLIENT_SECRET = CONFIG.AUTH_OAUTH2_CLIENT_SECRET
|
|||
AUTH_OAUTH2_CLIENT_ID = CONFIG.AUTH_OAUTH2_CLIENT_ID
|
||||
AUTH_OAUTH2_SCOPE = CONFIG.AUTH_OAUTH2_SCOPE
|
||||
AUTH_OAUTH2_USER_ATTR_MAP = CONFIG.AUTH_OAUTH2_USER_ATTR_MAP
|
||||
AUTH_OAUTH2_LOGOUT_COMPLETELY = CONFIG.AUTH_OAUTH2_LOGOUT_COMPLETELY
|
||||
AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT = CONFIG.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT
|
||||
AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:oauth2:login-callback'
|
||||
AUTH_OAUTH2_AUTHENTICATION_REDIRECT_URI = '/'
|
||||
AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI = '/'
|
||||
|
|
|
@ -177,3 +177,7 @@ HELP_SUPPORT_URL = CONFIG.HELP_SUPPORT_URL
|
|||
|
||||
SESSION_RSA_PRIVATE_KEY_NAME = 'jms_private_key'
|
||||
SESSION_RSA_PUBLIC_KEY_NAME = 'jms_public_key'
|
||||
|
||||
# Magnus DB Port
|
||||
MAGNUS_DB_PORTS_START = CONFIG.MAGNUS_DB_PORTS_START
|
||||
MAGNUS_DB_PORTS_LIMIT_COUNT = CONFIG.MAGNUS_DB_PORTS_LIMIT_COUNT
|
||||
|
|
|
@ -9,7 +9,6 @@ from .base import (
|
|||
)
|
||||
from ..const import CONFIG, PROJECT_DIR
|
||||
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
# Use Django's standard `django.contrib.auth` permissions,
|
||||
# or allow read-only access for unauthenticated users.
|
||||
|
@ -64,7 +63,6 @@ SWAGGER_SETTINGS = {
|
|||
'DEFAULT_INFO': 'jumpserver.views.swagger.api_info',
|
||||
}
|
||||
|
||||
|
||||
# Captcha settings, more see https://django-simple-captcha.readthedocs.io/en/latest/advanced.html
|
||||
CAPTCHA_IMAGE_SIZE = (180, 38)
|
||||
CAPTCHA_FOREGROUND_COLOR = '#001100'
|
||||
|
@ -81,7 +79,6 @@ BOOTSTRAP3 = {
|
|||
'required_css_class': 'required',
|
||||
}
|
||||
|
||||
|
||||
# Django channels support websocket
|
||||
if not REDIS_USE_SSL:
|
||||
redis_ssl = None
|
||||
|
@ -108,7 +105,6 @@ CHANNEL_LAYERS = {
|
|||
}
|
||||
ASGI_APPLICATION = 'jumpserver.routing.application'
|
||||
|
||||
|
||||
# Dump all celery log to here
|
||||
CELERY_LOG_DIR = os.path.join(PROJECT_DIR, 'data', 'celery')
|
||||
|
||||
|
@ -131,6 +127,7 @@ CELERY_TASK_EAGER_PROPAGATES = True
|
|||
CELERY_WORKER_REDIRECT_STDOUTS = True
|
||||
CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO"
|
||||
CELERY_TASK_SOFT_TIME_LIMIT = 3600
|
||||
CELERY_WORKER_CANCEL_LONG_RUNNING_TASKS_ON_CONNECTION_LOSS = True
|
||||
|
||||
if REDIS_USE_SSL:
|
||||
CELERY_BROKER_USE_SSL = CELERY_REDIS_BACKEND_USE_SSL = {
|
||||
|
@ -148,3 +145,7 @@ REDIS_PORT = CONFIG.REDIS_PORT
|
|||
REDIS_PASSWORD = CONFIG.REDIS_PASSWORD
|
||||
|
||||
DJANGO_REDIS_SCAN_ITERSIZE = 1000
|
||||
|
||||
# GM DEVICE
|
||||
PIICO_DEVICE_ENABLE = CONFIG.PIICO_DEVICE_ENABLE
|
||||
PIICO_DRIVER_PATH = CONFIG.PIICO_DRIVER_PATH
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4d5dcc300fa64f04b513b670af0d9717fcdb108b54ddf264971b355cc72178de
|
||||
size 132193
|
||||
oid sha256:529cf82721ab8594a7ee8e4e1bff1f80fb702d3bfcbb5fb6e7bfb8b897d4920b
|
||||
size 132560
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-09-15 15:52+0800\n"
|
||||
"POT-Creation-Date: 2022-09-22 19:01+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -23,13 +23,13 @@ msgid "Acls"
|
|||
msgstr "Acls"
|
||||
|
||||
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
|
||||
#: applications/models/application.py:220 assets/models/asset.py:138
|
||||
#: applications/models/application.py:219 assets/models/asset.py:138
|
||||
#: assets/models/base.py:175 assets/models/cluster.py:18
|
||||
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
|
||||
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
|
||||
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
|
||||
#: settings/models.py:33 settings/serializers/sms.py:6
|
||||
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
|
||||
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
|
||||
#: terminal/models/storage.py:26 terminal/models/task.py:16
|
||||
#: terminal/models/terminal.py:100 users/forms/profile.py:33
|
||||
#: users/models/group.py:15 users/models/user.py:669
|
||||
|
@ -38,12 +38,12 @@ msgid "Name"
|
|||
msgstr "名前"
|
||||
|
||||
#: acls/models/base.py:27 assets/models/cmd_filter.py:84
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:89
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:90
|
||||
msgid "Priority"
|
||||
msgstr "優先順位"
|
||||
|
||||
#: acls/models/base.py:28 assets/models/cmd_filter.py:84
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:90
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:91
|
||||
msgid "1-100, the lower the value will be match first"
|
||||
msgstr "1-100、低い値は最初に一致します"
|
||||
|
||||
|
@ -53,7 +53,7 @@ msgstr "1-100、低い値は最初に一致します"
|
|||
msgid "Active"
|
||||
msgstr "アクティブ"
|
||||
|
||||
#: acls/models/base.py:32 applications/models/application.py:233
|
||||
#: acls/models/base.py:32 applications/models/application.py:232
|
||||
#: assets/models/asset.py:143 assets/models/asset.py:231
|
||||
#: assets/models/backup.py:54 assets/models/base.py:180
|
||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:48
|
||||
|
@ -61,7 +61,7 @@ msgstr "アクティブ"
|
|||
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
|
||||
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
|
||||
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
|
||||
#: terminal/models/endpoint.py:22 terminal/models/endpoint.py:97
|
||||
#: terminal/models/storage.py:29 terminal/models/terminal.py:114
|
||||
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
|
||||
#: users/models/group.py:16 users/models/user.py:706
|
||||
|
@ -241,7 +241,7 @@ msgstr ""
|
|||
msgid "Time Period"
|
||||
msgstr "期間"
|
||||
|
||||
#: applications/apps.py:9 applications/models/application.py:64
|
||||
#: applications/apps.py:9 applications/models/application.py:62
|
||||
msgid "Applications"
|
||||
msgstr "アプリケーション"
|
||||
|
||||
|
@ -260,11 +260,7 @@ msgstr "リモートアプリ"
|
|||
msgid "Custom"
|
||||
msgstr "カスタム"
|
||||
|
||||
#: applications/const.py:91 rbac/tree.py:29
|
||||
msgid "Other"
|
||||
msgstr "その他"
|
||||
|
||||
#: applications/models/account.py:12 applications/models/application.py:237
|
||||
#: applications/models/account.py:12 applications/models/application.py:236
|
||||
#: assets/models/backup.py:32 assets/models/cmd_filter.py:45
|
||||
#: authentication/models.py:67 authentication/models.py:95
|
||||
#: perms/models/application_permission.py:28
|
||||
|
@ -282,9 +278,8 @@ msgstr "アプリケーション"
|
|||
msgid "System user"
|
||||
msgstr "システムユーザー"
|
||||
|
||||
#: applications/models/account.py:17
|
||||
#: applications/serializers/attrs/application_type/oracle.py:13
|
||||
#: assets/models/authbook.py:21 settings/serializers/auth/cas.py:18
|
||||
#: applications/models/account.py:17 assets/models/authbook.py:21
|
||||
#: settings/serializers/auth/cas.py:18
|
||||
msgid "Version"
|
||||
msgstr "バージョン"
|
||||
|
||||
|
@ -300,7 +295,7 @@ msgstr "アプリケーションアカウントの秘密を表示できます"
|
|||
msgid "Can change application account secret"
|
||||
msgstr "アプリケーションアカウントの秘密を変更できます"
|
||||
|
||||
#: applications/models/application.py:222
|
||||
#: applications/models/application.py:221
|
||||
#: applications/serializers/application.py:101 assets/models/label.py:21
|
||||
#: perms/models/application_permission.py:21
|
||||
#: perms/serializers/application/user_permission.py:33
|
||||
|
@ -309,7 +304,7 @@ msgstr "アプリケーションアカウントの秘密を変更できます"
|
|||
msgid "Category"
|
||||
msgstr "カテゴリ"
|
||||
|
||||
#: applications/models/application.py:225
|
||||
#: applications/models/application.py:224
|
||||
#: applications/serializers/application.py:103 assets/models/backup.py:49
|
||||
#: assets/models/cmd_filter.py:82 assets/models/user.py:250
|
||||
#: authentication/models.py:70 perms/models/application_permission.py:24
|
||||
|
@ -323,21 +318,21 @@ msgstr "カテゴリ"
|
|||
msgid "Type"
|
||||
msgstr "タイプ"
|
||||
|
||||
#: applications/models/application.py:229 assets/models/asset.py:217
|
||||
#: applications/models/application.py:228 assets/models/asset.py:217
|
||||
#: assets/models/domain.py:29 assets/models/domain.py:64
|
||||
msgid "Domain"
|
||||
msgstr "ドメイン"
|
||||
|
||||
#: applications/models/application.py:231 xpack/plugins/cloud/models.py:33
|
||||
#: applications/models/application.py:230 xpack/plugins/cloud/models.py:33
|
||||
#: xpack/plugins/cloud/serializers/account.py:63
|
||||
msgid "Attrs"
|
||||
msgstr "ツールバーの"
|
||||
|
||||
#: applications/models/application.py:241
|
||||
#: applications/models/application.py:240
|
||||
msgid "Can match application"
|
||||
msgstr "アプリケーションを一致させることができます"
|
||||
|
||||
#: applications/models/application.py:320
|
||||
#: applications/models/application.py:310
|
||||
msgid "Application user"
|
||||
msgstr "アプリケーションユーザー"
|
||||
|
||||
|
@ -393,7 +388,7 @@ msgstr "クラスター"
|
|||
|
||||
#: applications/serializers/attrs/application_category/db.py:11
|
||||
#: ops/models/adhoc.py:157 settings/serializers/auth/radius.py:14
|
||||
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:11
|
||||
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:15
|
||||
#: xpack/plugins/cloud/serializers/account_attrs.py:72
|
||||
msgid "Host"
|
||||
msgstr "ホスト"
|
||||
|
@ -402,7 +397,7 @@ msgstr "ホスト"
|
|||
#: applications/serializers/attrs/application_type/mongodb.py:10
|
||||
#: applications/serializers/attrs/application_type/mysql.py:10
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:22
|
||||
#: applications/serializers/attrs/application_type/oracle.py:16
|
||||
#: applications/serializers/attrs/application_type/oracle.py:10
|
||||
#: applications/serializers/attrs/application_type/pgsql.py:10
|
||||
#: applications/serializers/attrs/application_type/redis.py:10
|
||||
#: applications/serializers/attrs/application_type/sqlserver.py:10
|
||||
|
@ -497,10 +492,6 @@ msgstr "Mysql workbench のユーザー名"
|
|||
msgid "Mysql workbench password"
|
||||
msgstr "Mysql workbench パスワード"
|
||||
|
||||
#: applications/serializers/attrs/application_type/oracle.py:14
|
||||
msgid "Magnus currently supports only 11g and 12c connections"
|
||||
msgstr "現在、Magnusは11gおよび12cバージョンへの接続のみをサポートしています"
|
||||
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:26
|
||||
msgid "Vmware username"
|
||||
msgstr "Vmware ユーザー名"
|
||||
|
@ -3457,6 +3448,10 @@ msgstr "監査ビュー"
|
|||
msgid "System setting"
|
||||
msgstr "システム設定"
|
||||
|
||||
#: rbac/tree.py:29
|
||||
msgid "Other"
|
||||
msgstr "その他"
|
||||
|
||||
#: rbac/tree.py:37
|
||||
msgid "Accounts"
|
||||
msgstr "アカウント"
|
||||
|
@ -4956,58 +4951,34 @@ msgstr "ストレージが無効です"
|
|||
msgid "Command record"
|
||||
msgstr "コマンドレコード"
|
||||
|
||||
#: terminal/models/endpoint.py:13
|
||||
#: terminal/models/endpoint.py:17
|
||||
msgid "HTTPS Port"
|
||||
msgstr "HTTPS ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:14 terminal/models/terminal.py:107
|
||||
#: terminal/models/endpoint.py:18 terminal/models/terminal.py:107
|
||||
msgid "HTTP Port"
|
||||
msgstr "HTTP ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:15 terminal/models/terminal.py:106
|
||||
#: terminal/models/endpoint.py:19 terminal/models/terminal.py:106
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:16
|
||||
#: terminal/models/endpoint.py:20
|
||||
msgid "RDP Port"
|
||||
msgstr "RDP ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:17
|
||||
msgid "MySQL Port"
|
||||
msgstr "MySQL ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:18
|
||||
msgid "MariaDB Port"
|
||||
msgstr "MariaDB ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:19
|
||||
msgid "PostgreSQL Port"
|
||||
msgstr "PostgreSQL ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:20
|
||||
msgid "Redis Port"
|
||||
msgstr "Redis ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:21
|
||||
msgid "Oracle 11g Port"
|
||||
msgstr "Oracle 11g ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:22
|
||||
msgid "Oracle 12c Port"
|
||||
msgstr "Oracle 12c ポート"
|
||||
|
||||
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:94
|
||||
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
|
||||
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
|
||||
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
|
||||
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
|
||||
msgid "Endpoint"
|
||||
msgstr "エンドポイント"
|
||||
|
||||
#: terminal/models/endpoint.py:87
|
||||
#: terminal/models/endpoint.py:88
|
||||
msgid "IP group"
|
||||
msgstr "IP グループ"
|
||||
|
||||
#: terminal/models/endpoint.py:99
|
||||
#: terminal/models/endpoint.py:100
|
||||
msgid "Endpoint rule"
|
||||
msgstr "エンドポイントルール"
|
||||
|
||||
|
@ -5188,9 +5159,19 @@ msgstr "レベル"
|
|||
msgid "Batch danger command alert"
|
||||
msgstr "一括危険コマンド警告"
|
||||
|
||||
#: terminal/serializers/endpoint.py:12
|
||||
msgid "Oracle port"
|
||||
msgstr ""
|
||||
#: terminal/serializers/endpoint.py:14
|
||||
msgid "Magnus listen db port"
|
||||
msgstr "Magnus がリッスンするデータベース ポート"
|
||||
|
||||
#: terminal/serializers/endpoint.py:17
|
||||
msgid "Magnus Listen port range"
|
||||
msgstr "Magnus がリッスンするポート範囲"
|
||||
|
||||
#: terminal/serializers/endpoint.py:19
|
||||
msgid ""
|
||||
"The range of ports that Magnus listens on is modified in the configuration "
|
||||
"file"
|
||||
msgstr "Magnus がリッスンするポート範囲を構成ファイルで変更してください"
|
||||
|
||||
#: terminal/serializers/endpoint.py:51
|
||||
msgid ""
|
||||
|
@ -5321,6 +5302,16 @@ msgstr "見つかりません"
|
|||
msgid "view"
|
||||
msgstr "表示"
|
||||
|
||||
#: terminal/utils/db_port_mapper.py:77
|
||||
msgid ""
|
||||
"No ports can be used, check and modify the limit on the number of ports that "
|
||||
"Magnus listens on in the configuration file."
|
||||
msgstr "使用できるポートがありません。設定ファイルで Magnus がリッスンするポート数の制限を確認して変更してください. "
|
||||
|
||||
#: terminal/utils/db_port_mapper.py:79
|
||||
msgid "All available port count: {}, Already use port count: {}"
|
||||
msgstr "使用可能なすべてのポート数: {}、すでに使用しているポート数: {}"
|
||||
|
||||
#: tickets/apps.py:7
|
||||
msgid "Tickets"
|
||||
msgstr "チケット"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:618dce0f2521591410fb16b3d0afa536333d2e4f317d75d1955fe413dc0e64cb
|
||||
size 108949
|
||||
oid sha256:a346a8166af782cbc41eac33475b4cfac2e3713b26f84ddf6fa532742133b89d
|
||||
size 109216
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-09-15 15:52+0800\n"
|
||||
"POT-Creation-Date: 2022-09-22 19:01+0800\n"
|
||||
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||
|
@ -22,13 +22,13 @@ msgid "Acls"
|
|||
msgstr "访问控制"
|
||||
|
||||
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
|
||||
#: applications/models/application.py:220 assets/models/asset.py:138
|
||||
#: applications/models/application.py:219 assets/models/asset.py:138
|
||||
#: assets/models/base.py:175 assets/models/cluster.py:18
|
||||
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
|
||||
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
|
||||
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
|
||||
#: settings/models.py:33 settings/serializers/sms.py:6
|
||||
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
|
||||
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
|
||||
#: terminal/models/storage.py:26 terminal/models/task.py:16
|
||||
#: terminal/models/terminal.py:100 users/forms/profile.py:33
|
||||
#: users/models/group.py:15 users/models/user.py:669
|
||||
|
@ -37,12 +37,12 @@ msgid "Name"
|
|||
msgstr "名称"
|
||||
|
||||
#: acls/models/base.py:27 assets/models/cmd_filter.py:84
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:89
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:90
|
||||
msgid "Priority"
|
||||
msgstr "优先级"
|
||||
|
||||
#: acls/models/base.py:28 assets/models/cmd_filter.py:84
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:90
|
||||
#: assets/models/user.py:251 terminal/models/endpoint.py:91
|
||||
msgid "1-100, the lower the value will be match first"
|
||||
msgstr "优先级可选范围为 1-100 (数值越小越优先)"
|
||||
|
||||
|
@ -52,7 +52,7 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)"
|
|||
msgid "Active"
|
||||
msgstr "激活中"
|
||||
|
||||
#: acls/models/base.py:32 applications/models/application.py:233
|
||||
#: acls/models/base.py:32 applications/models/application.py:232
|
||||
#: assets/models/asset.py:143 assets/models/asset.py:231
|
||||
#: assets/models/backup.py:54 assets/models/base.py:180
|
||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:48
|
||||
|
@ -60,7 +60,7 @@ msgstr "激活中"
|
|||
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
|
||||
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
|
||||
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
|
||||
#: terminal/models/endpoint.py:22 terminal/models/endpoint.py:97
|
||||
#: terminal/models/storage.py:29 terminal/models/terminal.py:114
|
||||
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
|
||||
#: users/models/group.py:16 users/models/user.py:706
|
||||
|
@ -236,7 +236,7 @@ msgstr ""
|
|||
msgid "Time Period"
|
||||
msgstr "时段"
|
||||
|
||||
#: applications/apps.py:9 applications/models/application.py:64
|
||||
#: applications/apps.py:9 applications/models/application.py:62
|
||||
msgid "Applications"
|
||||
msgstr "应用管理"
|
||||
|
||||
|
@ -255,11 +255,7 @@ msgstr "远程应用"
|
|||
msgid "Custom"
|
||||
msgstr "自定义"
|
||||
|
||||
#: applications/const.py:91 rbac/tree.py:29
|
||||
msgid "Other"
|
||||
msgstr "其它"
|
||||
|
||||
#: applications/models/account.py:12 applications/models/application.py:237
|
||||
#: applications/models/account.py:12 applications/models/application.py:236
|
||||
#: assets/models/backup.py:32 assets/models/cmd_filter.py:45
|
||||
#: authentication/models.py:67 authentication/models.py:95
|
||||
#: perms/models/application_permission.py:28
|
||||
|
@ -277,9 +273,8 @@ msgstr "应用程序"
|
|||
msgid "System user"
|
||||
msgstr "系统用户"
|
||||
|
||||
#: applications/models/account.py:17
|
||||
#: applications/serializers/attrs/application_type/oracle.py:13
|
||||
#: assets/models/authbook.py:21 settings/serializers/auth/cas.py:18
|
||||
#: applications/models/account.py:17 assets/models/authbook.py:21
|
||||
#: settings/serializers/auth/cas.py:18
|
||||
msgid "Version"
|
||||
msgstr "版本"
|
||||
|
||||
|
@ -295,7 +290,7 @@ msgstr "可以查看应用账号密码"
|
|||
msgid "Can change application account secret"
|
||||
msgstr "可以查看应用账号密码"
|
||||
|
||||
#: applications/models/application.py:222
|
||||
#: applications/models/application.py:221
|
||||
#: applications/serializers/application.py:101 assets/models/label.py:21
|
||||
#: perms/models/application_permission.py:21
|
||||
#: perms/serializers/application/user_permission.py:33
|
||||
|
@ -304,7 +299,7 @@ msgstr "可以查看应用账号密码"
|
|||
msgid "Category"
|
||||
msgstr "类别"
|
||||
|
||||
#: applications/models/application.py:225
|
||||
#: applications/models/application.py:224
|
||||
#: applications/serializers/application.py:103 assets/models/backup.py:49
|
||||
#: assets/models/cmd_filter.py:82 assets/models/user.py:250
|
||||
#: authentication/models.py:70 perms/models/application_permission.py:24
|
||||
|
@ -318,21 +313,21 @@ msgstr "类别"
|
|||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
||||
#: applications/models/application.py:229 assets/models/asset.py:217
|
||||
#: applications/models/application.py:228 assets/models/asset.py:217
|
||||
#: assets/models/domain.py:29 assets/models/domain.py:64
|
||||
msgid "Domain"
|
||||
msgstr "网域"
|
||||
|
||||
#: applications/models/application.py:231 xpack/plugins/cloud/models.py:33
|
||||
#: applications/models/application.py:230 xpack/plugins/cloud/models.py:33
|
||||
#: xpack/plugins/cloud/serializers/account.py:63
|
||||
msgid "Attrs"
|
||||
msgstr "属性"
|
||||
|
||||
#: applications/models/application.py:241
|
||||
#: applications/models/application.py:240
|
||||
msgid "Can match application"
|
||||
msgstr "匹配应用"
|
||||
|
||||
#: applications/models/application.py:320
|
||||
#: applications/models/application.py:310
|
||||
msgid "Application user"
|
||||
msgstr "应用用户"
|
||||
|
||||
|
@ -388,7 +383,7 @@ msgstr "集群"
|
|||
|
||||
#: applications/serializers/attrs/application_category/db.py:11
|
||||
#: ops/models/adhoc.py:157 settings/serializers/auth/radius.py:14
|
||||
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:11
|
||||
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:15
|
||||
#: xpack/plugins/cloud/serializers/account_attrs.py:72
|
||||
msgid "Host"
|
||||
msgstr "主机"
|
||||
|
@ -397,7 +392,7 @@ msgstr "主机"
|
|||
#: applications/serializers/attrs/application_type/mongodb.py:10
|
||||
#: applications/serializers/attrs/application_type/mysql.py:10
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:22
|
||||
#: applications/serializers/attrs/application_type/oracle.py:16
|
||||
#: applications/serializers/attrs/application_type/oracle.py:10
|
||||
#: applications/serializers/attrs/application_type/pgsql.py:10
|
||||
#: applications/serializers/attrs/application_type/redis.py:10
|
||||
#: applications/serializers/attrs/application_type/sqlserver.py:10
|
||||
|
@ -492,10 +487,6 @@ msgstr "Mysql 工作台 用户名"
|
|||
msgid "Mysql workbench password"
|
||||
msgstr "Mysql 工作台 密码"
|
||||
|
||||
#: applications/serializers/attrs/application_type/oracle.py:14
|
||||
msgid "Magnus currently supports only 11g and 12c connections"
|
||||
msgstr "目前 Magnus 只支持连接 11g、12c 版本"
|
||||
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:26
|
||||
msgid "Vmware username"
|
||||
msgstr "Vmware 用户名"
|
||||
|
@ -3414,6 +3405,10 @@ msgstr "审计台"
|
|||
msgid "System setting"
|
||||
msgstr "系统设置"
|
||||
|
||||
#: rbac/tree.py:29
|
||||
msgid "Other"
|
||||
msgstr "其它"
|
||||
|
||||
#: rbac/tree.py:37
|
||||
msgid "Accounts"
|
||||
msgstr "账号管理"
|
||||
|
@ -4879,58 +4874,34 @@ msgstr "存储无效"
|
|||
msgid "Command record"
|
||||
msgstr "命令记录"
|
||||
|
||||
#: terminal/models/endpoint.py:13
|
||||
#: terminal/models/endpoint.py:17
|
||||
msgid "HTTPS Port"
|
||||
msgstr "HTTPS 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:14 terminal/models/terminal.py:107
|
||||
#: terminal/models/endpoint.py:18 terminal/models/terminal.py:107
|
||||
msgid "HTTP Port"
|
||||
msgstr "HTTP 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:15 terminal/models/terminal.py:106
|
||||
#: terminal/models/endpoint.py:19 terminal/models/terminal.py:106
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:16
|
||||
#: terminal/models/endpoint.py:20
|
||||
msgid "RDP Port"
|
||||
msgstr "RDP 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:17
|
||||
msgid "MySQL Port"
|
||||
msgstr "MySQL 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:18
|
||||
msgid "MariaDB Port"
|
||||
msgstr "MariaDB 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:19
|
||||
msgid "PostgreSQL Port"
|
||||
msgstr "PostgreSQL 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:20
|
||||
msgid "Redis Port"
|
||||
msgstr "Redis 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:21
|
||||
msgid "Oracle 11g Port"
|
||||
msgstr "Oracle 11g 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:22
|
||||
msgid "Oracle 12c Port"
|
||||
msgstr "Oracle 12c 端口"
|
||||
|
||||
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:94
|
||||
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
|
||||
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
|
||||
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
|
||||
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
|
||||
msgid "Endpoint"
|
||||
msgstr "端点"
|
||||
|
||||
#: terminal/models/endpoint.py:87
|
||||
#: terminal/models/endpoint.py:88
|
||||
msgid "IP group"
|
||||
msgstr "IP 组"
|
||||
|
||||
#: terminal/models/endpoint.py:99
|
||||
#: terminal/models/endpoint.py:100
|
||||
msgid "Endpoint rule"
|
||||
msgstr "端点规则"
|
||||
|
||||
|
@ -5111,9 +5082,19 @@ msgstr "级别"
|
|||
msgid "Batch danger command alert"
|
||||
msgstr "批量危险命令告警"
|
||||
|
||||
#: terminal/serializers/endpoint.py:12
|
||||
msgid "Oracle port"
|
||||
msgstr ""
|
||||
#: terminal/serializers/endpoint.py:14
|
||||
msgid "Magnus listen db port"
|
||||
msgstr "Magnus 监听的数据库端口"
|
||||
|
||||
#: terminal/serializers/endpoint.py:17
|
||||
msgid "Magnus Listen port range"
|
||||
msgstr "Magnus 监听的端口范围"
|
||||
|
||||
#: terminal/serializers/endpoint.py:19
|
||||
msgid ""
|
||||
"The range of ports that Magnus listens on is modified in the configuration "
|
||||
"file"
|
||||
msgstr "请在配置文件中修改 Magnus 监听的端口范围"
|
||||
|
||||
#: terminal/serializers/endpoint.py:51
|
||||
msgid ""
|
||||
|
@ -5242,6 +5223,16 @@ msgstr "没有发现"
|
|||
msgid "view"
|
||||
msgstr "查看"
|
||||
|
||||
#: terminal/utils/db_port_mapper.py:77
|
||||
msgid ""
|
||||
"No ports can be used, check and modify the limit on the number of ports that "
|
||||
"Magnus listens on in the configuration file."
|
||||
msgstr "没有端口可以使用,检查并修改配置文件中 Magnus 监听的端口数量限制。"
|
||||
|
||||
#: terminal/utils/db_port_mapper.py:79
|
||||
msgid "All available port count: {}, Already use port count: {}"
|
||||
msgstr "所有可用端口数量:{},已使用端口数量:{}"
|
||||
|
||||
#: tickets/apps.py:7
|
||||
msgid "Tickets"
|
||||
msgstr "工单管理"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% load i18n %}
|
||||
<head>
|
||||
<title>{% trans 'Task log' %}</title>
|
||||
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
|
||||
<script src="{% static 'js/jquery-3.6.1.min.js' %}"></script>
|
||||
<script src="{% static 'js/plugins/xterm/xterm.js' %}"></script>
|
||||
<script src="{% static 'js/plugins/xterm/addons/fit/fit.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'js/plugins/xterm/xterm.css' %}" />
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -9,7 +9,7 @@
|
|||
<link href="{% static 'css/plugins/vaildator/jquery.validator.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/plugins/datatables/datatables.min.css' %}" rel="stylesheet">
|
||||
<!-- scripts -->
|
||||
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
|
||||
<script src="{% static 'js/jquery-3.6.1.min.js' %}"></script>
|
||||
<script src="{% static 'js/plugins/sweetalert/sweetalert.min.js' %}"></script>
|
||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'js/plugins/datatables/datatables.min.js' %}"></script>
|
||||
|
|
|
@ -8,3 +8,4 @@ from .storage import *
|
|||
from .status import *
|
||||
from .sharing import *
|
||||
from .endpoint import *
|
||||
from .db_listen_port import *
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from ..utils import db_port_manager, DBPortManager
|
||||
from applications import serializers
|
||||
|
||||
|
||||
db_port_manager: DBPortManager
|
||||
|
||||
|
||||
__all__ = ['DBListenPortViewSet']
|
||||
|
||||
|
||||
class DBListenPortViewSet(GenericViewSet):
|
||||
rbac_perms = {
|
||||
'GET': 'applications.view_application',
|
||||
'list': 'applications.view_application',
|
||||
'db_info': 'applications.view_application',
|
||||
}
|
||||
|
||||
http_method_names = ['get', 'post']
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
ports = db_port_manager.get_already_use_ports()
|
||||
return Response(data=ports, status=status.HTTP_200_OK)
|
||||
|
||||
@action(methods=['get'], detail=False, url_path='db-info')
|
||||
def db_info(self, request, *args, **kwargs):
|
||||
port = request.query_params.get("port")
|
||||
db = db_port_manager.get_db_by_port(port)
|
||||
serializer = serializers.AppSerializer(instance=db)
|
||||
return Response(data=serializer.data, status=status.HTTP_200_OK)
|
|
@ -47,11 +47,12 @@ class SmartEndpointViewMixin:
|
|||
return Endpoint.match_by_instance_label(self.target_instance, self.target_protocol)
|
||||
|
||||
def match_endpoint_by_target_ip(self):
|
||||
# 用来方便测试
|
||||
target_ip = self.request.GET.get('target_ip', '')
|
||||
target_ip = self.request.GET.get('target_ip', '') # 支持target_ip参数,用来方便测试
|
||||
if not target_ip and callable(getattr(self.target_instance, 'get_target_ip', None)):
|
||||
target_ip = self.target_instance.get_target_ip()
|
||||
endpoint = EndpointRule.match_endpoint(target_ip, self.target_protocol, self.request)
|
||||
endpoint = EndpointRule.match_endpoint(
|
||||
self.target_instance, target_ip, self.target_protocol, self.request
|
||||
)
|
||||
return endpoint
|
||||
|
||||
def get_target_instance(self):
|
||||
|
@ -83,12 +84,7 @@ class SmartEndpointViewMixin:
|
|||
return instance
|
||||
|
||||
def get_target_protocol(self):
|
||||
protocol = None
|
||||
if isinstance(self.target_instance, Application) and self.target_instance.is_type(Application.APP_TYPE.oracle):
|
||||
protocol = self.target_instance.get_target_protocol_for_oracle()
|
||||
if not protocol:
|
||||
protocol = self.request.GET.get('protocol')
|
||||
return protocol
|
||||
return self.request.GET.get('protocol')
|
||||
|
||||
|
||||
class EndpointViewSet(SmartEndpointViewMixin, JMSBulkModelViewSet):
|
||||
|
|
|
@ -169,6 +169,14 @@ class CommandStore(object):
|
|||
return self.es.index(index=self.index, doc_type=self.doc_type, body=data)
|
||||
|
||||
def filter(self, query: dict, from_=None, size=None, sort=None):
|
||||
try:
|
||||
data = self._filter(query, from_, size, sort)
|
||||
except Exception as e:
|
||||
logger.error('ES filter error: {}'.format(e))
|
||||
data = []
|
||||
return data
|
||||
|
||||
def _filter(self, query: dict, from_=None, size=None, sort=None):
|
||||
body = self.get_query_body(**query)
|
||||
|
||||
data = self.es.search(
|
||||
|
@ -184,9 +192,14 @@ class CommandStore(object):
|
|||
return Command.from_multi_dict(source_data)
|
||||
|
||||
def count(self, **query):
|
||||
try:
|
||||
body = self.get_query_body(**query)
|
||||
data = self.es.count(index=self.query_index, doc_type=self.doc_type, body=body)
|
||||
return data["count"]
|
||||
count = data["count"]
|
||||
except Exception as e:
|
||||
logger.error('ES count error: {}'.format(e))
|
||||
count = 0
|
||||
return count
|
||||
|
||||
def __getattr__(self, item):
|
||||
return getattr(self.es, item)
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 3.2.15 on 2022-10-09 09:55
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('terminal', '0052_auto_20220713_1417'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='endpoint',
|
||||
name='mariadb_port',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='endpoint',
|
||||
name='mysql_port',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='endpoint',
|
||||
name='oracle_11g_port',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='endpoint',
|
||||
name='oracle_12c_port',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='endpoint',
|
||||
name='postgresql_port',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='endpoint',
|
||||
name='redis_port',
|
||||
),
|
||||
]
|
|
@ -1,25 +1,24 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
from applications.models import Application
|
||||
from ..utils import db_port_manager, DBPortManager
|
||||
from common.db.models import JMSModel
|
||||
from common.db.fields import PortField
|
||||
from common.utils.ip import contains_ip
|
||||
|
||||
db_port_manager: DBPortManager
|
||||
|
||||
|
||||
class Endpoint(JMSModel):
|
||||
name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True)
|
||||
host = models.CharField(max_length=256, blank=True, verbose_name=_('Host'))
|
||||
# disabled value=0
|
||||
# value=0 表示 disabled
|
||||
https_port = PortField(default=443, verbose_name=_('HTTPS Port'))
|
||||
http_port = PortField(default=80, verbose_name=_('HTTP Port'))
|
||||
ssh_port = PortField(default=2222, verbose_name=_('SSH Port'))
|
||||
rdp_port = PortField(default=3389, verbose_name=_('RDP Port'))
|
||||
mysql_port = PortField(default=33060, verbose_name=_('MySQL Port'))
|
||||
mariadb_port = PortField(default=33061, verbose_name=_('MariaDB Port'))
|
||||
postgresql_port = PortField(default=54320, verbose_name=_('PostgreSQL Port'))
|
||||
redis_port = PortField(default=63790, verbose_name=_('Redis Port'))
|
||||
oracle_11g_port = PortField(default=15211, verbose_name=_('Oracle 11g Port'))
|
||||
oracle_12c_port = PortField(default=15212, verbose_name=_('Oracle 12c Port'))
|
||||
|
||||
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
|
||||
|
||||
default_id = '00000000-0000-0000-0000-000000000001'
|
||||
|
@ -31,12 +30,14 @@ class Endpoint(JMSModel):
|
|||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_port(self, protocol):
|
||||
return getattr(self, f'{protocol}_port', 0)
|
||||
|
||||
def get_oracle_port(self, version):
|
||||
protocol = f'oracle_{version}'
|
||||
return self.get_port(protocol)
|
||||
def get_port(self, target_instance, protocol):
|
||||
if protocol in ['https', 'http', 'ssh', 'rdp']:
|
||||
port = getattr(self, f'{protocol}_port', 0)
|
||||
elif isinstance(target_instance, Application) and target_instance.category_db:
|
||||
port = db_port_manager.get_port_by_db(target_instance)
|
||||
else:
|
||||
port = 0
|
||||
return port
|
||||
|
||||
def is_default(self):
|
||||
return str(self.id) == self.default_id
|
||||
|
@ -46,10 +47,10 @@ class Endpoint(JMSModel):
|
|||
return
|
||||
return super().delete(using, keep_parents)
|
||||
|
||||
def is_valid_for(self, protocol):
|
||||
def is_valid_for(self, target_instance, protocol):
|
||||
if self.is_default():
|
||||
return True
|
||||
if self.host and self.get_port(protocol) != 0:
|
||||
if self.host and self.get_port(target_instance, protocol) != 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -103,19 +104,19 @@ class EndpointRule(JMSModel):
|
|||
return f'{self.name}({self.priority})'
|
||||
|
||||
@classmethod
|
||||
def match(cls, target_ip, protocol):
|
||||
def match(cls, target_instance, target_ip, protocol):
|
||||
for endpoint_rule in cls.objects.all().prefetch_related('endpoint'):
|
||||
if not contains_ip(target_ip, endpoint_rule.ip_group):
|
||||
continue
|
||||
if not endpoint_rule.endpoint:
|
||||
continue
|
||||
if not endpoint_rule.endpoint.is_valid_for(protocol):
|
||||
if not endpoint_rule.endpoint.is_valid_for(target_instance, protocol):
|
||||
continue
|
||||
return endpoint_rule
|
||||
|
||||
@classmethod
|
||||
def match_endpoint(cls, target_ip, protocol, request=None):
|
||||
endpoint_rule = cls.match(target_ip, protocol)
|
||||
def match_endpoint(cls, target_instance, target_ip, protocol, request=None):
|
||||
endpoint_rule = cls.match(target_instance, target_ip, protocol)
|
||||
if endpoint_rule:
|
||||
endpoint = endpoint_rule.endpoint
|
||||
else:
|
||||
|
|
|
@ -2,25 +2,31 @@ from rest_framework import serializers
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from common.drf.serializers import BulkModelSerializer
|
||||
from acls.serializers.rules import ip_group_child_validator, ip_group_help_text
|
||||
from ..utils import db_port_manager
|
||||
from ..models import Endpoint, EndpointRule
|
||||
|
||||
|
||||
__all__ = ['EndpointSerializer', 'EndpointRuleSerializer']
|
||||
|
||||
|
||||
class EndpointSerializer(BulkModelSerializer):
|
||||
# 解决 luna 处理繁琐的问题,oracle_port 返回匹配到的端口
|
||||
oracle_port = serializers.SerializerMethodField(label=_('Oracle port'))
|
||||
# 解决 luna 处理繁琐的问题, 返回 magnus 监听的当前 db 的 port
|
||||
magnus_listen_db_port = serializers.SerializerMethodField(label=_('Magnus listen db port'))
|
||||
magnus_listen_port_range = serializers.CharField(
|
||||
max_length=128, default=db_port_manager.magnus_listen_port_range, read_only=True,
|
||||
label=_('Magnus Listen port range'),
|
||||
help_text=_(
|
||||
'The range of ports that Magnus listens on is modified in the configuration file'
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Endpoint
|
||||
fields_mini = ['id', 'name']
|
||||
fields_small = [
|
||||
'host',
|
||||
'https_port', 'http_port', 'ssh_port',
|
||||
'rdp_port', 'mysql_port', 'mariadb_port',
|
||||
'postgresql_port', 'redis_port',
|
||||
'oracle_11g_port', 'oracle_12c_port',
|
||||
'oracle_port',
|
||||
'https_port', 'http_port', 'ssh_port', 'rdp_port',
|
||||
'magnus_listen_db_port', 'magnus_listen_port_range',
|
||||
]
|
||||
fields = fields_mini + fields_small + [
|
||||
'comment', 'date_created', 'date_updated', 'created_by'
|
||||
|
@ -30,19 +36,13 @@ class EndpointSerializer(BulkModelSerializer):
|
|||
'http_port': {'default': 80},
|
||||
'ssh_port': {'default': 2222},
|
||||
'rdp_port': {'default': 3389},
|
||||
'mysql_port': {'default': 33060},
|
||||
'mariadb_port': {'default': 33061},
|
||||
'postgresql_port': {'default': 54320},
|
||||
'redis_port': {'default': 63790},
|
||||
'oracle_11g_port': {'default': 15211},
|
||||
'oracle_12c_port': {'default': 15212},
|
||||
}
|
||||
|
||||
def get_oracle_port(self, obj: Endpoint):
|
||||
def get_magnus_listen_db_port(self, obj: Endpoint):
|
||||
view = self.context.get('view')
|
||||
if not view or view.action not in ['smart']:
|
||||
return 0
|
||||
return obj.get_port(view.target_protocol)
|
||||
return obj.get_port(view.target_instance, view.target_protocol)
|
||||
|
||||
|
||||
class EndpointRuleSerializer(BulkModelSerializer):
|
||||
|
|
|
@ -1,2 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
|
||||
from common.signals import django_ready
|
||||
from django.dispatch import receiver
|
||||
from common.utils import get_logger
|
||||
from .models import Application
|
||||
from .utils import db_port_manager, DBPortManager
|
||||
|
||||
db_port_manager: DBPortManager
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@receiver(django_ready)
|
||||
def init_db_port_mapper(sender, **kwargs):
|
||||
logger.info('Init db port mapper')
|
||||
db_port_manager.init()
|
||||
|
||||
|
||||
@receiver(post_save, sender=Application)
|
||||
def on_db_app_created(sender, instance: Application, created, **kwargs):
|
||||
if not instance.category_db:
|
||||
return
|
||||
if not created:
|
||||
return
|
||||
db_port_manager.add(instance)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Application)
|
||||
def on_db_app_delete(sender, instance, **kwargs):
|
||||
if not instance.category_db:
|
||||
return
|
||||
db_port_manager.pop(instance)
|
||||
|
|
|
@ -24,6 +24,7 @@ router.register(r'session-sharings', api.SessionSharingViewSet, 'session-sharing
|
|||
router.register(r'session-join-records', api.SessionJoinRecordsViewSet, 'session-sharing-record')
|
||||
router.register(r'endpoints', api.EndpointViewSet, 'endpoint')
|
||||
router.register(r'endpoint-rules', api.EndpointRuleViewSet, 'endpoint-rule')
|
||||
router.register(r'db-listen-ports', api.DBListenPortViewSet, 'db-listen-ports')
|
||||
|
||||
urlpatterns = [
|
||||
path('my-sessions/', api.MySessionAPIView.as_view(), name='my-session'),
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
from .components import *
|
||||
from .common import *
|
||||
from .session_replay import *
|
||||
from .db_port_mapper import *
|
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from common.utils import get_logger
|
||||
from .. import const
|
||||
from tickets.models import TicketSession
|
||||
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class ComputeStatUtil:
|
||||
# system status
|
||||
@staticmethod
|
||||
def _common_compute_system_status(value, thresholds):
|
||||
if thresholds[0] <= value <= thresholds[1]:
|
||||
return const.ComponentStatusChoices.normal.value
|
||||
elif thresholds[1] < value <= thresholds[2]:
|
||||
return const.ComponentStatusChoices.high.value
|
||||
else:
|
||||
return const.ComponentStatusChoices.critical.value
|
||||
|
||||
@classmethod
|
||||
def _compute_system_stat_status(cls, stat):
|
||||
system_stat_thresholds_mapper = {
|
||||
'cpu_load': [0, 5, 20],
|
||||
'memory_used': [0, 85, 95],
|
||||
'disk_used': [0, 80, 99]
|
||||
}
|
||||
system_status = {}
|
||||
for stat_key, thresholds in system_stat_thresholds_mapper.items():
|
||||
stat_value = getattr(stat, stat_key)
|
||||
if stat_value is None:
|
||||
msg = 'stat: {}, stat_key: {}, stat_value: {}'
|
||||
logger.debug(msg.format(stat, stat_key, stat_value))
|
||||
stat_value = 0
|
||||
status = cls._common_compute_system_status(stat_value, thresholds)
|
||||
system_status[stat_key] = status
|
||||
return system_status
|
||||
|
||||
@classmethod
|
||||
def compute_component_status(cls, stat):
|
||||
if not stat:
|
||||
return const.ComponentStatusChoices.offline
|
||||
system_status_values = cls._compute_system_stat_status(stat).values()
|
||||
if const.ComponentStatusChoices.critical in system_status_values:
|
||||
return const.ComponentStatusChoices.critical
|
||||
elif const.ComponentStatusChoices.high in system_status_values:
|
||||
return const.ComponentStatusChoices.high
|
||||
else:
|
||||
return const.ComponentStatusChoices.normal
|
||||
|
||||
|
||||
def is_session_approver(session_id, user_id):
|
||||
ticket = TicketSession.get_ticket_by_session_id(session_id)
|
||||
if not ticket:
|
||||
return False
|
||||
ok = ticket.has_all_assignee(user_id)
|
||||
return ok
|
|
@ -1,124 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import os
|
||||
from itertools import groupby, chain
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import default_storage
|
||||
|
||||
import jms_storage
|
||||
from itertools import groupby
|
||||
|
||||
from common.utils import get_logger
|
||||
from . import const
|
||||
from .models import ReplayStorage
|
||||
from tickets.models import TicketSession, TicketStep, TicketAssignee
|
||||
from tickets.const import StepState
|
||||
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def find_session_replay_local(session):
|
||||
# 存在外部存储上,所有可能的路径名
|
||||
session_paths = session.get_all_possible_relative_path()
|
||||
|
||||
# 存在本地存储上,所有可能的路径名
|
||||
local_paths = session.get_all_possible_local_path()
|
||||
|
||||
for _local_path in chain(session_paths, local_paths):
|
||||
if default_storage.exists(_local_path):
|
||||
url = default_storage.url(_local_path)
|
||||
return _local_path, url
|
||||
return None, None
|
||||
|
||||
|
||||
def download_session_replay(session):
|
||||
replay_storages = ReplayStorage.objects.all()
|
||||
configs = {
|
||||
storage.name: storage.config
|
||||
for storage in replay_storages
|
||||
if not storage.type_null_or_server
|
||||
}
|
||||
if settings.SERVER_REPLAY_STORAGE:
|
||||
configs['SERVER_REPLAY_STORAGE'] = settings.SERVER_REPLAY_STORAGE
|
||||
if not configs:
|
||||
msg = "Not found replay file, and not remote storage set"
|
||||
return None, msg
|
||||
storage = jms_storage.get_multi_object_storage(configs)
|
||||
|
||||
# 获取外部存储路径名
|
||||
session_path = session.find_ok_relative_path_in_storage(storage)
|
||||
if not session_path:
|
||||
msg = "Not found session replay file"
|
||||
return None, msg
|
||||
|
||||
# 通过外部存储路径名后缀,构造真实的本地存储路径
|
||||
local_path = session.get_local_path_by_relative_path(session_path)
|
||||
|
||||
# 保存到storage的路径
|
||||
target_path = os.path.join(default_storage.base_location, local_path)
|
||||
target_dir = os.path.dirname(target_path)
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
|
||||
ok, err = storage.download(session_path, target_path)
|
||||
if not ok:
|
||||
msg = "Failed download replay file: {}".format(err)
|
||||
logger.error(msg)
|
||||
return None, msg
|
||||
url = default_storage.url(local_path)
|
||||
return local_path, url
|
||||
|
||||
|
||||
def get_session_replay_url(session):
|
||||
local_path, url = find_session_replay_local(session)
|
||||
if local_path is None:
|
||||
local_path, url = download_session_replay(session)
|
||||
return local_path, url
|
||||
|
||||
|
||||
class ComputeStatUtil:
|
||||
# system status
|
||||
@staticmethod
|
||||
def _common_compute_system_status(value, thresholds):
|
||||
if thresholds[0] <= value <= thresholds[1]:
|
||||
return const.ComponentStatusChoices.normal.value
|
||||
elif thresholds[1] < value <= thresholds[2]:
|
||||
return const.ComponentStatusChoices.high.value
|
||||
else:
|
||||
return const.ComponentStatusChoices.critical.value
|
||||
|
||||
@classmethod
|
||||
def _compute_system_stat_status(cls, stat):
|
||||
system_stat_thresholds_mapper = {
|
||||
'cpu_load': [0, 5, 20],
|
||||
'memory_used': [0, 85, 95],
|
||||
'disk_used': [0, 80, 99]
|
||||
}
|
||||
system_status = {}
|
||||
for stat_key, thresholds in system_stat_thresholds_mapper.items():
|
||||
stat_value = getattr(stat, stat_key)
|
||||
if stat_value is None:
|
||||
msg = 'stat: {}, stat_key: {}, stat_value: {}'
|
||||
logger.debug(msg.format(stat, stat_key, stat_value))
|
||||
stat_value = 0
|
||||
status = cls._common_compute_system_status(stat_value, thresholds)
|
||||
system_status[stat_key] = status
|
||||
return system_status
|
||||
|
||||
@classmethod
|
||||
def compute_component_status(cls, stat):
|
||||
if not stat:
|
||||
return const.ComponentStatusChoices.offline
|
||||
system_status_values = cls._compute_system_stat_status(stat).values()
|
||||
if const.ComponentStatusChoices.critical in system_status_values:
|
||||
return const.ComponentStatusChoices.critical
|
||||
elif const.ComponentStatusChoices.high in system_status_values:
|
||||
return const.ComponentStatusChoices.high
|
||||
else:
|
||||
return const.ComponentStatusChoices.normal
|
||||
|
||||
|
||||
class TypedComponentsStatusMetricsUtil(object):
|
||||
def __init__(self):
|
||||
self.components = []
|
||||
|
@ -126,7 +15,7 @@ class TypedComponentsStatusMetricsUtil(object):
|
|||
self.get_components()
|
||||
|
||||
def get_components(self):
|
||||
from .models import Terminal
|
||||
from ..models import Terminal
|
||||
components = Terminal.objects.filter(is_deleted=False).order_by('type')
|
||||
grouped_components = groupby(components, lambda c: c.type)
|
||||
grouped_components = [(i[0], list(i[1])) for i in grouped_components]
|
||||
|
@ -251,10 +140,3 @@ class ComponentsPrometheusMetricsUtil(TypedComponentsStatusMetricsUtil):
|
|||
prometheus_metrics_text = '\n'.join(prometheus_metrics)
|
||||
return prometheus_metrics_text
|
||||
|
||||
|
||||
def is_session_approver(session_id, user_id):
|
||||
ticket = TicketSession.get_ticket_by_session_id(session_id)
|
||||
if not ticket:
|
||||
return False
|
||||
ok = ticket.has_all_assignee(user_id)
|
||||
return ok
|
|
@ -0,0 +1,112 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.cache import cache
|
||||
from django.conf import settings
|
||||
|
||||
from common.decorator import Singleton
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from common.exceptions import JMSException
|
||||
from applications.const import AppCategory
|
||||
from applications.models import Application
|
||||
from orgs.utils import tmp_to_root_org
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@Singleton
|
||||
class DBPortManager(object):
|
||||
""" 管理端口-数据库ID的映射, Magnus 要使用 """
|
||||
|
||||
CACHE_KEY = 'PORT_DB_MAPPER'
|
||||
|
||||
def __init__(self):
|
||||
self.port_start = settings.MAGNUS_DB_PORTS_START
|
||||
self.port_limit = settings.MAGNUS_DB_PORTS_LIMIT_COUNT
|
||||
self.port_end = self.port_start + self.port_limit
|
||||
# 可以使用的端口列表
|
||||
self.all_available_ports = list(range(self.port_start, self.port_end))
|
||||
|
||||
@property
|
||||
def magnus_listen_port_range(self):
|
||||
return f'{self.port_start}-{self.port_end - 1}'
|
||||
|
||||
def init(self):
|
||||
with tmp_to_root_org():
|
||||
db_ids = Application.objects.filter(category=AppCategory.db).values_list('id', flat=True)
|
||||
db_ids = [str(i) for i in db_ids]
|
||||
mapper = dict(zip(self.all_available_ports, list(db_ids)))
|
||||
self.set_mapper(mapper)
|
||||
|
||||
def add(self, db: Application):
|
||||
mapper = self.get_mapper()
|
||||
available_port = self.get_next_available_port()
|
||||
mapper.update({available_port: str(db.id)})
|
||||
self.set_mapper(mapper)
|
||||
return True
|
||||
|
||||
def pop(self, db: Application):
|
||||
mapper = self.get_mapper()
|
||||
to_delete_port = self.get_port_by_db(db)
|
||||
mapper.pop(to_delete_port, None)
|
||||
self.set_mapper(mapper)
|
||||
|
||||
def get_port_by_db(self, db):
|
||||
mapper = self.get_mapper()
|
||||
for port, db_id in mapper.items():
|
||||
if db_id == str(db.id):
|
||||
return port
|
||||
raise JMSException(
|
||||
'Not matched db port, db id: {}, mapper length: {}'.format(db.id, len(mapper))
|
||||
)
|
||||
|
||||
def get_db_by_port(self, port):
|
||||
try:
|
||||
port = int(port)
|
||||
except Exception as e:
|
||||
raise JMSException('Port type error: {}'.format(e))
|
||||
mapper = self.get_mapper()
|
||||
db_id = mapper.get(port, None)
|
||||
if not db_id:
|
||||
raise JMSException('Database not in port-db mapper, port: {}'.format(port))
|
||||
with tmp_to_root_org():
|
||||
db = get_object_or_none(Application, id=db_id)
|
||||
if not db:
|
||||
raise JMSException('Database not exists, db id: {}'.format(db_id))
|
||||
return db
|
||||
|
||||
def get_next_available_port(self):
|
||||
already_use_ports = self.get_already_use_ports()
|
||||
available_ports = sorted(list(set(self.all_available_ports) - set(already_use_ports)))
|
||||
if len(available_ports) <= 0:
|
||||
msg = _('No ports can be used, check and modify the limit on the number '
|
||||
'of ports that Magnus listens on in the configuration file.')
|
||||
tips = _('All available port count: {}, Already use port count: {}').format(
|
||||
len(self.all_available_ports), len(already_use_ports)
|
||||
)
|
||||
error = msg + tips
|
||||
raise JMSException(error)
|
||||
port = available_ports[0]
|
||||
logger.debug('Get next available port: {}'.format(port))
|
||||
return port
|
||||
|
||||
def get_already_use_ports(self):
|
||||
mapper = self.get_mapper()
|
||||
return sorted(list(mapper.keys()))
|
||||
|
||||
def get_mapper(self):
|
||||
mapper = cache.get(self.CACHE_KEY, {})
|
||||
if not mapper:
|
||||
# redis 可能被清空,重新初始化一下
|
||||
self.init()
|
||||
return cache.get(self.CACHE_KEY, {})
|
||||
|
||||
def set_mapper(self, value):
|
||||
"""
|
||||
value: {
|
||||
port: db_id
|
||||
}
|
||||
"""
|
||||
cache.set(self.CACHE_KEY, value, timeout=None)
|
||||
|
||||
|
||||
db_port_manager = DBPortManager()
|
|
@ -0,0 +1,75 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import os
|
||||
from itertools import groupby, chain
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import default_storage
|
||||
|
||||
import jms_storage
|
||||
|
||||
from common.utils import get_logger
|
||||
from ..models import ReplayStorage
|
||||
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def find_session_replay_local(session):
|
||||
# 存在外部存储上,所有可能的路径名
|
||||
session_paths = session.get_all_possible_relative_path()
|
||||
|
||||
# 存在本地存储上,所有可能的路径名
|
||||
local_paths = session.get_all_possible_local_path()
|
||||
|
||||
for _local_path in chain(session_paths, local_paths):
|
||||
if default_storage.exists(_local_path):
|
||||
url = default_storage.url(_local_path)
|
||||
return _local_path, url
|
||||
return None, None
|
||||
|
||||
|
||||
def download_session_replay(session):
|
||||
replay_storages = ReplayStorage.objects.all()
|
||||
configs = {
|
||||
storage.name: storage.config
|
||||
for storage in replay_storages
|
||||
if not storage.type_null_or_server
|
||||
}
|
||||
if settings.SERVER_REPLAY_STORAGE:
|
||||
configs['SERVER_REPLAY_STORAGE'] = settings.SERVER_REPLAY_STORAGE
|
||||
if not configs:
|
||||
msg = "Not found replay file, and not remote storage set"
|
||||
return None, msg
|
||||
storage = jms_storage.get_multi_object_storage(configs)
|
||||
|
||||
# 获取外部存储路径名
|
||||
session_path = session.find_ok_relative_path_in_storage(storage)
|
||||
if not session_path:
|
||||
msg = "Not found session replay file"
|
||||
return None, msg
|
||||
|
||||
# 通过外部存储路径名后缀,构造真实的本地存储路径
|
||||
local_path = session.get_local_path_by_relative_path(session_path)
|
||||
|
||||
# 保存到storage的路径
|
||||
target_path = os.path.join(default_storage.base_location, local_path)
|
||||
target_dir = os.path.dirname(target_path)
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
|
||||
ok, err = storage.download(session_path, target_path)
|
||||
if not ok:
|
||||
msg = "Failed download replay file: {}".format(err)
|
||||
logger.error(msg)
|
||||
return None, msg
|
||||
url = default_storage.url(local_path)
|
||||
return local_path, url
|
||||
|
||||
|
||||
def get_session_replay_url(session):
|
||||
local_path, url = find_session_replay_local(session)
|
||||
if local_path is None:
|
||||
local_path, url = download_session_replay(session)
|
||||
return local_path, url
|
||||
|
|
@ -19,7 +19,7 @@ class ApplyApplicationSerializer(BaseApplyAssetApplicationSerializer, TicketAppl
|
|||
class Meta:
|
||||
model = ApplyApplicationTicket
|
||||
writeable_fields = [
|
||||
'id', 'title', 'type', 'apply_category',
|
||||
'id', 'title', 'type', 'apply_category', 'comment',
|
||||
'apply_type', 'apply_applications', 'apply_system_users',
|
||||
'apply_actions', 'apply_date_start', 'apply_date_expired', 'org_id'
|
||||
]
|
||||
|
|
|
@ -23,7 +23,7 @@ class ApplyAssetSerializer(BaseApplyAssetApplicationSerializer, TicketApplySeria
|
|||
model = ApplyAssetTicket
|
||||
writeable_fields = [
|
||||
'id', 'title', 'type', 'apply_nodes', 'apply_assets',
|
||||
'apply_system_users', 'apply_actions',
|
||||
'apply_system_users', 'apply_actions', 'comment',
|
||||
'apply_date_start', 'apply_date_expired', 'org_id'
|
||||
]
|
||||
fields = TicketApplySerializer.Meta.fields + \
|
||||
|
|
|
@ -191,7 +191,7 @@ class UserExpirationReminderMsg(UserMessage):
|
|||
class ResetSSHKeyMsg(UserMessage):
|
||||
def get_html_msg(self) -> dict:
|
||||
subject = _('Reset SSH Key')
|
||||
update_url = urljoin(settings.SITE_URL, '/ui/#/users/profile/?activeTab=SSHUpdate')
|
||||
update_url = urljoin(settings.SITE_URL, '/ui/#/profile/setting/?activeTab=SSHUpdate')
|
||||
context = {
|
||||
'name': self.user.name,
|
||||
'url': update_url,
|
||||
|
|
|
@ -80,8 +80,6 @@ REDIS_PORT: 6379
|
|||
# 启用定时任务
|
||||
# PERIOD_TASK_ENABLED: True
|
||||
#
|
||||
# Windows 登录跳过手动输入密码
|
||||
# WINDOWS_SKIP_ALL_MANUAL_PASSWORD: False
|
||||
|
||||
# 是否开启 Luna 水印
|
||||
# SECURITY_WATERMARK_ENABLED: False
|
||||
|
@ -97,3 +95,4 @@ REDIS_PORT: 6379
|
|||
|
||||
# 仅允许已存在的用户登录,不允许第三方认证后,自动创建用户
|
||||
# ONLY_ALLOW_EXIST_USER_AUTH: False
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[1;31m 你想偷看啥 !!!
|
||||
[1;31m What are you trying to peek at !!!
|
|
@ -3,3 +3,4 @@
|
|||
|
||||
## 访问在线文档
|
||||
[访问](https://docs.jumpserver.org)
|
||||
|
||||
|
|
|
@ -22,3 +22,4 @@ elif [[ "$action" == "sleep" ]];then
|
|||
else
|
||||
python jms "${action}" "${service}"
|
||||
fi
|
||||
|
||||
|
|
1
jms
1
jms
|
@ -189,3 +189,4 @@ if __name__ == '__main__':
|
|||
collect_static()
|
||||
else:
|
||||
start_services()
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -62,7 +62,7 @@ jsonfield2==4.0.0.post0
|
|||
geoip2==4.5.0
|
||||
ipip-ipdb==1.6.1
|
||||
# Django environment
|
||||
Django==3.2.14
|
||||
Django==3.2.15
|
||||
django-bootstrap3==14.2.0
|
||||
django-filter==2.4.0
|
||||
django-formtools==2.2
|
||||
|
@ -91,7 +91,7 @@ eventlet==0.33.1
|
|||
greenlet==1.1.2
|
||||
gunicorn==20.1.0
|
||||
celery==5.2.7
|
||||
flower==1.0.0
|
||||
flower==1.2.0
|
||||
django-celery-beat==2.3.0
|
||||
kombu==5.2.4
|
||||
# Auth
|
||||
|
@ -133,6 +133,7 @@ pymssql==2.1.5
|
|||
django-mysql==3.9.0
|
||||
django-redis==5.2.0
|
||||
python-redis-lock==3.7.0
|
||||
pyOpenSSL==22.0.0
|
||||
redis==4.3.3
|
||||
pymongo==4.2.0
|
||||
# Debug
|
||||
|
|
|
@ -6,6 +6,5 @@ import subprocess
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
subprocess.call('python3 jms start all', shell=True,
|
||||
stdin=sys.stdin, stdout=sys.stdout)
|
||||
subprocess.call('python3 jms start all', shell=True, stdin=sys.stdin, stdout=sys.stdout)
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -2,3 +2,4 @@ daemonize yes
|
|||
port 6379
|
||||
logfile "/opt/jumpserver/logs/redis.log"
|
||||
dir /opt/jumpserver/
|
||||
|
||||
|
|
Loading…
Reference in New Issue