mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-16 17:12:53 +00:00
Compare commits
123 Commits
v4.10.9
...
pr@dev@per
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
396144e3a8 | ||
|
|
c191d86f43 | ||
|
|
7911137ffb | ||
|
|
1053933cae | ||
|
|
96fdc025cd | ||
|
|
fde19764e0 | ||
|
|
978fbc70e6 | ||
|
|
636ffd786d | ||
|
|
3b756aa26f | ||
|
|
817c0099d1 | ||
|
|
a0d7871130 | ||
|
|
c97124c279 | ||
|
|
32a766ed34 | ||
|
|
58fd15d743 | ||
|
|
f50250dedb | ||
|
|
9e150b7fbe | ||
|
|
16c79f59a7 | ||
|
|
be0f04862a | ||
|
|
1a3fb2f0db | ||
|
|
4cd70efe66 | ||
|
|
28700c01c8 | ||
|
|
4524822245 | ||
|
|
9d04fda018 | ||
|
|
01c277cd1e | ||
|
|
c4b3531d72 | ||
|
|
8870d1ef9e | ||
|
|
6c5086a083 | ||
|
|
e9f762a982 | ||
|
|
d4d4cadbcd | ||
|
|
5e56590405 | ||
|
|
ad8c0f6664 | ||
|
|
47dd6babfc | ||
|
|
691d1c4dba | ||
|
|
ac485804d5 | ||
|
|
51e5fdb301 | ||
|
|
69c4d613f7 | ||
|
|
1ad825bf0d | ||
|
|
a286cb9343 | ||
|
|
1eb489bb2d | ||
|
|
4334ae9e5e | ||
|
|
f2e346a0c3 | ||
|
|
dc20b06431 | ||
|
|
387a9248fc | ||
|
|
705fd6385f | ||
|
|
0ccf36621f | ||
|
|
a9ae12fc2c | ||
|
|
7b1a25adde | ||
|
|
a1b5eb1cd8 | ||
|
|
24ac642c5e | ||
|
|
e4f5e21219 | ||
|
|
a2aae9db47 | ||
|
|
206c43cf75 | ||
|
|
019a657ec3 | ||
|
|
fad60ee40f | ||
|
|
1728412793 | ||
|
|
3e93034fbc | ||
|
|
f4b3a7d73a | ||
|
|
3781c40179 | ||
|
|
fab6219cea | ||
|
|
dd0cacb4bc | ||
|
|
b8639601a1 | ||
|
|
ab9882c9c1 | ||
|
|
77a7b74b15 | ||
|
|
4bc05865f1 | ||
|
|
bec9e4f3a7 | ||
|
|
359adf3dbb | ||
|
|
ac54bb672c | ||
|
|
9e3ba00bc4 | ||
|
|
2ec9a43317 | ||
|
|
06be56ef06 | ||
|
|
b2a618b206 | ||
|
|
1039c2e320 | ||
|
|
8d7267400d | ||
|
|
d67e473884 | ||
|
|
70068c9253 | ||
|
|
d68babb2e1 | ||
|
|
afb6f466d5 | ||
|
|
453ad331ee | ||
|
|
d309d11a8f | ||
|
|
4771693a56 | ||
|
|
cefc820ac1 | ||
|
|
d007afdb43 | ||
|
|
e8921a43be | ||
|
|
a9b44103d4 | ||
|
|
4abf2bded6 | ||
|
|
54693089a0 | ||
|
|
0b859dd502 | ||
|
|
3fb27f969a | ||
|
|
45627a1d92 | ||
|
|
245e2dab66 | ||
|
|
8f0a41b1a8 | ||
|
|
1a9e56c520 | ||
|
|
67c2f471b4 | ||
|
|
b04f96f5f2 | ||
|
|
30f03b7d89 | ||
|
|
28a97d0b5a | ||
|
|
3410686690 | ||
|
|
6860e2327f | ||
|
|
20253e760c | ||
|
|
a63cfde8d2 | ||
|
|
92e250e03b | ||
|
|
098f0950cb | ||
|
|
39b0830a6b | ||
|
|
2e847bc2bc | ||
|
|
f82f31876a | ||
|
|
cde182c015 | ||
|
|
b990cdf561 | ||
|
|
c9a062823d | ||
|
|
643ba4fc15 | ||
|
|
d16a55bbe2 | ||
|
|
ae31554729 | ||
|
|
53b47980a2 | ||
|
|
d31b5ee570 | ||
|
|
65aea1ea36 | ||
|
|
5abb5c5d5a | ||
|
|
93e41a5004 | ||
|
|
95f51bbe48 | ||
|
|
0184d292ec | ||
|
|
23a6d320c7 | ||
|
|
b16304c48a | ||
|
|
7cd1e4d3a0 | ||
|
|
64a9987c3f | ||
|
|
18bfe312fa |
26
.github/.github/issue-spam-config.json
vendored
Normal file
26
.github/.github/issue-spam-config.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"dry_run": false,
|
||||
"min_account_age_days": 3,
|
||||
"max_urls_for_spam": 1,
|
||||
"min_body_len_for_links": 40,
|
||||
"spam_words": [
|
||||
"call now",
|
||||
"zadzwoń",
|
||||
"zadzwoń teraz",
|
||||
"kontakt",
|
||||
"telefon",
|
||||
"telefone",
|
||||
"contato",
|
||||
"suporte",
|
||||
"infolinii",
|
||||
"click here",
|
||||
"buy now",
|
||||
"subscribe",
|
||||
"visit"
|
||||
],
|
||||
"bracket_max": 6,
|
||||
"special_char_density_threshold": 0.12,
|
||||
"phone_regex": "\\+?\\d[\\d\\-\\s\\(\\)\\.]{6,}\\d",
|
||||
"labels_for_spam": ["spam"],
|
||||
"labels_for_review": ["needs-triage"]
|
||||
}
|
||||
120
.github/workflows/build-base-image.yml
vendored
120
.github/workflows/build-base-image.yml
vendored
@@ -1,74 +1,72 @@
|
||||
name: Build and Push Base Image
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'dev'
|
||||
- 'v*'
|
||||
paths:
|
||||
- poetry.lock
|
||||
- pyproject.toml
|
||||
- Dockerfile-base
|
||||
- package.json
|
||||
- go.mod
|
||||
- yarn.lock
|
||||
- pom.xml
|
||||
- install_deps.sh
|
||||
- utils/clean_site_packages.sh
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
pull_request:
|
||||
branches:
|
||||
- 'dev'
|
||||
- 'v*'
|
||||
paths:
|
||||
- poetry.lock
|
||||
- pyproject.toml
|
||||
- Dockerfile-base
|
||||
- package.json
|
||||
- go.mod
|
||||
- yarn.lock
|
||||
- pom.xml
|
||||
- install_deps.sh
|
||||
- utils/clean_site_packages.sh
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
build-and-push:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:qemu-v7.0.0-28
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract date
|
||||
id: vars
|
||||
run: echo "IMAGE_TAG=$(date +'%Y%m%d_%H%M%S')" >> $GITHUB_ENV
|
||||
- name: Extract date
|
||||
id: vars
|
||||
run: echo "IMAGE_TAG=$(date +'%Y%m%d_%H%M%S')" >> $GITHUB_ENV
|
||||
|
||||
- name: Extract repository name
|
||||
id: repo
|
||||
run: echo "REPO=$(basename ${{ github.repository }})" >> $GITHUB_ENV
|
||||
- name: Extract repository name
|
||||
id: repo
|
||||
run: echo "REPO=$(basename ${{ github.repository }})" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
file: Dockerfile-base
|
||||
tags: jumpserver/core-base:${{ env.IMAGE_TAG }}
|
||||
- name: Build and push multi-arch image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
file: Dockerfile-base
|
||||
tags: jumpserver/core-base:${{ env.IMAGE_TAG }}
|
||||
|
||||
- name: Update Dockerfile
|
||||
run: |
|
||||
sed -i 's|-base:.* AS stage-build|-base:${{ env.IMAGE_TAG }} AS stage-build|' Dockerfile
|
||||
- name: Update Dockerfile
|
||||
run: |
|
||||
sed -i 's|-base:.* AS stage-build|-base:${{ env.IMAGE_TAG }} AS stage-build|' Dockerfile
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git add Dockerfile
|
||||
git commit -m "perf: Update Dockerfile with new base image tag"
|
||||
git push origin ${{ github.event.pull_request.head.ref }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git add Dockerfile
|
||||
git commit -m "perf: Update Dockerfile with new base image tag"
|
||||
git push origin ${{ github.event.pull_request.head.ref }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
46
.github/workflows/build-python-image.yml
vendored
Normal file
46
.github/workflows/build-python-image.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Build and Push Python Base Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag to build'
|
||||
required: true
|
||||
default: '3.11-slim-bullseye-v1'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:qemu-v7.0.0-28
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract repository name
|
||||
id: repo
|
||||
run: echo "REPO=$(basename ${{ github.repository }})" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
file: Dockerfile-python
|
||||
tags: jumpserver/core-base:python-${{ inputs.tag }}
|
||||
|
||||
123
.github/workflows/cleanup-branches.yml
vendored
Normal file
123
.github/workflows/cleanup-branches.yml
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
name: Cleanup PR Branches
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 每天凌晨2点运行
|
||||
- cron: '0 2 * * *'
|
||||
workflow_dispatch:
|
||||
# 允许手动触发
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Dry run mode (default: true)'
|
||||
required: false
|
||||
default: 'true'
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
cleanup-branches:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # 获取所有分支和提交历史
|
||||
|
||||
- name: Setup Git
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions"
|
||||
git config --global user.email "actions@github.com"
|
||||
|
||||
- name: Get dry run setting
|
||||
id: dry-run
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "dry_run=${{ github.event.inputs.dry_run }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "dry_run=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Cleanup branches
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DRY_RUN: ${{ steps.dry-run.outputs.dry_run }}
|
||||
run: |
|
||||
echo "Starting branch cleanup..."
|
||||
echo "Dry run mode: $DRY_RUN"
|
||||
|
||||
# 获取所有本地分支
|
||||
git fetch --all --prune
|
||||
|
||||
# 获取以 pr 或 repr 开头的分支
|
||||
branches=$(git branch -r | grep -E 'origin/(pr|repr)' | sed 's/origin\///' | grep -v 'HEAD')
|
||||
|
||||
echo "Found branches matching pattern:"
|
||||
echo "$branches"
|
||||
|
||||
deleted_count=0
|
||||
skipped_count=0
|
||||
|
||||
for branch in $branches; do
|
||||
echo ""
|
||||
echo "Processing branch: $branch"
|
||||
|
||||
# 检查分支是否有未合并的PR
|
||||
pr_info=$(gh pr list --head "$branch" --state open --json number,title,state 2>/dev/null)
|
||||
|
||||
if [ $? -eq 0 ] && [ "$pr_info" != "[]" ]; then
|
||||
echo " ⚠️ Branch has open PR(s), skipping deletion"
|
||||
echo " PR info: $pr_info"
|
||||
skipped_count=$((skipped_count + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# 检查分支是否有已合并的PR(可选:如果PR已合并也可以删除)
|
||||
merged_pr_info=$(gh pr list --head "$branch" --state merged --json number,title,state 2>/dev/null)
|
||||
|
||||
if [ $? -eq 0 ] && [ "$merged_pr_info" != "[]" ]; then
|
||||
echo " ✅ Branch has merged PR(s), safe to delete"
|
||||
echo " Merged PR info: $merged_pr_info"
|
||||
else
|
||||
echo " ℹ️ No PRs found for this branch"
|
||||
fi
|
||||
|
||||
# 执行删除操作
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
echo " 🔍 [DRY RUN] Would delete branch: $branch"
|
||||
deleted_count=$((deleted_count + 1))
|
||||
else
|
||||
echo " 🗑️ Deleting branch: $branch"
|
||||
|
||||
# 删除远程分支
|
||||
if git push origin --delete "$branch" 2>/dev/null; then
|
||||
echo " ✅ Successfully deleted remote branch: $branch"
|
||||
deleted_count=$((deleted_count + 1))
|
||||
else
|
||||
echo " ❌ Failed to delete remote branch: $branch"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Cleanup Summary ==="
|
||||
echo "Branches processed: $(echo "$branches" | wc -l)"
|
||||
echo "Branches deleted: $deleted_count"
|
||||
echo "Branches skipped: $skipped_count"
|
||||
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
echo ""
|
||||
echo "🔍 This was a DRY RUN - no branches were actually deleted"
|
||||
echo "To perform actual deletion, run this workflow manually with dry_run=false"
|
||||
fi
|
||||
|
||||
- name: Create summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Branch Cleanup Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Workflow:** ${{ github.workflow }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Run ID:** ${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Dry Run:** ${{ steps.dry-run.outputs.dry_run }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Triggered by:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Check the logs above for detailed information about processed branches." >> $GITHUB_STEP_SUMMARY
|
||||
29
.github/workflows/jms-generic-action-handler.yml
vendored
29
.github/workflows/jms-generic-action-handler.yml
vendored
@@ -1,10 +1,33 @@
|
||||
on: [push, pull_request, release]
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, synchronize, closed]
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
name: JumpServer repos generic handler
|
||||
|
||||
jobs:
|
||||
generic_handler:
|
||||
name: Run generic handler
|
||||
handle_pull_request:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: jumpserver/action-generic-handler@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }}
|
||||
I18N_TOKEN: ${{ secrets.I18N_TOKEN }}
|
||||
|
||||
handle_push:
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: jumpserver/action-generic-handler@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }}
|
||||
I18N_TOKEN: ${{ secrets.I18N_TOKEN }}
|
||||
|
||||
handle_release:
|
||||
if: github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: jumpserver/action-generic-handler@master
|
||||
|
||||
9
.github/workflows/sync-gitee.yml
vendored
9
.github/workflows/sync-gitee.yml
vendored
@@ -1,11 +1,9 @@
|
||||
name: 🔀 Sync mirror to Gitee
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
create:
|
||||
schedule:
|
||||
# 每天凌晨3点运行
|
||||
- cron: '0 3 * * *'
|
||||
|
||||
jobs:
|
||||
mirror:
|
||||
@@ -14,7 +12,6 @@ jobs:
|
||||
steps:
|
||||
- name: mirror
|
||||
continue-on-error: true
|
||||
if: github.event_name == 'push' || (github.event_name == 'create' && github.event.ref_type == 'tag')
|
||||
uses: wearerequired/git-mirror-action@v1
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.GITEE_SSH_PRIVATE_KEY }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM jumpserver/core-base:20250827_025554 AS stage-build
|
||||
FROM jumpserver/core-base:20251113_092612 AS stage-build
|
||||
|
||||
ARG VERSION
|
||||
|
||||
@@ -19,7 +19,7 @@ RUN set -ex \
|
||||
&& python manage.py compilemessages
|
||||
|
||||
|
||||
FROM python:3.11-slim-bullseye
|
||||
FROM python:3.11-slim-trixie
|
||||
ENV LANG=en_US.UTF-8 \
|
||||
PATH=/opt/py3/bin:$PATH
|
||||
|
||||
@@ -39,7 +39,7 @@ ARG TOOLS=" \
|
||||
ARG APT_MIRROR=http://deb.debian.org
|
||||
|
||||
RUN set -ex \
|
||||
&& sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
|
||||
&& sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list.d/debian.sources \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& apt-get update > /dev/null \
|
||||
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
FROM python:3.11-slim-bullseye
|
||||
FROM python:3.11.14-slim-trixie
|
||||
ARG TARGETARCH
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.6.14 /uv /uvx /usr/local/bin/
|
||||
# Install APT dependencies
|
||||
ARG DEPENDENCIES=" \
|
||||
ca-certificates \
|
||||
@@ -22,13 +21,13 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
|
||||
set -ex \
|
||||
&& rm -f /etc/apt/apt.conf.d/docker-clean \
|
||||
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache \
|
||||
&& sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
|
||||
&& sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list.d/debian.sources \
|
||||
&& apt-get update > /dev/null \
|
||||
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
|
||||
&& echo "no" | dpkg-reconfigure dash
|
||||
|
||||
# Install bin tools
|
||||
ARG CHECK_VERSION=v1.0.4
|
||||
ARG CHECK_VERSION=v1.0.5
|
||||
RUN set -ex \
|
||||
&& wget https://github.com/jumpserver-dev/healthcheck/releases/download/${CHECK_VERSION}/check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \
|
||||
&& tar -xf check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \
|
||||
@@ -41,12 +40,10 @@ RUN set -ex \
|
||||
WORKDIR /opt/jumpserver
|
||||
|
||||
ARG PIP_MIRROR=https://pypi.org/simple
|
||||
ENV POETRY_PYPI_MIRROR_URL=${PIP_MIRROR}
|
||||
ENV ANSIBLE_COLLECTIONS_PATHS=/opt/py3/lib/python3.11/site-packages/ansible_collections
|
||||
ENV LANG=en_US.UTF-8 \
|
||||
PATH=/opt/py3/bin:$PATH
|
||||
|
||||
ENV UV_LINK_MODE=copy
|
||||
ENV SETUPTOOLS_SCM_PRETEND_VERSION=3.4.5
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
@@ -54,6 +51,7 @@ RUN --mount=type=cache,target=/root/.cache \
|
||||
--mount=type=bind,source=requirements/collections.yml,target=collections.yml \
|
||||
--mount=type=bind,source=requirements/static_files.sh,target=utils/static_files.sh \
|
||||
set -ex \
|
||||
&& pip install uv -i${PIP_MIRROR} \
|
||||
&& uv venv \
|
||||
&& uv pip install -i${PIP_MIRROR} -r pyproject.toml \
|
||||
&& ln -sf $(pwd)/.venv /opt/py3 \
|
||||
|
||||
@@ -13,7 +13,7 @@ ARG TOOLS=" \
|
||||
nmap \
|
||||
telnet \
|
||||
vim \
|
||||
postgresql-client-13 \
|
||||
postgresql-client \
|
||||
wget \
|
||||
poppler-utils"
|
||||
|
||||
|
||||
@@ -77,7 +77,8 @@ JumpServer consists of multiple key components, which collectively form the func
|
||||
| [Luna](https://github.com/jumpserver/luna) | <a href="https://github.com/jumpserver/luna/releases"><img alt="Luna release" src="https://img.shields.io/github/release/jumpserver/luna.svg" /></a> | JumpServer Web Terminal |
|
||||
| [KoKo](https://github.com/jumpserver/koko) | <a href="https://github.com/jumpserver/koko/releases"><img alt="Koko release" src="https://img.shields.io/github/release/jumpserver/koko.svg" /></a> | JumpServer Character Protocol Connector |
|
||||
| [Lion](https://github.com/jumpserver/lion) | <a href="https://github.com/jumpserver/lion/releases"><img alt="Lion release" src="https://img.shields.io/github/release/jumpserver/lion.svg" /></a> | JumpServer Graphical Protocol Connector |
|
||||
| [Chen](https://github.com/jumpserver/chen) | <a href="https://github.com/jumpserver/chen/releases"><img alt="Chen release" src="https://img.shields.io/github/release/jumpserver/chen.svg" /> | JumpServer Web DB |
|
||||
| [Chen](https://github.com/jumpserver/chen) | <a href="https://github.com/jumpserver/chen/releases"><img alt="Chen release" src="https://img.shields.io/github/release/jumpserver/chen.svg" /> | JumpServer Web DB
|
||||
| [Client](https://github.com/jumpserver/clients) | <a href="https://github.com/jumpserver/clients/releases"><img alt="Clients release" src="https://img.shields.io/github/release/jumpserver/clients.svg" /> | JumpServer Client |
|
||||
| [Tinker](https://github.com/jumpserver/tinker) | <img alt="Tinker" src="https://img.shields.io/badge/release-private-red" /> | JumpServer Remote Application Connector (Windows) |
|
||||
| [Panda](https://github.com/jumpserver/Panda) | <img alt="Panda" src="https://img.shields.io/badge/release-private-red" /> | JumpServer EE Remote Application Connector (Linux) |
|
||||
| [Razor](https://github.com/jumpserver/razor) | <img alt="Chen" src="https://img.shields.io/badge/release-private-red" /> | JumpServer EE RDP Proxy Connector |
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers as drf_serializers
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.generics import ListAPIView, CreateAPIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.status import HTTP_200_OK
|
||||
from rest_framework.status import HTTP_200_OK, HTTP_400_BAD_REQUEST
|
||||
|
||||
from accounts import serializers
|
||||
from accounts.const import ChangeSecretRecordStatusChoice
|
||||
from accounts.filters import AccountFilterSet, NodeFilterBackend
|
||||
from accounts.mixins import AccountRecordViewLogMixin
|
||||
from accounts.models import Account, ChangeSecretRecord
|
||||
from assets.const.gpt import create_or_update_chatx_resources
|
||||
from assets.models import Asset, Node
|
||||
from authentication.permissions import UserConfirmation, ConfirmType
|
||||
from common.api.mixin import ExtraFilterFieldsMixin
|
||||
@@ -18,6 +20,7 @@ from common.drf.filters import AttrRulesFilterBackend
|
||||
from common.permissions import IsValidUser
|
||||
from common.utils import lazyproperty, get_logger
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from rbac.permissions import RBACPermission
|
||||
|
||||
logger = get_logger(__file__)
|
||||
@@ -43,6 +46,7 @@ class AccountViewSet(OrgBulkModelViewSet):
|
||||
'clear_secret': 'accounts.change_account',
|
||||
'move_to_assets': 'accounts.delete_account',
|
||||
'copy_to_assets': 'accounts.add_account',
|
||||
'chat': 'accounts.view_account',
|
||||
}
|
||||
export_as_zip = True
|
||||
|
||||
@@ -152,6 +156,13 @@ class AccountViewSet(OrgBulkModelViewSet):
|
||||
def copy_to_assets(self, request, *args, **kwargs):
|
||||
return self._copy_or_move_to_assets(request, move=False)
|
||||
|
||||
@action(methods=['get'], detail=False, url_path='chat')
|
||||
def chat(self, request, *args, **kwargs):
|
||||
with tmp_to_root_org():
|
||||
__, account = create_or_update_chatx_resources()
|
||||
serializer = self.get_serializer(account)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class AccountSecretsViewSet(AccountRecordViewLogMixin, AccountViewSet):
|
||||
"""
|
||||
@@ -174,12 +185,66 @@ class AssetAccountBulkCreateApi(CreateAPIView):
|
||||
'POST': 'accounts.add_account',
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_all_assets(base_payload: dict):
|
||||
nodes = base_payload.pop('nodes', [])
|
||||
asset_ids = base_payload.pop('assets', [])
|
||||
nodes = Node.objects.filter(id__in=nodes).only('id', 'key')
|
||||
|
||||
node_asset_ids = Node.get_nodes_all_assets(*nodes).values_list('id', flat=True)
|
||||
asset_ids = set(asset_ids + list(node_asset_ids))
|
||||
return Asset.objects.filter(id__in=asset_ids)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
data = serializer.create(serializer.validated_data)
|
||||
serializer = serializers.AssetAccountBulkSerializerResultSerializer(data, many=True)
|
||||
return Response(data=serializer.data, status=HTTP_200_OK)
|
||||
if hasattr(request.data, "copy"):
|
||||
base_payload = request.data.copy()
|
||||
else:
|
||||
base_payload = dict(request.data)
|
||||
|
||||
templates = base_payload.pop("template", None)
|
||||
assets = self.get_all_assets(base_payload)
|
||||
if not assets.exists():
|
||||
error = _("No valid assets found for account creation.")
|
||||
return Response(
|
||||
data={
|
||||
"detail": error,
|
||||
"code": "no_valid_assets"
|
||||
},
|
||||
status=HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
result = []
|
||||
errors = []
|
||||
|
||||
def handle_one(_payload):
|
||||
try:
|
||||
ser = self.get_serializer(data=_payload)
|
||||
ser.is_valid(raise_exception=True)
|
||||
data = ser.bulk_create(ser.validated_data, assets)
|
||||
if isinstance(data, (list, tuple)):
|
||||
result.extend(data)
|
||||
else:
|
||||
result.append(data)
|
||||
except drf_serializers.ValidationError as e:
|
||||
errors.extend(list(e.detail))
|
||||
except Exception as e:
|
||||
errors.extend([str(e)])
|
||||
|
||||
if not templates:
|
||||
handle_one(base_payload)
|
||||
else:
|
||||
if not isinstance(templates, (list, tuple)):
|
||||
templates = [templates]
|
||||
for tpl in templates:
|
||||
payload = dict(base_payload)
|
||||
payload["template"] = tpl
|
||||
handle_one(payload)
|
||||
|
||||
if errors:
|
||||
raise drf_serializers.ValidationError(errors)
|
||||
|
||||
out_ser = serializers.AssetAccountBulkSerializerResultSerializer(result, many=True)
|
||||
return Response(data=out_ser.data, status=HTTP_200_OK)
|
||||
|
||||
|
||||
class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixin, ListAPIView):
|
||||
|
||||
@@ -25,7 +25,8 @@ class IntegrationApplicationViewSet(OrgBulkModelViewSet):
|
||||
}
|
||||
rbac_perms = {
|
||||
'get_once_secret': 'accounts.change_integrationapplication',
|
||||
'get_account_secret': 'accounts.view_integrationapplication'
|
||||
'get_account_secret': 'accounts.view_integrationapplication',
|
||||
'get_sdks_info': 'accounts.view_integrationapplication'
|
||||
}
|
||||
|
||||
def read_file(self, path):
|
||||
@@ -36,7 +37,6 @@ class IntegrationApplicationViewSet(OrgBulkModelViewSet):
|
||||
|
||||
@action(
|
||||
['GET'], detail=False, url_path='sdks',
|
||||
permission_classes=[IsValidUser]
|
||||
)
|
||||
def get_sdks_info(self, request, *args, **kwargs):
|
||||
code_suffix_mapper = {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
|
||||
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
|
||||
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
|
||||
group: "{{ params.group if params.group | length > 0 else omit }}"
|
||||
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
|
||||
append: "{{ true if params.groups | length > 0 else false }}"
|
||||
expires: -1
|
||||
|
||||
@@ -28,6 +28,12 @@ params:
|
||||
default: ''
|
||||
help_text: "{{ 'Params home help text' | trans }}"
|
||||
|
||||
- name: group
|
||||
type: str
|
||||
label: "{{ 'Params group label' | trans }}"
|
||||
default: ''
|
||||
help_text: "{{ 'Params group help text' | trans }}"
|
||||
|
||||
- name: groups
|
||||
type: str
|
||||
label: "{{ 'Params groups label' | trans }}"
|
||||
@@ -61,6 +67,11 @@ i18n:
|
||||
ja: 'デフォルトのホームディレクトリ /home/{アカウントユーザ名}'
|
||||
en: 'Default home directory /home/{account username}'
|
||||
|
||||
Params group help text:
|
||||
zh: '请输入用户组(名字或数字),只能输入一个(需填写已存在的用户组)'
|
||||
ja: 'ユーザー グループ (名前または番号) を入力してください。入力できるのは 1 つだけです (既存のユーザー グループを入力する必要があります)'
|
||||
en: 'Please enter a user group (name or number), only one can be entered (must fill in an existing user group)'
|
||||
|
||||
Params groups help text:
|
||||
zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
|
||||
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
|
||||
@@ -86,6 +97,11 @@ i18n:
|
||||
ja: 'グループ'
|
||||
en: 'Groups'
|
||||
|
||||
Params group label:
|
||||
zh: '主组'
|
||||
ja: '主组'
|
||||
en: 'Main group'
|
||||
|
||||
Params uid label:
|
||||
zh: '用户ID'
|
||||
ja: 'ユーザーID'
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
|
||||
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
|
||||
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
|
||||
group: "{{ params.group if params.group | length > 0 else omit }}"
|
||||
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
|
||||
append: "{{ true if params.groups | length > 0 else false }}"
|
||||
expires: -1
|
||||
|
||||
@@ -30,6 +30,12 @@ params:
|
||||
default: ''
|
||||
help_text: "{{ 'Params home help text' | trans }}"
|
||||
|
||||
- name: group
|
||||
type: str
|
||||
label: "{{ 'Params group label' | trans }}"
|
||||
default: ''
|
||||
help_text: "{{ 'Params group help text' | trans }}"
|
||||
|
||||
- name: groups
|
||||
type: str
|
||||
label: "{{ 'Params groups label' | trans }}"
|
||||
@@ -63,6 +69,11 @@ i18n:
|
||||
ja: 'デフォルトのホームディレクトリ /home/{アカウントユーザ名}'
|
||||
en: 'Default home directory /home/{account username}'
|
||||
|
||||
Params group help text:
|
||||
zh: '请输入用户组(名字或数字),只能输入一个(需填写已存在的用户组)'
|
||||
ja: 'ユーザー グループ (名前または番号) を入力してください。入力できるのは 1 つだけです (既存のユーザー グループを入力する必要があります)'
|
||||
en: 'Please enter a user group (name or number), only one can be entered (must fill in an existing user group)'
|
||||
|
||||
Params groups help text:
|
||||
zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
|
||||
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
|
||||
@@ -88,6 +99,11 @@ i18n:
|
||||
ja: 'グループ'
|
||||
en: 'Groups'
|
||||
|
||||
Params group label:
|
||||
zh: '主组'
|
||||
ja: '主组'
|
||||
en: 'Main group'
|
||||
|
||||
Params uid label:
|
||||
zh: '用户ID'
|
||||
ja: 'ユーザーID'
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
|
||||
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
|
||||
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
|
||||
group: "{{ params.group if params.group | length > 0 else omit }}"
|
||||
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
|
||||
append: "{{ true if params.groups | length > 0 else false }}"
|
||||
expires: -1
|
||||
|
||||
@@ -28,6 +28,12 @@ params:
|
||||
default: ''
|
||||
help_text: "{{ 'Params home help text' | trans }}"
|
||||
|
||||
- name: group
|
||||
type: str
|
||||
label: "{{ 'Params group label' | trans }}"
|
||||
default: ''
|
||||
help_text: "{{ 'Params group help text' | trans }}"
|
||||
|
||||
- name: groups
|
||||
type: str
|
||||
label: "{{ 'Params groups label' | trans }}"
|
||||
@@ -61,6 +67,11 @@ i18n:
|
||||
ja: 'デフォルトのホームディレクトリ /home/{アカウントユーザ名}'
|
||||
en: 'Default home directory /home/{account username}'
|
||||
|
||||
Params group help text:
|
||||
zh: '请输入用户组(名字或数字),只能输入一个(需填写已存在的用户组)'
|
||||
ja: 'ユーザー グループ (名前または番号) を入力してください。入力できるのは 1 つだけです (既存のユーザー グループを入力する必要があります)'
|
||||
en: 'Please enter a user group (name or number), only one can be entered (must fill in an existing user group)'
|
||||
|
||||
Params groups help text:
|
||||
zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
|
||||
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
|
||||
@@ -86,6 +97,11 @@ i18n:
|
||||
ja: 'グループ'
|
||||
en: 'Groups'
|
||||
|
||||
Params group label:
|
||||
zh: '主组'
|
||||
ja: '主组'
|
||||
en: 'Main group'
|
||||
|
||||
Params uid label:
|
||||
zh: '用户ID'
|
||||
ja: 'ユーザーID'
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
|
||||
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
|
||||
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
|
||||
group: "{{ params.group if params.group | length > 0 else omit }}"
|
||||
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
|
||||
append: "{{ true if params.groups | length > 0 else false }}"
|
||||
expires: -1
|
||||
|
||||
@@ -30,6 +30,12 @@ params:
|
||||
default: ''
|
||||
help_text: "{{ 'Params home help text' | trans }}"
|
||||
|
||||
- name: group
|
||||
type: str
|
||||
label: "{{ 'Params group label' | trans }}"
|
||||
default: ''
|
||||
help_text: "{{ 'Params group help text' | trans }}"
|
||||
|
||||
- name: groups
|
||||
type: str
|
||||
label: "{{ 'Params groups label' | trans }}"
|
||||
@@ -63,6 +69,11 @@ i18n:
|
||||
ja: 'デフォルトのホームディレクトリ /home/{アカウントユーザ名}'
|
||||
en: 'Default home directory /home/{account username}'
|
||||
|
||||
Params group help text:
|
||||
zh: '请输入用户组(名字或数字),只能输入一个(需填写已存在的用户组)'
|
||||
ja: 'ユーザー グループ (名前または番号) を入力してください。入力できるのは 1 つだけです (既存のユーザー グループを入力する必要があります)'
|
||||
en: 'Please enter a user group (name or number), only one can be entered (must fill in an existing user group)'
|
||||
|
||||
Params groups help text:
|
||||
zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
|
||||
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
|
||||
@@ -84,9 +95,14 @@ i18n:
|
||||
en: 'Home'
|
||||
|
||||
Params groups label:
|
||||
zh: '用户组'
|
||||
ja: 'グループ'
|
||||
en: 'Groups'
|
||||
zh: '附加组'
|
||||
ja: '追加グループ'
|
||||
en: 'Additional Group'
|
||||
|
||||
Params group label:
|
||||
zh: '主组'
|
||||
ja: '主组'
|
||||
en: 'Main group'
|
||||
|
||||
Params uid label:
|
||||
zh: '用户ID'
|
||||
|
||||
@@ -14,7 +14,7 @@ from accounts.models import Account, AccountTemplate, GatheredAccount
|
||||
from accounts.tasks import push_accounts_to_assets_task
|
||||
from assets.const import Category, AllTypes
|
||||
from assets.models import Asset
|
||||
from common.serializers import SecretReadableMixin
|
||||
from common.serializers import SecretReadableMixin, CommonBulkModelSerializer
|
||||
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
|
||||
from common.utils import get_logger
|
||||
from .base import BaseAccountSerializer, AuthValidateMixin
|
||||
@@ -292,26 +292,26 @@ class AccountDetailSerializer(AccountSerializer):
|
||||
|
||||
class AssetAccountBulkSerializerResultSerializer(serializers.Serializer):
|
||||
asset = serializers.CharField(read_only=True, label=_('Asset'))
|
||||
account = serializers.CharField(read_only=True, label=_('Account'))
|
||||
state = serializers.CharField(read_only=True, label=_('State'))
|
||||
error = serializers.CharField(read_only=True, label=_('Error'))
|
||||
changed = serializers.BooleanField(read_only=True, label=_('Changed'))
|
||||
|
||||
|
||||
class AssetAccountBulkSerializer(
|
||||
AccountCreateUpdateSerializerMixin, AuthValidateMixin, serializers.ModelSerializer
|
||||
AccountCreateUpdateSerializerMixin, AuthValidateMixin, CommonBulkModelSerializer
|
||||
):
|
||||
su_from_username = serializers.CharField(
|
||||
max_length=128, required=False, write_only=True, allow_null=True, label=_("Su from"),
|
||||
allow_blank=True,
|
||||
)
|
||||
assets = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, many=True, label=_('Assets'))
|
||||
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = [
|
||||
'name', 'username', 'secret', 'secret_type', 'passphrase',
|
||||
'privileged', 'is_active', 'comment', 'template',
|
||||
'on_invalid', 'push_now', 'params', 'assets',
|
||||
'name', 'username', 'secret', 'secret_type', 'secret_reset',
|
||||
'passphrase', 'privileged', 'is_active', 'comment', 'template',
|
||||
'on_invalid', 'push_now', 'params',
|
||||
'su_from_username', 'source', 'source_id',
|
||||
]
|
||||
extra_kwargs = {
|
||||
@@ -393,8 +393,7 @@ class AssetAccountBulkSerializer(
|
||||
handler = self._handle_err_create
|
||||
return handler
|
||||
|
||||
def perform_bulk_create(self, vd):
|
||||
assets = vd.pop('assets')
|
||||
def perform_bulk_create(self, vd, assets):
|
||||
on_invalid = vd.pop('on_invalid', 'skip')
|
||||
secret_type = vd.get('secret_type', 'password')
|
||||
|
||||
@@ -402,8 +401,7 @@ class AssetAccountBulkSerializer(
|
||||
vd['name'] = vd.get('username')
|
||||
|
||||
create_handler = self.get_create_handler(on_invalid)
|
||||
asset_ids = [asset.id for asset in assets]
|
||||
secret_type_supports = Asset.get_secret_type_assets(asset_ids, secret_type)
|
||||
secret_type_supports = Asset.get_secret_type_assets(assets, secret_type)
|
||||
|
||||
_results = {}
|
||||
for asset in assets:
|
||||
@@ -411,6 +409,7 @@ class AssetAccountBulkSerializer(
|
||||
_results[asset] = {
|
||||
'error': _('Asset does not support this secret type: %s') % secret_type,
|
||||
'state': 'error',
|
||||
'account': vd['name'],
|
||||
}
|
||||
continue
|
||||
|
||||
@@ -420,13 +419,13 @@ class AssetAccountBulkSerializer(
|
||||
self.clean_auth_fields(vd)
|
||||
instance, changed, state = self.perform_create(vd, create_handler)
|
||||
_results[asset] = {
|
||||
'changed': changed, 'instance': instance.id, 'state': state
|
||||
'changed': changed, 'instance': instance.id, 'state': state, 'account': vd['name']
|
||||
}
|
||||
except serializers.ValidationError as e:
|
||||
_results[asset] = {'error': e.detail[0], 'state': 'error'}
|
||||
_results[asset] = {'error': e.detail[0], 'state': 'error', 'account': vd['name']}
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
_results[asset] = {'error': str(e), 'state': 'error'}
|
||||
_results[asset] = {'error': str(e), 'state': 'error', 'account': vd['name']}
|
||||
|
||||
results = [{'asset': asset, **result} for asset, result in _results.items()]
|
||||
state_score = {'created': 3, 'updated': 2, 'skipped': 1, 'error': 0}
|
||||
@@ -443,7 +442,8 @@ class AssetAccountBulkSerializer(
|
||||
errors.append({
|
||||
'error': _('Account has exist'),
|
||||
'state': 'error',
|
||||
'asset': str(result['asset'])
|
||||
'asset': str(result['asset']),
|
||||
'account': result.get('account'),
|
||||
})
|
||||
if errors:
|
||||
raise serializers.ValidationError(errors)
|
||||
@@ -462,10 +462,16 @@ class AssetAccountBulkSerializer(
|
||||
account_ids = [str(_id) for _id in accounts.values_list('id', flat=True)]
|
||||
push_accounts_to_assets_task.delay(account_ids, params)
|
||||
|
||||
def create(self, validated_data):
|
||||
def bulk_create(self, validated_data, assets):
|
||||
if not assets:
|
||||
raise serializers.ValidationError(
|
||||
{'assets': _('At least one asset or node must be specified')},
|
||||
{'nodes': _('At least one asset or node must be specified')}
|
||||
)
|
||||
|
||||
params = validated_data.pop('params', None)
|
||||
push_now = validated_data.pop('push_now', False)
|
||||
results = self.perform_bulk_create(validated_data)
|
||||
results = self.perform_bulk_create(validated_data, assets)
|
||||
self.push_accounts_if_need(results, push_now, params)
|
||||
for res in results:
|
||||
res['asset'] = str(res['asset'])
|
||||
|
||||
@@ -3,3 +3,4 @@ from .connect_method import *
|
||||
from .login_acl import *
|
||||
from .login_asset_acl import *
|
||||
from .login_asset_check import *
|
||||
from .data_masking import *
|
||||
20
apps/acls/api/data_masking.py
Normal file
20
apps/acls/api/data_masking.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
|
||||
from .common import ACLUserFilterMixin
|
||||
from ..models import DataMaskingRule
|
||||
from .. import serializers
|
||||
|
||||
__all__ = ['DataMaskingRuleViewSet']
|
||||
|
||||
|
||||
class DataMaskingRuleFilter(ACLUserFilterMixin):
|
||||
class Meta:
|
||||
model = DataMaskingRule
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
class DataMaskingRuleViewSet(OrgBulkModelViewSet):
|
||||
model = DataMaskingRule
|
||||
filterset_class = DataMaskingRuleFilter
|
||||
search_fields = ('name',)
|
||||
serializer_class = serializers.DataMaskingRuleSerializer
|
||||
@@ -8,7 +8,7 @@ __all__ = ['LoginAssetACLViewSet']
|
||||
class LoginAssetACLFilter(ACLUserAssetFilterMixin):
|
||||
class Meta:
|
||||
model = models.LoginAssetACL
|
||||
fields = ['name', ]
|
||||
fields = ['name', 'action']
|
||||
|
||||
|
||||
class LoginAssetACLViewSet(OrgBulkModelViewSet):
|
||||
|
||||
45
apps/acls/migrations/0003_datamaskingrule.py
Normal file
45
apps/acls/migrations/0003_datamaskingrule.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# Generated by Django 4.1.13 on 2025-10-07 16:16
|
||||
|
||||
import common.db.fields
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('acls', '0002_auto_20210926_1047'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DataMaskingRule',
|
||||
fields=[
|
||||
('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')),
|
||||
('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')),
|
||||
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||
('priority', models.IntegerField(default=50, help_text='1-100, the lower the value will be match first', validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name='Priority')),
|
||||
('action', models.CharField(default='reject', max_length=64, verbose_name='Action')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='Active')),
|
||||
('users', common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users')),
|
||||
('assets', common.db.fields.JSONManyToManyField(default=dict, to='assets.Asset', verbose_name='Assets')),
|
||||
('accounts', models.JSONField(default=list, verbose_name='Accounts')),
|
||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||
('fields_pattern', models.CharField(default='password', max_length=128, verbose_name='Fields pattern')),
|
||||
('masking_method', models.CharField(choices=[('fixed_char', 'Fixed Character Replacement'), ('hide_middle', 'Hide Middle Characters'), ('keep_prefix', 'Keep Prefix Only'), ('keep_suffix', 'Keep Suffix Only')], default='fixed_char', max_length=32, verbose_name='Masking Method')),
|
||||
('mask_pattern', models.CharField(blank=True, default='######', max_length=128, null=True, verbose_name='Mask Pattern')),
|
||||
('reviewers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Data Masking Rule',
|
||||
'unique_together': {('org_id', 'name')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -2,3 +2,4 @@ from .command_acl import *
|
||||
from .connect_method import *
|
||||
from .login_acl import *
|
||||
from .login_asset_acl import *
|
||||
from .data_masking import *
|
||||
42
apps/acls/models/data_masking.py
Normal file
42
apps/acls/models/data_masking.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from django.db import models
|
||||
|
||||
from acls.models import UserAssetAccountBaseACL
|
||||
from common.utils import get_logger
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
__all__ = ['MaskingMethod', 'DataMaskingRule']
|
||||
|
||||
|
||||
class MaskingMethod(models.TextChoices):
|
||||
fixed_char = "fixed_char", _("Fixed Character Replacement") # 固定字符替换
|
||||
hide_middle = "hide_middle", _("Hide Middle Characters") # 隐藏中间几位
|
||||
keep_prefix = "keep_prefix", _("Keep Prefix Only") # 只保留前缀
|
||||
keep_suffix = "keep_suffix", _("Keep Suffix Only") # 只保留后缀
|
||||
|
||||
|
||||
class DataMaskingRule(UserAssetAccountBaseACL):
|
||||
name = models.CharField(max_length=128, verbose_name=_("Name"))
|
||||
fields_pattern = models.CharField(max_length=128, default='password', verbose_name=_("Fields pattern"))
|
||||
|
||||
masking_method = models.CharField(
|
||||
max_length=32,
|
||||
choices=MaskingMethod.choices,
|
||||
default=MaskingMethod.fixed_char,
|
||||
verbose_name=_("Masking Method"),
|
||||
)
|
||||
mask_pattern = models.CharField(
|
||||
max_length=128,
|
||||
verbose_name=_("Mask Pattern"),
|
||||
default="######",
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
unique_together = [('org_id', 'name')]
|
||||
verbose_name = _("Data Masking Rule")
|
||||
@@ -101,7 +101,8 @@ class AssetLoginReminderMsg(UserMessage):
|
||||
'ip': self.ip,
|
||||
'time': self.time,
|
||||
'login_from': self.login_from,
|
||||
'recipient': self.user,
|
||||
'recipient_name': self.user.name,
|
||||
'recipient_username': self.user.username,
|
||||
'username': self.login_user.username,
|
||||
'name': self.login_user.name,
|
||||
'asset': str(self.asset),
|
||||
|
||||
@@ -3,3 +3,4 @@ from .connect_method import *
|
||||
from .login_acl import *
|
||||
from .login_asset_acl import *
|
||||
from .login_asset_check import *
|
||||
from .data_masking import *
|
||||
19
apps/acls/serializers/data_masking.py
Normal file
19
apps/acls/serializers/data_masking.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from acls.models import MaskingMethod, DataMaskingRule
|
||||
from common.serializers.fields import LabeledChoiceField
|
||||
from common.serializers.mixin import CommonBulkModelSerializer
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from .base import BaseUserAssetAccountACLSerializer as BaseSerializer
|
||||
|
||||
__all__ = ['DataMaskingRuleSerializer']
|
||||
|
||||
|
||||
class DataMaskingRuleSerializer(BaseSerializer, BulkOrgResourceModelSerializer):
|
||||
masking_method = LabeledChoiceField(
|
||||
choices=MaskingMethod.choices, default=MaskingMethod.fixed_char, label=_('Masking Method')
|
||||
)
|
||||
|
||||
class Meta(BaseSerializer.Meta):
|
||||
model = DataMaskingRule
|
||||
fields = BaseSerializer.Meta.fields + ['fields_pattern', 'masking_method', 'mask_pattern']
|
||||
@@ -11,6 +11,7 @@ router.register(r'login-asset-acls', api.LoginAssetACLViewSet, 'login-asset-acl'
|
||||
router.register(r'command-filter-acls', api.CommandFilterACLViewSet, 'command-filter-acl')
|
||||
router.register(r'command-groups', api.CommandGroupViewSet, 'command-group')
|
||||
router.register(r'connect-method-acls', api.ConnectMethodACLViewSet, 'connect-method-acl')
|
||||
router.register(r'data-masking-rules', api.DataMaskingRuleViewSet, 'data-masking-rule')
|
||||
|
||||
urlpatterns = [
|
||||
path('login-asset/check/', api.LoginAssetCheckAPI.as_view(), name='login-asset-check'),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from collections import defaultdict
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext as _
|
||||
from django_filters import rest_framework as drf_filters
|
||||
@@ -113,7 +112,7 @@ class BaseAssetViewSet(OrgBulkModelViewSet):
|
||||
("accounts", AccountSerializer),
|
||||
)
|
||||
rbac_perms = (
|
||||
("match", "assets.match_asset"),
|
||||
("match", "assets.view_asset"),
|
||||
("platform", "assets.view_platform"),
|
||||
("gateways", "assets.view_gateway"),
|
||||
("accounts", "assets.view_account"),
|
||||
@@ -181,33 +180,18 @@ class AssetViewSet(SuggestionMixin, BaseAssetViewSet):
|
||||
def sync_platform_protocols(self, request, *args, **kwargs):
|
||||
platform_id = request.data.get('platform_id')
|
||||
platform = get_object_or_404(Platform, pk=platform_id)
|
||||
assets = platform.assets.all()
|
||||
asset_ids = list(platform.assets.values_list('id', flat=True))
|
||||
platform_protocols = list(platform.protocols.values('name', 'port'))
|
||||
|
||||
platform_protocols = {
|
||||
p['name']: p['port']
|
||||
for p in platform.protocols.values('name', 'port')
|
||||
}
|
||||
asset_protocols_map = defaultdict(set)
|
||||
protocols = assets.prefetch_related('protocols').values_list(
|
||||
'id', 'protocols__name'
|
||||
)
|
||||
for asset_id, protocol in protocols:
|
||||
asset_id = str(asset_id)
|
||||
asset_protocols_map[asset_id].add(protocol)
|
||||
objs = []
|
||||
for asset_id, protocols in asset_protocols_map.items():
|
||||
protocol_names = set(platform_protocols) - protocols
|
||||
if not protocol_names:
|
||||
continue
|
||||
for name in protocol_names:
|
||||
objs.append(
|
||||
Protocol(
|
||||
name=name,
|
||||
port=platform_protocols[name],
|
||||
asset_id=asset_id,
|
||||
)
|
||||
)
|
||||
Protocol.objects.bulk_create(objs)
|
||||
with transaction.atomic():
|
||||
if asset_ids:
|
||||
Protocol.objects.filter(asset_id__in=asset_ids).delete()
|
||||
if asset_ids and platform_protocols:
|
||||
objs = []
|
||||
for aid in asset_ids:
|
||||
for p in platform_protocols:
|
||||
objs.append(Protocol(name=p['name'], port=p['port'], asset_id=aid))
|
||||
Protocol.objects.bulk_create(objs)
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
def filter_bulk_update_data(self):
|
||||
|
||||
@@ -43,7 +43,7 @@ class NodeViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
||||
search_fields = ('full_value',)
|
||||
serializer_class = serializers.NodeSerializer
|
||||
rbac_perms = {
|
||||
'match': 'assets.match_node',
|
||||
'match': 'assets.view_node',
|
||||
'check_assets_amount_task': 'assets.change_node'
|
||||
}
|
||||
|
||||
|
||||
@@ -112,8 +112,10 @@ class PlatformProtocolViewSet(JMSModelViewSet):
|
||||
|
||||
|
||||
class PlatformAutomationMethodsApi(generics.ListAPIView):
|
||||
permission_classes = (IsValidUser,)
|
||||
queryset = PlatformAutomation.objects.none()
|
||||
rbac_perms = {
|
||||
'list': 'assets.view_platform'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def automation_methods():
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from orgs.models import Organization
|
||||
from .base import BaseType
|
||||
|
||||
|
||||
@@ -52,3 +53,41 @@ class GPTTypes(BaseType):
|
||||
return [
|
||||
cls.CHATGPT,
|
||||
]
|
||||
|
||||
|
||||
CHATX_NAME = 'ChatX'
|
||||
|
||||
|
||||
def create_or_update_chatx_resources(chatx_name=CHATX_NAME, org_id=Organization.SYSTEM_ID):
|
||||
from django.apps import apps
|
||||
|
||||
platform_model = apps.get_model('assets', 'Platform')
|
||||
asset_model = apps.get_model('assets', 'Asset')
|
||||
account_model = apps.get_model('accounts', 'Account')
|
||||
|
||||
platform, __ = platform_model.objects.get_or_create(
|
||||
name=chatx_name,
|
||||
defaults={
|
||||
'internal': True,
|
||||
'type': chatx_name,
|
||||
'category': 'ai',
|
||||
}
|
||||
)
|
||||
asset, __ = asset_model.objects.get_or_create(
|
||||
address=chatx_name,
|
||||
defaults={
|
||||
'name': chatx_name,
|
||||
'platform': platform,
|
||||
'org_id': org_id
|
||||
}
|
||||
)
|
||||
|
||||
account, __ = account_model.objects.get_or_create(
|
||||
username=chatx_name,
|
||||
defaults={
|
||||
'name': chatx_name,
|
||||
'asset': asset,
|
||||
'org_id': org_id
|
||||
}
|
||||
)
|
||||
return asset, account
|
||||
|
||||
@@ -268,6 +268,14 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
||||
'port_from_addr': True,
|
||||
'required': True,
|
||||
'secret_types': ['token'],
|
||||
'setting': {
|
||||
'namespace': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
'default': '',
|
||||
'label': _('Namespace')
|
||||
}
|
||||
}
|
||||
},
|
||||
cls.http: {
|
||||
'port': 80,
|
||||
|
||||
@@ -408,8 +408,7 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
|
||||
return tree_node
|
||||
|
||||
@staticmethod
|
||||
def get_secret_type_assets(asset_ids, secret_type):
|
||||
assets = Asset.objects.filter(id__in=asset_ids)
|
||||
def get_secret_type_assets(assets, secret_type):
|
||||
asset_protocol = assets.prefetch_related('protocols').values_list('id', 'protocols__name')
|
||||
protocol_secret_types_map = const.Protocol.protocol_secret_types()
|
||||
asset_secret_types_mapp = defaultdict(set)
|
||||
|
||||
@@ -28,7 +28,8 @@ class MyAsset(JMSBaseModel):
|
||||
|
||||
@staticmethod
|
||||
def set_asset_custom_value(assets, user):
|
||||
my_assets = MyAsset.objects.filter(asset__in=assets, user=user).all()
|
||||
asset_ids = [asset.id for asset in assets]
|
||||
my_assets = MyAsset.objects.filter(asset_id__in=asset_ids, user=user).all()
|
||||
customs = {my_asset.asset.id: my_asset.custom_to_dict() for my_asset in my_assets}
|
||||
for asset in assets:
|
||||
custom = customs.get(asset.id)
|
||||
|
||||
@@ -59,7 +59,10 @@ class DatabaseSerializer(AssetSerializer):
|
||||
if not platform:
|
||||
return
|
||||
|
||||
if platform.type in ['mysql', 'mariadb']:
|
||||
if platform.type in [
|
||||
'mysql', 'mariadb', 'oracle', 'sqlserver',
|
||||
'db2', 'dameng', 'clickhouse', 'redis'
|
||||
]:
|
||||
db_field.required = False
|
||||
db_field.allow_blank = True
|
||||
db_field.allow_null = True
|
||||
|
||||
@@ -26,4 +26,13 @@ class WebSerializer(AssetSerializer):
|
||||
'submit_selector': {
|
||||
'default': 'id=login_button',
|
||||
},
|
||||
'script': {
|
||||
'default': [],
|
||||
}
|
||||
}
|
||||
|
||||
def to_internal_value(self, data):
|
||||
data = data.copy()
|
||||
if data.get('script') in ("", None):
|
||||
data.pop('script', None)
|
||||
return super().to_internal_value(data)
|
||||
|
||||
@@ -84,6 +84,7 @@ class PlatformAutomationSerializer(serializers.ModelSerializer):
|
||||
class PlatformProtocolSerializer(serializers.ModelSerializer):
|
||||
setting = MethodSerializer(required=False, label=_("Setting"))
|
||||
port_from_addr = serializers.BooleanField(label=_("Port from addr"), read_only=True)
|
||||
port = serializers.IntegerField(label=_("Port"), required=False, min_value=0, max_value=65535)
|
||||
|
||||
class Meta:
|
||||
model = PlatformProtocol
|
||||
|
||||
@@ -23,6 +23,8 @@ logger = get_logger(__name__)
|
||||
|
||||
class OperatorLogHandler(metaclass=Singleton):
|
||||
CACHE_KEY = 'OPERATOR_LOG_CACHE_KEY'
|
||||
SYSTEM_OBJECTS = frozenset({"Role"})
|
||||
PREFER_CURRENT_ELSE_USER = frozenset({"SSOToken"})
|
||||
|
||||
def __init__(self):
|
||||
self.log_client = self.get_storage_client()
|
||||
@@ -142,13 +144,21 @@ class OperatorLogHandler(metaclass=Singleton):
|
||||
after = self.__data_processing(after)
|
||||
return before, after
|
||||
|
||||
@staticmethod
|
||||
def get_org_id(object_name):
|
||||
system_obj = ('Role',)
|
||||
org_id = get_current_org_id()
|
||||
if object_name in system_obj:
|
||||
org_id = Organization.SYSTEM_ID
|
||||
return org_id
|
||||
def get_org_id(self, user, object_name):
|
||||
if object_name in self.SYSTEM_OBJECTS:
|
||||
return Organization.SYSTEM_ID
|
||||
|
||||
current = get_current_org_id()
|
||||
current_id = str(current) if current else None
|
||||
|
||||
if object_name in self.PREFER_CURRENT_ELSE_USER:
|
||||
if current_id and current_id != Organization.DEFAULT_ID:
|
||||
return current_id
|
||||
|
||||
org = user.orgs.distinct().first()
|
||||
return str(org.id) if org else Organization.DEFAULT_ID
|
||||
|
||||
return current_id or Organization.DEFAULT_ID
|
||||
|
||||
def create_or_update_operate_log(
|
||||
self, action, resource_type, resource=None, resource_display=None,
|
||||
@@ -168,7 +178,7 @@ class OperatorLogHandler(metaclass=Singleton):
|
||||
# 前后都没变化,没必要生成日志,除非手动强制保存
|
||||
return
|
||||
|
||||
org_id = self.get_org_id(object_name)
|
||||
org_id = self.get_org_id(user, object_name)
|
||||
data = {
|
||||
'id': log_id, "user": str(user), 'action': action,
|
||||
'resource_type': str(resource_type), 'org_id': org_id,
|
||||
|
||||
@@ -47,20 +47,21 @@ def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs):
|
||||
objs = model.objects.filter(pk__in=pk_set)
|
||||
objs_display = [str(o) for o in objs]
|
||||
action = M2M_ACTION[action]
|
||||
changed_field = current_instance.get(field_name, [])
|
||||
changed_field = current_instance.get(field_name, {})
|
||||
changed_value = changed_field.get('value', [])
|
||||
|
||||
after, before, before_value = None, None, None
|
||||
if action == ActionChoices.create:
|
||||
before_value = list(set(changed_field) - set(objs_display))
|
||||
before_value = list(set(changed_value) - set(objs_display))
|
||||
elif action == ActionChoices.delete:
|
||||
before_value = list(
|
||||
set(changed_field).symmetric_difference(set(objs_display))
|
||||
)
|
||||
before_value = list(set(changed_value).symmetric_difference(set(objs_display)))
|
||||
|
||||
if changed_field:
|
||||
after = {field_name: changed_field}
|
||||
if before_value:
|
||||
before = {field_name: before_value}
|
||||
before_change_field = changed_field.copy()
|
||||
before_change_field['value'] = before_value
|
||||
before = {field_name: before_change_field}
|
||||
|
||||
if sorted(str(before)) == sorted(str(after)):
|
||||
return
|
||||
|
||||
@@ -69,6 +69,8 @@ class RDPFileClientProtocolURLMixin:
|
||||
'autoreconnection enabled:i': '1',
|
||||
'bookmarktype:i': '3',
|
||||
'use redirection server name:i': '0',
|
||||
'bitmapcachepersistenable:i': '0',
|
||||
'bitmapcachesize:i': '1500',
|
||||
}
|
||||
|
||||
# copy from
|
||||
@@ -76,7 +78,6 @@ class RDPFileClientProtocolURLMixin:
|
||||
rdp_low_speed_broadband_option = {
|
||||
"connection type:i": 2,
|
||||
"disable wallpaper:i": 1,
|
||||
"bitmapcachepersistenable:i": 1,
|
||||
"disable full window drag:i": 1,
|
||||
"disable menu anims:i": 1,
|
||||
"allow font smoothing:i": 0,
|
||||
@@ -87,7 +88,6 @@ class RDPFileClientProtocolURLMixin:
|
||||
rdp_high_speed_broadband_option = {
|
||||
"connection type:i": 4,
|
||||
"disable wallpaper:i": 0,
|
||||
"bitmapcachepersistenable:i": 1,
|
||||
"disable full window drag:i": 1,
|
||||
"disable menu anims:i": 0,
|
||||
"allow font smoothing:i": 0,
|
||||
@@ -362,6 +362,7 @@ class ConnectionTokenViewSet(AuthFaceMixin, ExtraActionApiMixin, RootOrgViewMixi
|
||||
self.validate_serializer(serializer)
|
||||
return super().perform_create(serializer)
|
||||
|
||||
|
||||
def _insert_connect_options(self, data, user):
|
||||
connect_options = data.pop('connect_options', {})
|
||||
default_name_opts = {
|
||||
@@ -375,7 +376,7 @@ class ConnectionTokenViewSet(AuthFaceMixin, ExtraActionApiMixin, RootOrgViewMixi
|
||||
for name in default_name_opts.keys():
|
||||
value = preferences.get(name, default_name_opts[name])
|
||||
connect_options[name] = value
|
||||
connect_options['lang'] = getattr(user, 'lang', settings.LANGUAGE_CODE)
|
||||
connect_options['lang'] = getattr(user, 'lang') or settings.LANGUAGE_CODE
|
||||
data['connect_options'] = connect_options
|
||||
|
||||
@staticmethod
|
||||
@@ -564,7 +565,9 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet):
|
||||
rbac_perms = {
|
||||
'create': 'authentication.add_superconnectiontoken',
|
||||
'renewal': 'authentication.add_superconnectiontoken',
|
||||
'list': 'authentication.view_superconnectiontoken',
|
||||
'check': 'authentication.view_superconnectiontoken',
|
||||
'retrieve': 'authentication.view_superconnectiontoken',
|
||||
'get_secret_detail': 'authentication.view_superconnectiontokensecret',
|
||||
'get_applet_info': 'authentication.view_superconnectiontoken',
|
||||
'release_applet_account': 'authentication.view_superconnectiontoken',
|
||||
@@ -572,7 +575,12 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet):
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return ConnectionToken.objects.all()
|
||||
return ConnectionToken.objects.none()
|
||||
|
||||
def get_object(self):
|
||||
pk = self.kwargs.get(self.lookup_field)
|
||||
token = get_object_or_404(ConnectionToken, pk=pk)
|
||||
return token
|
||||
|
||||
def get_user(self, serializer):
|
||||
return serializer.validated_data.get('user')
|
||||
|
||||
@@ -67,8 +67,9 @@ class UserResetPasswordSendCodeApi(CreateAPIView):
|
||||
|
||||
code = random_string(settings.SMS_CODE_LENGTH, lower=False, upper=False)
|
||||
subject = '%s: %s' % (get_login_title(), _('Forgot password'))
|
||||
tip = _('The validity period of the verification code is {} minute').format(settings.VERIFY_CODE_TTL // 60)
|
||||
context = {
|
||||
'user': user, 'title': subject, 'code': code,
|
||||
'user': user, 'title': subject, 'code': code, 'tip': tip,
|
||||
}
|
||||
message = render_to_string('authentication/_msg_reset_password_code.html', context)
|
||||
content = {'subject': subject, 'message': message}
|
||||
|
||||
@@ -25,7 +25,10 @@ class JMSBaseAuthBackend:
|
||||
"""
|
||||
# 三方用户认证完成后,在后续的 get_user 获取逻辑中,也应该需要检查用户是否有效
|
||||
is_valid = getattr(user, 'is_valid', None)
|
||||
return is_valid or is_valid is None
|
||||
if not is_valid:
|
||||
logger.info("User %s is not valid", getattr(user, "username", "<unknown>"))
|
||||
return False
|
||||
return True
|
||||
|
||||
# allow user to authenticate
|
||||
def username_allow_authenticate(self, username):
|
||||
|
||||
@@ -3,6 +3,7 @@ from django.contrib import auth
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
|
||||
from authentication.backends.base import BaseAuthCallbackClientView
|
||||
@@ -61,6 +62,10 @@ class OAuth2AuthCallbackView(View, FlashMessageMixin):
|
||||
return HttpResponseRedirect(
|
||||
settings.AUTH_OAUTH2_AUTHENTICATION_REDIRECT_URI
|
||||
)
|
||||
else:
|
||||
if getattr(request, 'error_message', ''):
|
||||
response = self.get_failed_response('/', title=_('OAuth2 Error'), msg=request.error_message)
|
||||
return response
|
||||
|
||||
logger.debug(log_prompt.format('Redirect'))
|
||||
redirect_url = settings.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT or '/'
|
||||
|
||||
@@ -134,6 +134,7 @@ class OIDCAuthCallbackView(View, FlashMessageMixin):
|
||||
log_prompt = "Process GET requests [OIDCAuthCallbackView]: {}"
|
||||
logger.debug(log_prompt.format('Start'))
|
||||
callback_params = request.GET
|
||||
error_title = _("OpenID Error")
|
||||
|
||||
# Retrieve the state value that was previously generated. No state means that we cannot
|
||||
# authenticate the user (so a failure should be returned).
|
||||
@@ -172,10 +173,9 @@ class OIDCAuthCallbackView(View, FlashMessageMixin):
|
||||
try:
|
||||
user = auth.authenticate(nonce=nonce, request=request, code_verifier=code_verifier)
|
||||
except IntegrityError as e:
|
||||
title = _("OpenID Error")
|
||||
msg = _('Please check if a user with the same username or email already exists')
|
||||
logger.error(e, exc_info=True)
|
||||
response = self.get_failed_response('/', title, msg)
|
||||
response = self.get_failed_response('/', error_title, msg)
|
||||
return response
|
||||
if user:
|
||||
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
||||
@@ -194,7 +194,6 @@ class OIDCAuthCallbackView(View, FlashMessageMixin):
|
||||
return HttpResponseRedirect(
|
||||
next_url or settings.AUTH_OPENID_AUTHENTICATION_REDIRECT_URI
|
||||
)
|
||||
|
||||
if 'error' in callback_params:
|
||||
logger.debug(
|
||||
log_prompt.format('Error in callback params: {}'.format(callback_params['error']))
|
||||
@@ -205,9 +204,12 @@ class OIDCAuthCallbackView(View, FlashMessageMixin):
|
||||
# OpenID Connect Provider authenticate endpoint.
|
||||
logger.debug(log_prompt.format('Logout'))
|
||||
auth.logout(request)
|
||||
|
||||
redirect_url = settings.AUTH_OPENID_AUTHENTICATION_FAILURE_REDIRECT_URI
|
||||
if not user and getattr(request, 'error_message', ''):
|
||||
response = self.get_failed_response(redirect_url, title=error_title, msg=request.error_message)
|
||||
return response
|
||||
logger.debug(log_prompt.format('Redirect'))
|
||||
return HttpResponseRedirect(settings.AUTH_OPENID_AUTHENTICATION_FAILURE_REDIRECT_URI)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
|
||||
class OIDCAuthCallbackClientView(BaseAuthCallbackClientView):
|
||||
|
||||
@@ -252,6 +252,7 @@ class Saml2AuthCallbackView(View, PrepareRequestMixin, FlashMessageMixin):
|
||||
def post(self, request):
|
||||
log_prompt = "Process SAML2 POST requests: {}"
|
||||
post_data = request.POST
|
||||
error_title = _("SAML2 Error")
|
||||
|
||||
try:
|
||||
saml_instance = self.init_saml_auth(request)
|
||||
@@ -279,15 +280,18 @@ class Saml2AuthCallbackView(View, PrepareRequestMixin, FlashMessageMixin):
|
||||
try:
|
||||
user = auth.authenticate(request=request, saml_user_data=saml_user_data)
|
||||
except IntegrityError as e:
|
||||
title = _("SAML2 Error")
|
||||
msg = _('Please check if a user with the same username or email already exists')
|
||||
logger.error(e, exc_info=True)
|
||||
response = self.get_failed_response('/', title, msg)
|
||||
response = self.get_failed_response('/', error_title, msg)
|
||||
return response
|
||||
if user and user.is_valid:
|
||||
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
||||
auth.login(self.request, user)
|
||||
|
||||
if not user and getattr(request, 'error_message', ''):
|
||||
response = self.get_failed_response('/', title=error_title, msg=request.error_message)
|
||||
return response
|
||||
|
||||
logger.debug(log_prompt.format('Redirect'))
|
||||
redir = post_data.get('RelayState')
|
||||
if not redir or len(redir) == 0:
|
||||
|
||||
@@ -114,12 +114,12 @@ class BlockMFAError(AuthFailedNeedLogMixin, AuthFailedError):
|
||||
super().__init__(username=username, request=request, ip=ip)
|
||||
|
||||
|
||||
class BlockLoginError(AuthFailedNeedBlockMixin, AuthFailedError):
|
||||
class BlockLoginError(AuthFailedNeedLogMixin, AuthFailedNeedBlockMixin, AuthFailedError):
|
||||
error = 'block_login'
|
||||
|
||||
def __init__(self, username, ip):
|
||||
def __init__(self, username, ip, request):
|
||||
self.msg = const.block_user_login_msg.format(settings.SECURITY_LOGIN_LIMIT_TIME)
|
||||
super().__init__(username=username, ip=ip)
|
||||
super().__init__(username=username, ip=ip, request=request)
|
||||
|
||||
|
||||
class SessionEmptyError(AuthFailedError):
|
||||
|
||||
@@ -50,7 +50,7 @@ class UserLoginForm(forms.Form):
|
||||
|
||||
class UserCheckOtpCodeForm(forms.Form):
|
||||
code = forms.CharField(label=_('MFA Code'), max_length=128, required=False)
|
||||
mfa_type = forms.CharField(label=_('MFA type'), max_length=128)
|
||||
mfa_type = forms.CharField(label=_('MFA type'), max_length=128, required=False)
|
||||
|
||||
|
||||
class CustomCaptchaTextInput(CaptchaTextInput):
|
||||
|
||||
@@ -38,7 +38,7 @@ class BaseMFA(abc.ABC):
|
||||
if not ok:
|
||||
return False, msg
|
||||
|
||||
cache.set(cache_key, code, 60)
|
||||
cache.set(cache_key, code, settings.VERIFY_CODE_TTL)
|
||||
return True, msg
|
||||
|
||||
def is_authenticated(self):
|
||||
@@ -72,10 +72,9 @@ class BaseMFA(abc.ABC):
|
||||
def is_active(self):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def global_enabled():
|
||||
return False
|
||||
@classmethod
|
||||
def global_enabled(cls):
|
||||
return cls.name in settings.SECURITY_MFA_ENABLED_BACKENDS
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_enable_url(self) -> str:
|
||||
|
||||
@@ -39,9 +39,9 @@ class MFACustom(BaseMFA):
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def global_enabled():
|
||||
return settings.MFA_CUSTOM and callable(mfa_custom_method)
|
||||
@classmethod
|
||||
def global_enabled(cls):
|
||||
return super().global_enabled() and settings.MFA_CUSTOM and callable(mfa_custom_method)
|
||||
|
||||
def get_enable_url(self) -> str:
|
||||
return ''
|
||||
|
||||
@@ -39,19 +39,20 @@ class MFAEmail(BaseMFA):
|
||||
def send_challenge(self):
|
||||
code = random_string(settings.SMS_CODE_LENGTH, lower=False, upper=False)
|
||||
subject = '%s: %s' % (get_login_title(), _('MFA code'))
|
||||
tip = _('The validity period of the verification code is {} minute').format(settings.VERIFY_CODE_TTL // 60)
|
||||
context = {
|
||||
'user': self.user, 'title': subject, 'code': code,
|
||||
'user': self.user, 'title': subject, 'code': code, 'tip': tip,
|
||||
}
|
||||
message = render_to_string('authentication/_msg_mfa_email_code.html', context)
|
||||
content = {'subject': subject, 'message': message}
|
||||
sender_util = SendAndVerifyCodeUtil(
|
||||
self.user.email, code=code, backend=self.name, timeout=60, **content
|
||||
self.user.email, code=code, backend=self.name, **content
|
||||
)
|
||||
sender_util.gen_and_send_async()
|
||||
|
||||
@staticmethod
|
||||
def global_enabled():
|
||||
return settings.SECURITY_MFA_BY_EMAIL
|
||||
@classmethod
|
||||
def global_enabled(cls):
|
||||
return super().global_enabled and settings.SECURITY_MFA_BY_EMAIL
|
||||
|
||||
def disable(self):
|
||||
return '/ui/#/profile/index'
|
||||
|
||||
@@ -29,9 +29,10 @@ class MFAFace(BaseMFA, AuthFaceMixin):
|
||||
return True
|
||||
return bool(self.user.face_vector)
|
||||
|
||||
@staticmethod
|
||||
def global_enabled():
|
||||
@classmethod
|
||||
def global_enabled(cls):
|
||||
return (
|
||||
super().global_enabled() and
|
||||
settings.XPACK_LICENSE_IS_VALID and
|
||||
settings.XPACK_LICENSE_EDITION_ULTIMATE and
|
||||
settings.FACE_RECOGNITION_ENABLED
|
||||
|
||||
@@ -25,10 +25,6 @@ class MFAOtp(BaseMFA):
|
||||
return True
|
||||
return self.user.otp_secret_key
|
||||
|
||||
@staticmethod
|
||||
def global_enabled():
|
||||
return True
|
||||
|
||||
def get_enable_url(self) -> str:
|
||||
return reverse('authentication:user-otp-enable-start')
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ class MFAPasskey(BaseMFA):
|
||||
return False
|
||||
return self.user.passkey_set.count()
|
||||
|
||||
@staticmethod
|
||||
def global_enabled():
|
||||
return settings.AUTH_PASSKEY
|
||||
@classmethod
|
||||
def global_enabled(cls):
|
||||
return super().global_enabled() and settings.AUTH_PASSKEY
|
||||
|
||||
def get_enable_url(self) -> str:
|
||||
return '/ui/#/profile/passkeys'
|
||||
|
||||
@@ -27,9 +27,9 @@ class MFARadius(BaseMFA):
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def global_enabled():
|
||||
return settings.OTP_IN_RADIUS
|
||||
@classmethod
|
||||
def global_enabled(cls):
|
||||
return super().global_enabled() and settings.OTP_IN_RADIUS
|
||||
|
||||
def get_enable_url(self) -> str:
|
||||
return ''
|
||||
|
||||
@@ -46,9 +46,9 @@ class MFASms(BaseMFA):
|
||||
def send_challenge(self):
|
||||
self.sms.gen_and_send_async()
|
||||
|
||||
@staticmethod
|
||||
def global_enabled():
|
||||
return settings.SMS_ENABLED
|
||||
@classmethod
|
||||
def global_enabled(cls):
|
||||
return super().global_enabled() and settings.SMS_ENABLED
|
||||
|
||||
def get_enable_url(self) -> str:
|
||||
return '/ui/#/profile/index'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import inspect
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from functools import partial
|
||||
@@ -12,6 +13,7 @@ from django.contrib.auth import (
|
||||
BACKEND_SESSION_KEY, load_backend,
|
||||
PermissionDenied, user_login_failed, _clean_credentials,
|
||||
)
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.shortcuts import reverse, redirect, get_object_or_404
|
||||
@@ -46,6 +48,10 @@ def _get_backends(return_tuples=False):
|
||||
return backends
|
||||
|
||||
|
||||
class OnlyAllowExistUserAuthError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
auth._get_backends = _get_backends
|
||||
|
||||
|
||||
@@ -54,6 +60,24 @@ def authenticate(request=None, **credentials):
|
||||
If the given credentials are valid, return a User object.
|
||||
之所以 hack 这个 authenticate
|
||||
"""
|
||||
|
||||
UserModel = get_user_model()
|
||||
original_get_or_create = UserModel.objects.get_or_create
|
||||
|
||||
thread_local = threading.local()
|
||||
thread_local.thread_id = threading.get_ident()
|
||||
|
||||
def custom_get_or_create(self, *args, **kwargs):
|
||||
logger.debug(f"get_or_create: thread_id={threading.get_ident()}, username={username}")
|
||||
if threading.get_ident() != thread_local.thread_id or not settings.ONLY_ALLOW_EXIST_USER_AUTH:
|
||||
return original_get_or_create(*args, **kwargs)
|
||||
create_username = kwargs.get('username')
|
||||
try:
|
||||
UserModel.objects.get(username=create_username)
|
||||
except UserModel.DoesNotExist:
|
||||
raise OnlyAllowExistUserAuthError
|
||||
return original_get_or_create(*args, **kwargs)
|
||||
|
||||
username = credentials.get('username')
|
||||
|
||||
temp_user = None
|
||||
@@ -71,10 +95,19 @@ def authenticate(request=None, **credentials):
|
||||
# This backend doesn't accept these credentials as arguments. Try the next one.
|
||||
continue
|
||||
try:
|
||||
UserModel.objects.get_or_create = custom_get_or_create.__get__(UserModel.objects)
|
||||
user = backend.authenticate(request, **credentials)
|
||||
except PermissionDenied:
|
||||
# This backend says to stop in our tracks - this user should not be allowed in at all.
|
||||
break
|
||||
except OnlyAllowExistUserAuthError:
|
||||
request.error_message = _(
|
||||
'''The administrator has enabled "Only allow existing users to log in",
|
||||
and the current user is not in the user list. Please contact the administrator.'''
|
||||
)
|
||||
continue
|
||||
finally:
|
||||
UserModel.objects.get_or_create = original_get_or_create
|
||||
if user is None:
|
||||
continue
|
||||
|
||||
@@ -176,9 +209,9 @@ class AuthPreCheckMixin:
|
||||
if not is_block:
|
||||
return
|
||||
logger.warning('Ip was blocked' + ': ' + username + ':' + ip)
|
||||
exception = errors.BlockLoginError(username=username, ip=ip)
|
||||
exception = errors.BlockLoginError(username=username, ip=ip, request=self.request)
|
||||
if raise_exception:
|
||||
raise errors.BlockLoginError(username=username, ip=ip)
|
||||
raise exception
|
||||
else:
|
||||
return exception
|
||||
|
||||
|
||||
@@ -338,6 +338,18 @@ class ConnectionToken(JMSOrgBaseModel):
|
||||
acls = CommandFilterACL.filter_queryset(**kwargs).valid()
|
||||
return acls
|
||||
|
||||
@lazyproperty
|
||||
def data_masking_rules(self):
|
||||
from acls.models import DataMaskingRule
|
||||
kwargs = {
|
||||
'user': self.user,
|
||||
'asset': self.asset,
|
||||
'account': self.account_object,
|
||||
}
|
||||
with tmp_to_org(self.asset.org_id):
|
||||
rules = DataMaskingRule.filter_queryset(**kwargs).valid()
|
||||
return rules
|
||||
|
||||
|
||||
class SuperConnectionToken(ConnectionToken):
|
||||
_type = ConnectionTokenType.SUPER
|
||||
|
||||
@@ -3,7 +3,7 @@ from rest_framework import serializers
|
||||
|
||||
from accounts.const import SecretType
|
||||
from accounts.models import Account
|
||||
from acls.models import CommandGroup, CommandFilterACL
|
||||
from acls.models import CommandGroup, CommandFilterACL, DataMaskingRule
|
||||
from assets.models import Asset, Platform, Gateway, Zone
|
||||
from assets.serializers.asset import AssetProtocolsSerializer
|
||||
from assets.serializers.platform import PlatformSerializer
|
||||
@@ -83,6 +83,14 @@ class _ConnectionTokenGatewaySerializer(serializers.ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class _ConnectionTokenDataMaskingRuleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = DataMaskingRule
|
||||
fields = ['id', 'name', 'fields_pattern',
|
||||
'masking_method', 'mask_pattern',
|
||||
'is_active', 'priority']
|
||||
|
||||
|
||||
class _ConnectionTokenCommandFilterACLSerializer(serializers.ModelSerializer):
|
||||
command_groups = ObjectRelatedField(
|
||||
many=True, required=False, queryset=CommandGroup.objects,
|
||||
@@ -105,7 +113,7 @@ class _ConnectionTokenPlatformSerializer(PlatformSerializer):
|
||||
class Meta(PlatformSerializer.Meta):
|
||||
model = Platform
|
||||
fields = [field for field in PlatformSerializer.Meta.fields
|
||||
if field not in PlatformSerializer.Meta.fields_m2m]
|
||||
if field not in PlatformSerializer.Meta.fields_m2m]
|
||||
|
||||
def get_field_names(self, declared_fields, info):
|
||||
names = super().get_field_names(declared_fields, info)
|
||||
@@ -139,6 +147,7 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
|
||||
platform = _ConnectionTokenPlatformSerializer(read_only=True)
|
||||
zone = ObjectRelatedField(queryset=Zone.objects, required=False, label=_('Domain'))
|
||||
command_filter_acls = _ConnectionTokenCommandFilterACLSerializer(read_only=True, many=True)
|
||||
data_masking_rules = _ConnectionTokenDataMaskingRuleSerializer(read_only=True, many=True)
|
||||
expire_now = serializers.BooleanField(label=_('Expired now'), write_only=True, default=True)
|
||||
connect_method = _ConnectTokenConnectMethodSerializer(read_only=True, source='connect_method_object')
|
||||
connect_options = serializers.JSONField(read_only=True)
|
||||
@@ -149,7 +158,7 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
|
||||
model = ConnectionToken
|
||||
fields = [
|
||||
'id', 'value', 'user', 'asset', 'account',
|
||||
'platform', 'command_filter_acls', 'protocol',
|
||||
'platform', 'command_filter_acls', 'data_masking_rules', 'protocol',
|
||||
'zone', 'gateway', 'actions', 'expire_at',
|
||||
'from_ticket', 'expire_now', 'connect_method',
|
||||
'connect_options', 'face_monitor_token'
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<td style="height: 50px;">{% trans 'MFA code' %}: <span style="font-weight: bold;">{{ code }}</span></td>
|
||||
</tr>
|
||||
<tr style="border: 1px solid #eee">
|
||||
<td style="height: 30px;">{% trans 'The validity period of the verification code is one minute' %}</td>
|
||||
<td style="height: 30px;">{{ tip }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<td style="height: 30px;"> {% trans 'Copy the verification code to the Reset Password page to reset the password.' %} </td>
|
||||
</tr>
|
||||
<tr style="border: 1px solid #eee">
|
||||
<td style="height: 30px;">{% trans 'The validity period of the verification code is one minute' %}</td>
|
||||
<td style="height: 30px;">{{ tip }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -376,7 +376,7 @@
|
||||
</div>
|
||||
{% if form.challenge %}
|
||||
{% bootstrap_field form.challenge show_label=False %}
|
||||
{% elif form.mfa_type %}
|
||||
{% elif form.mfa_type and mfa_backends %}
|
||||
<div class="form-group" style="display: flex">
|
||||
{% include '_mfa_login_field.html' %}
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ from common.utils import get_logger
|
||||
from common.utils.common import get_request_ip
|
||||
from common.utils.django import reverse, get_object_or_none
|
||||
from users.models import User
|
||||
from users.signal_handlers import check_only_allow_exist_user_auth, bind_user_to_org_role
|
||||
from users.signal_handlers import bind_user_to_org_role, check_only_allow_exist_user_auth
|
||||
from .mixins import FlashMessageMixin
|
||||
|
||||
logger = get_logger(__file__)
|
||||
@@ -55,7 +55,6 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, IMClientMixin, View):
|
||||
)
|
||||
|
||||
if not check_only_allow_exist_user_auth(create):
|
||||
user.delete()
|
||||
return user, (self.msg_client_err, self.request.error_message)
|
||||
|
||||
setattr(user, f'{self.user_type}_id', user_id)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.conf import settings
|
||||
from typing import Callable
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.throttling import UserRateThrottle
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
@@ -14,8 +16,12 @@ from orgs.utils import current_org
|
||||
__all__ = ['SuggestionMixin', 'RenderToJsonMixin']
|
||||
|
||||
|
||||
class CustomUserRateThrottle(UserRateThrottle):
|
||||
rate = '60/m'
|
||||
|
||||
|
||||
class SuggestionMixin:
|
||||
suggestion_limit = 10
|
||||
suggestion_limit = settings.SUGGESTION_LIMIT
|
||||
|
||||
filter_queryset: Callable
|
||||
get_queryset: Callable
|
||||
@@ -35,6 +41,7 @@ class SuggestionMixin:
|
||||
queryset = queryset.none()
|
||||
|
||||
queryset = self.filter_queryset(queryset)
|
||||
|
||||
queryset = queryset[:self.suggestion_limit]
|
||||
page = self.paginate_queryset(queryset)
|
||||
|
||||
@@ -45,6 +52,11 @@ class SuggestionMixin:
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def get_throttles(self):
|
||||
if self.action == 'match':
|
||||
return [CustomUserRateThrottle()]
|
||||
return super().get_throttles()
|
||||
|
||||
|
||||
class RenderToJsonMixin:
|
||||
@action(methods=[POST, PUT], detail=False, url_path='render-to-json')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from rest_framework.filters import SearchFilter as SearchFilterBase
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
@@ -35,6 +36,14 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
class SearchFilter(SearchFilterBase):
|
||||
def get_search_terms(self, request):
|
||||
params = request.query_params.get(self.search_param, '') or request.query_params.get('search', '')
|
||||
params = params.replace('\x00', '') # strip null characters
|
||||
params = params.replace(',', ' ')
|
||||
return params.split()
|
||||
|
||||
|
||||
class BaseFilterSet(drf_filters.FilterSet):
|
||||
days = drf_filters.NumberFilter(method="filter_days")
|
||||
days__lt = drf_filters.NumberFilter(method="filter_days")
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import re
|
||||
import uuid
|
||||
import time
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.test import Client
|
||||
from django.urls import URLPattern, URLResolver
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
||||
from jumpserver.urls import api_v1
|
||||
|
||||
@@ -85,50 +89,262 @@ known_error_urls = [
|
||||
'/api/v1/terminal/sessions/00000000-0000-0000-0000-000000000000/replay/download/',
|
||||
]
|
||||
|
||||
# API 白名单 - 普通用户可以访问的 API
|
||||
user_accessible_urls = known_unauth_urls + [
|
||||
# 添加更多普通用户可以访问的 API
|
||||
"/api/v1/settings/public/",
|
||||
"/api/v1/users/profile/",
|
||||
"/api/v1/users/change-password/",
|
||||
"/api/v1/users/logout/",
|
||||
"/api/v1/settings/chatai-prompts/",
|
||||
"/api/v1/authentication/confirm/",
|
||||
"/api/v1/users/connection-token/",
|
||||
"/api/v1/authentication/temp-tokens/",
|
||||
"/api/v1/notifications/backends/",
|
||||
"/api/v1/authentication/passkeys/",
|
||||
"/api/v1/orgs/orgs/current/",
|
||||
"/api/v1/tickets/apply-asset-tickets/",
|
||||
"/api/v1/ops/celery/task/00000000-0000-0000-0000-000000000000/task-execution/00000000-0000-0000-0000-000000000000/log/",
|
||||
"/api/v1/assets/favorite-assets/",
|
||||
"/api/v1/authentication/connection-token/",
|
||||
"/api/v1/ops/jobs/",
|
||||
"/api/v1/assets/categories/",
|
||||
"/api/v1/tickets/tickets/",
|
||||
"/api/v1/authentication/ssh-key/",
|
||||
"/api/v1/terminal/my-sessions/",
|
||||
"/api/v1/authentication/access-keys/",
|
||||
"/api/v1/users/profile/permissions/",
|
||||
"/api/v1/tickets/apply-login-asset-tickets/",
|
||||
"/api/v1/resources/",
|
||||
"/api/v1/ops/celery/task/00000000-0000-0000-0000-000000000000/task-execution/00000000-0000-0000-0000-000000000000/result/",
|
||||
"/api/v1/notifications/site-messages/",
|
||||
"/api/v1/notifications/site-messages/unread-total/",
|
||||
"/api/v1/assets/assets/suggestions/",
|
||||
"/api/v1/search/",
|
||||
"/api/v1/notifications/user-msg-subscription/",
|
||||
"/api/v1/ops/ansible/job-execution/00000000-0000-0000-0000-000000000000/log/",
|
||||
"/api/v1/tickets/apply-login-tickets/",
|
||||
"/api/v1/ops/variables/form-data/",
|
||||
"/api/v1/ops/variables/help/",
|
||||
"/api/v1/users/profile/password/",
|
||||
"/api/v1/tickets/apply-command-tickets/",
|
||||
"/api/v1/ops/job-executions/",
|
||||
"/api/v1/audits/my-login-logs/",
|
||||
"/api/v1/terminal/components/connect-methods/"
|
||||
"/api/v1/ops/task-executions/",
|
||||
"/api/v1/terminal/sessions/online-info/",
|
||||
"/api/v1/ops/adhocs/",
|
||||
"/api/v1/tickets/apply-nodes/suggestions/",
|
||||
"/api/v1/tickets/apply-assets/suggestions/",
|
||||
"/api/v1/settings/server-info/",
|
||||
"/api/v1/ops/playbooks/",
|
||||
"/api/v1/assets/categories/types/",
|
||||
"/api/v1/assets/protocols/",
|
||||
"/api/v1/common/countries/",
|
||||
"/api/v1/audits/jobs/",
|
||||
"/api/v1/terminal/components/connect-methods/",
|
||||
"/api/v1/ops/task-executions/",
|
||||
]
|
||||
|
||||
errors = {}
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Check api if unauthorized'
|
||||
"""
|
||||
Check API authorization and user access permissions.
|
||||
|
||||
This command performs two types of checks:
|
||||
1. Anonymous access check - finds APIs that can be accessed without authentication
|
||||
2. User access check - finds APIs that can be accessed by a normal user
|
||||
|
||||
The functionality is split into two methods:
|
||||
- check_anonymous_access(): Checks for APIs accessible without authentication
|
||||
- check_user_access(): Checks for APIs accessible by a normal user
|
||||
|
||||
Usage examples:
|
||||
# Check both anonymous and user access (default behavior)
|
||||
python manage.py check_api
|
||||
|
||||
# Check only anonymous access
|
||||
python manage.py check_api --skip-user-check
|
||||
|
||||
# Check only user access
|
||||
python manage.py check_api --skip-anonymous-check
|
||||
|
||||
# Check user access and update whitelist
|
||||
python manage.py check_api --update-whitelist
|
||||
"""
|
||||
help = 'Check API authorization and user access permissions'
|
||||
password = uuid.uuid4().hex
|
||||
unauth_urls = []
|
||||
error_urls = []
|
||||
unformat_urls = []
|
||||
# 用户可以访问的 API,但不在白名单中的 API
|
||||
unexpected_access = []
|
||||
|
||||
def handle(self, *args, **options):
|
||||
settings.LOG_LEVEL = 'ERROR'
|
||||
urls = get_api_urls()
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--skip-anonymous-check',
|
||||
action='store_true',
|
||||
help='Skip anonymous access check (only check user access)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--skip-user-check',
|
||||
action='store_true',
|
||||
help='Skip user access check (only check anonymous access)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--update-whitelist',
|
||||
action='store_true',
|
||||
help='Update the user accessible URLs whitelist based on current scan results',
|
||||
)
|
||||
|
||||
def create_test_user(self):
|
||||
"""创建测试用户"""
|
||||
User = get_user_model()
|
||||
username = 'test_user_api_check'
|
||||
email = 'test@example.com'
|
||||
|
||||
# 删除可能存在的测试用户
|
||||
User.objects.filter(username=username).delete()
|
||||
|
||||
# 创建新的测试用户
|
||||
user = User.objects.create_user(
|
||||
username=username,
|
||||
email=email,
|
||||
password=self.password,
|
||||
is_active=True
|
||||
)
|
||||
return user
|
||||
|
||||
def check_user_api_access(self, urls):
|
||||
"""检查普通用户可以访问的 API"""
|
||||
user = self.create_test_user()
|
||||
client = Client()
|
||||
client.defaults['HTTP_HOST'] = 'localhost'
|
||||
unauth_urls = []
|
||||
|
||||
# 登录用户
|
||||
login_success = client.login(username=user.username, password=self.password)
|
||||
if not login_success:
|
||||
self.stdout.write(
|
||||
self.style.ERROR('Failed to login test user')
|
||||
)
|
||||
return [], []
|
||||
|
||||
accessible_urls = []
|
||||
error_urls = []
|
||||
unformat_urls = []
|
||||
|
||||
self.stdout.write('Checking user API access...')
|
||||
|
||||
for url, ourl in urls:
|
||||
if '(' in url or '<' in url:
|
||||
continue
|
||||
|
||||
try:
|
||||
response = client.get(url, follow=True)
|
||||
time.sleep(0.1)
|
||||
# 如果状态码是 200 或 201,说明用户可以访问
|
||||
if response.status_code in [200, 201]:
|
||||
accessible_urls.append((url, ourl, response.status_code))
|
||||
elif response.status_code == 403:
|
||||
# 403 表示权限不足,这是正常的
|
||||
pass
|
||||
else:
|
||||
# 其他状态码可能是错误
|
||||
error_urls.append((url, ourl, response.status_code))
|
||||
except Exception as e:
|
||||
error_urls.append((url, ourl, str(e)))
|
||||
|
||||
# 清理测试用户
|
||||
user.delete()
|
||||
|
||||
return accessible_urls, error_urls
|
||||
|
||||
def check_anonymous_access(self, urls):
|
||||
"""检查匿名访问权限"""
|
||||
client = Client()
|
||||
client.defaults['HTTP_HOST'] = 'localhost'
|
||||
|
||||
for url, ourl in urls:
|
||||
if '(' in url or '<' in url:
|
||||
unformat_urls.append([url, ourl])
|
||||
self.unformat_urls.append([url, ourl])
|
||||
continue
|
||||
|
||||
try:
|
||||
response = client.get(url, follow=True)
|
||||
if response.status_code != 401:
|
||||
errors[url] = str(response.status_code) + ' ' + str(ourl)
|
||||
unauth_urls.append(url)
|
||||
self.unauth_urls.append(url)
|
||||
except Exception as e:
|
||||
errors[url] = str(e)
|
||||
error_urls.append(url)
|
||||
self.error_urls.append(url)
|
||||
|
||||
unauth_urls = set(unauth_urls) - set(known_unauth_urls)
|
||||
print("\nUnauthorized urls:")
|
||||
if not unauth_urls:
|
||||
self.unauth_urls = set(self.unauth_urls) - set(known_unauth_urls)
|
||||
self.error_urls = set(self.error_urls)
|
||||
self.unformat_urls = set(self.unformat_urls)
|
||||
|
||||
def print_anonymous_access_result(self):
|
||||
print("\n=== Anonymous Access Check ===")
|
||||
print("Unauthorized urls:")
|
||||
if not self.unauth_urls:
|
||||
print(" Empty, very good!")
|
||||
for url in unauth_urls:
|
||||
for url in self.unauth_urls:
|
||||
print('"{}", {}'.format(url, errors.get(url, '')))
|
||||
|
||||
print("\nError urls:")
|
||||
if not error_urls:
|
||||
if not self.error_urls:
|
||||
print(" Empty, very good!")
|
||||
for url in set(error_urls):
|
||||
for url in set(self.error_urls):
|
||||
print(url, ': ' + errors.get(url))
|
||||
|
||||
print("\nUnformat urls:")
|
||||
if not unformat_urls:
|
||||
if not self.unformat_urls:
|
||||
print(" Empty, very good!")
|
||||
for url in unformat_urls:
|
||||
for url in self.unformat_urls:
|
||||
print(url)
|
||||
|
||||
def check_user_access(self, urls, update_whitelist=False):
|
||||
"""检查用户访问权限"""
|
||||
print("\n=== User Access Check ===")
|
||||
accessible_urls, user_error_urls = self.check_user_api_access(urls)
|
||||
|
||||
# 检查是否有不在白名单中的可访问 API
|
||||
accessible_url_list = [url for url, _, _ in accessible_urls]
|
||||
unexpected_access = set(accessible_url_list) - set(user_accessible_urls)
|
||||
self.unexpected_access = unexpected_access
|
||||
|
||||
# 如果启用了更新白名单选项
|
||||
if update_whitelist:
|
||||
print("\n=== Updating Whitelist ===")
|
||||
new_whitelist = sorted(set(user_accessible_urls + accessible_url_list))
|
||||
print("Updated whitelist would include:")
|
||||
for url in new_whitelist:
|
||||
print(f' "{url}",')
|
||||
print(f"\nTotal URLs in whitelist: {len(new_whitelist)}")
|
||||
|
||||
def print_user_access_result(self):
|
||||
print("\n=== User Access Check ===")
|
||||
|
||||
print("User unexpected urls:")
|
||||
if self.unexpected_access:
|
||||
print(f" Error: Found {len(self.unexpected_access)} URLs accessible by user but not in whitelist:")
|
||||
for url in self.unexpected_access:
|
||||
print(f' "{url}"')
|
||||
else:
|
||||
print(" Empty, very good!")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
settings.LOG_LEVEL = 'ERROR'
|
||||
urls = get_api_urls()
|
||||
|
||||
# 检查匿名访问权限(默认执行)
|
||||
if not options['skip_anonymous_check']:
|
||||
self.check_anonymous_access(urls)
|
||||
|
||||
# 检查用户访问权限(默认执行)
|
||||
if not options['skip_user_check']:
|
||||
self.check_user_access(urls, options['update_whitelist'])
|
||||
|
||||
print("\nCheck total urls: ", len(urls))
|
||||
self.print_anonymous_access_result()
|
||||
self.print_user_access_result()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
import datetime
|
||||
import inspect
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
|
||||
@@ -334,6 +335,10 @@ class ES(object):
|
||||
def is_keyword(props: dict, field: str) -> bool:
|
||||
return props.get(field, {}).get("type", "keyword") == "keyword"
|
||||
|
||||
@staticmethod
|
||||
def is_long(props: dict, field: str) -> bool:
|
||||
return props.get(field, {}).get("type") == "long"
|
||||
|
||||
def get_query_body(self, **kwargs):
|
||||
new_kwargs = {}
|
||||
for k, v in kwargs.items():
|
||||
@@ -361,10 +366,10 @@ class ES(object):
|
||||
if index_in_field in kwargs:
|
||||
index['values'] = kwargs[index_in_field]
|
||||
|
||||
mapping = self.es.indices.get_mapping(index=self.query_index)
|
||||
mapping = self.es.indices.get_mapping(index=self.index)
|
||||
props = (
|
||||
mapping
|
||||
.get(self.query_index, {})
|
||||
.get(self.index, {})
|
||||
.get('mappings', {})
|
||||
.get('properties', {})
|
||||
)
|
||||
@@ -375,6 +380,9 @@ class ES(object):
|
||||
if k in ("org_id", "session") and self.is_keyword(props, k):
|
||||
exact[k] = v
|
||||
|
||||
elif self.is_long(props, k):
|
||||
exact[k] = v
|
||||
|
||||
elif k in common_keyword_able:
|
||||
exact[f"{k}.keyword"] = v
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@ class FeiShu(RequestMixin):
|
||||
except Exception as e:
|
||||
logger.error(f'Get user detail error: {e} data={data}')
|
||||
|
||||
data.update(kwargs['other_info'] if 'other_info' in kwargs else {})
|
||||
info = flatten_dict(data)
|
||||
default_detail = self.default_user_detail(data, user_id)
|
||||
detail = map_attributes(default_detail, info, self.attributes)
|
||||
|
||||
@@ -207,7 +207,8 @@ class WeComTool(object):
|
||||
|
||||
def check_state(self, state, request=None):
|
||||
return cache.get(state) == self.WECOM_STATE_VALUE or \
|
||||
request.session[self.WECOM_STATE_SESSION_KEY] == state
|
||||
request.session.get(self.WECOM_STATE_SESSION_KEY) == state or \
|
||||
request.GET.get('state') == state # 在企业微信桌面端打开的话,重新创建了个 session,会导致 session 校验失败
|
||||
|
||||
def wrap_redirect_url(self, next_url):
|
||||
params = {
|
||||
|
||||
@@ -101,7 +101,7 @@ def get_ip_city(ip):
|
||||
|
||||
info = get_ip_city_by_ipip(ip)
|
||||
if info:
|
||||
city = info.get('city', _("Unknown"))
|
||||
city = info.get('city') or _("Unknown")
|
||||
country = info.get('country')
|
||||
|
||||
# 国内城市 并且 语言是中文就使用国内
|
||||
|
||||
@@ -61,8 +61,10 @@ def contains_time_period(time_periods, ctime=None):
|
||||
"""
|
||||
time_periods: [{"id": 1, "value": "00:00~07:30、10:00~13:00"}, {"id": 2, "value": "00:00~00:00"}]
|
||||
"""
|
||||
if not time_periods:
|
||||
return None
|
||||
if not time_periods or all(item['value'] == "" for item in time_periods):
|
||||
# 需要处理 [{"id":1,"value":""},{"id":2,"value":""},{"id":3,"value":""},...]情况
|
||||
# 都没选择相当于全选
|
||||
return True
|
||||
|
||||
if ctime is None:
|
||||
ctime = local_now()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,46 @@
|
||||
{
|
||||
"\t%2d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s": "",
|
||||
"%s approved": "",
|
||||
"%s node has no assets": "",
|
||||
"%s protocol client not installed.": "",
|
||||
"%s rejected": "",
|
||||
"/ + IP, Hostname, Comment": "",
|
||||
"ACL reject": "",
|
||||
"Account <%s> and asset <%s> protocol are inconsistent.": "",
|
||||
"ActionPerm": "Action Permission",
|
||||
"Address": "",
|
||||
"AlreadyExistsPleaseRename": "File already exists, please rename it",
|
||||
"Announcement: ": "",
|
||||
"Authentication failed": "",
|
||||
"AvailableShortcutKey": "Available Shortcut Key",
|
||||
"Back": "Back",
|
||||
"Back: B/b": "",
|
||||
"Cancel": "Cancel",
|
||||
"Cancel confirm": "",
|
||||
"Cancel to login asset or max 3 retry": "",
|
||||
"CancelFileUpload": "Cancel file upload",
|
||||
"CaseSensitive": "Case sensitive",
|
||||
"Clone Connect": "Clone Connect",
|
||||
"Close All Tabs": "Close All Tabs",
|
||||
"Close Current Tab": "Close Current Tab",
|
||||
"Command `%s` is forbidden": "",
|
||||
"Comment": "",
|
||||
"Confirm": "Confirm",
|
||||
"ConfirmBtn": "Confirm",
|
||||
"ConfirmDelete": "Are you sure you want to delete this file?",
|
||||
"Connect": "Connect",
|
||||
"Connect idle more than %d minutes, disconnect": "",
|
||||
"Connect with api server failed": "",
|
||||
"Connecting to %s@%s": "",
|
||||
"Connecting to Database %s": "",
|
||||
"Connecting to Kubernetes %s": "",
|
||||
"Connecting to Kubernetes %s container %s": "",
|
||||
"Connection refused": "",
|
||||
"CopyLink": "Copy Link Address and Code",
|
||||
"CopyShareURLSuccess": "Copy Share URL Success",
|
||||
"Core API failed": "",
|
||||
"Could copy website URL to notify reviewers: %s": "",
|
||||
"Create k8s client err: %s": "",
|
||||
"CreateFolder": "Create folder",
|
||||
"CreateLink": "Create link",
|
||||
"CreateSuccess": "Success",
|
||||
@@ -27,10 +53,14 @@
|
||||
"DownloadProgress": "Download progress",
|
||||
"DownloadSuccess": "Download success",
|
||||
"Downloading": "Downloading",
|
||||
"Dynamic": "",
|
||||
"EndFileTransfer": "File transfer end",
|
||||
"Enter ID number directly login, multiple search use // + field, such as: //16": "",
|
||||
"ExceedTransferSize": "exceed max transfer size",
|
||||
"Expand": "Expand",
|
||||
"ExpiredTime": "Expiration time",
|
||||
"Face ACL is not supported yet. Please use the WebTerminal to connect the asset.": "",
|
||||
"Face verification is not supported yet. Please use the WebTerminal to connect the asset.": "",
|
||||
"FailedCreateConnection": "Failed to create connection",
|
||||
"FileAlreadyExists": "File already exists",
|
||||
"FileListError": "Failed to get file list",
|
||||
@@ -41,10 +71,15 @@
|
||||
"FileUploadInterrupted": "File upload interrupted",
|
||||
"Format": "Format",
|
||||
"General": "General",
|
||||
"Get auth password failed": "",
|
||||
"GetFileManagerTokenTimeOut": "Get file manager token timeout",
|
||||
"GetShareUser": "Enter username",
|
||||
"HandleTask does not support protocol %s, please use web terminal to access": "",
|
||||
"Hostname": "",
|
||||
"Hotkeys": "Hotkeys",
|
||||
"ID": "",
|
||||
"InputVerifyCode": "Input Verify Code",
|
||||
"Invalid ID": "",
|
||||
"JoinShare": "Join Session",
|
||||
"JoinedWithSuccess": "Successfully joined",
|
||||
"KubernetesManagement": "Kubernetes management",
|
||||
@@ -53,41 +88,71 @@
|
||||
"LeftArrow": "Left arrow",
|
||||
"LinkAddr": "Link Address",
|
||||
"List": "List",
|
||||
"Manual": "",
|
||||
"MatchWholeWords": "Match whole words",
|
||||
"Minute": "Minute",
|
||||
"Minutes": "Minutes",
|
||||
"Must be auto login account for %s": "",
|
||||
"Must be unique account for %s": "",
|
||||
"Must be unique asset for %s": "",
|
||||
"MustOneFile": "Only support to select one file",
|
||||
"MustSelectOneFile": "Must select one file",
|
||||
"Name": "Name",
|
||||
"Need ACL review, continue? (y/n): ": "",
|
||||
"Need ticket confirm to execute command, already send email to the reviewers": "",
|
||||
"Need ticket confirm to login, already send email to the reviewers": "",
|
||||
"NewFolder": "New Folder",
|
||||
"No Account found.": "No Account found.",
|
||||
"No Assets": "No Assets",
|
||||
"No Databases": "No Databases",
|
||||
"No account found.": "No account found.",
|
||||
"No found asset": "No found asset",
|
||||
"No kubernetes": "No kubernetes",
|
||||
"No protocol found.": "No protocol found.",
|
||||
"No route to host": "No route to host",
|
||||
"NoActiveTerminalTabFound": "No active terminal tab found",
|
||||
"NoData": "No data",
|
||||
"NoLink": "No Link",
|
||||
"NoRunningTerminalFound": "No running terminal found",
|
||||
"Node: [ ID.Name(Asset amount) ]": "Node: [ ID.Name(Asset amount) ]",
|
||||
"OnlineUser": "Online User",
|
||||
"OperationSuccessful": "Operation successful",
|
||||
"Organization": "Organization",
|
||||
"Owner": "Owner",
|
||||
"Page up: b\tPage down: n": "Page up: b\tPage down: n",
|
||||
"Page: %d, Count: %d, Total Page: %d, Total Count: %d": "Page: %d, Count: %d, Total Page: %d, Total Count: %d",
|
||||
"Paste": "Paste",
|
||||
"PauseSession": "Pause Session",
|
||||
"Permission has expired, disconnect": "Permission has expired, disconnect",
|
||||
"PermissionDenied": "Permission denied",
|
||||
"PermissionExpired": "Permission expired",
|
||||
"PermissionValid": "Permission valid",
|
||||
"Platform": "Platform",
|
||||
"Please waiting for the reviewers to confirm command `%s`, cancel by CTRL+C or CTRL+D.": "",
|
||||
"Please waiting for the reviewers to confirm, enter q to exit. ": "Please waiting for the reviewers to confirm, enter q to exit. ",
|
||||
"PleaseInput": "Please input",
|
||||
"PleaseInputVerifyCode": "Please input verify code",
|
||||
"PrimaryUser": "Primary user",
|
||||
"Protocol": "Protocol",
|
||||
"ReadOnly": "Read Only",
|
||||
"Reconnect": "Reconnect",
|
||||
"Refresh": "Refresh",
|
||||
"Refresh done": "",
|
||||
"Remove": "Remove",
|
||||
"RemoveShareUser": "You have been removed from the shared session.",
|
||||
"RemoveUser": "Remove User",
|
||||
"Rename": "Rename",
|
||||
"ResumeSession": "Resume Session",
|
||||
"Reuse SSH connections (%s@%s) [Number of connections: %d]": "",
|
||||
"RightArrow": "Right arrow",
|
||||
"Search": "Search",
|
||||
"Search: %s": "",
|
||||
"Select account exceed max retry times.": "",
|
||||
"Select protocol exceed max retry times.": "",
|
||||
"SelectAction": "Select",
|
||||
"SelectTheme": "Select Theme",
|
||||
"Self": "Self",
|
||||
"Session max time reached, disconnect": "",
|
||||
"SessionDetail": "Session Detail",
|
||||
"SessionShare": "Session Share",
|
||||
"Settings": "Settings",
|
||||
@@ -96,18 +161,33 @@
|
||||
"ShareUser": "Share User",
|
||||
"ShareUserHelpText": "If left blank, everyone could join the session.",
|
||||
"Size": "Size",
|
||||
"Start domain gateway failed %s": "",
|
||||
"Switch language successfully": "",
|
||||
"Switched to %s": "",
|
||||
"Sync": "Sync",
|
||||
"SyncUserPreferenceFailed": "Sync user preference failed",
|
||||
"SyncUserPreferenceSuccess": "Sync user preference success",
|
||||
"Terminal does not support protocol %s, please use web terminal to access": "",
|
||||
"TerminalInstanceNotFound": "Terminal instance not found for current tab",
|
||||
"TerminalInstanceNotFoundForCurrentTab": "Terminal instance not found for current tab",
|
||||
"Terminated by admin %s": "",
|
||||
"The command '%s' requires review. Continue or not [Y/n]?": "",
|
||||
"The command you executed is risky and an alert notification will be sent to the administrator. Do you want to continue?[Y/N]": "",
|
||||
"TheCurrentTerminalInstanceWasNotFound": "The current terminal instance was not found",
|
||||
"Theme": "Theme",
|
||||
"ThemeColors": "Theme Colors",
|
||||
"ThemeConfig": "Theme",
|
||||
"Ticket Reviewers: %s": "",
|
||||
"Tips: Enter asset[%s] account ID": "",
|
||||
"Tips: Enter g+NodeID to display the host under the node, such as g1": "",
|
||||
"Tips: Enter protocol ID": "",
|
||||
"Tips: To set a default language, go to Personal Settings → Preferences on Web": "",
|
||||
"Tips: switch language by ID (Current session only)": "",
|
||||
"Transfer": "Transfer",
|
||||
"Type": "Type",
|
||||
"UnableToGenerateWebSocketURL": "Unable to generate WebSocket URL, missing parameters",
|
||||
"Unknown error code: %s, detail: %s": "",
|
||||
"Unknown status": "",
|
||||
"UpArrow": "Up arrow",
|
||||
"Upload": "Upload",
|
||||
"UploadEnd": "Upload completed, please wait for further processing",
|
||||
@@ -117,114 +197,34 @@
|
||||
"UploadTips": "Drag file here or click to upload",
|
||||
"UploadTitle": "File upload",
|
||||
"User": "User",
|
||||
"Username": "",
|
||||
"UsingRegularExpressions": "Using regular expressions",
|
||||
"VerifyCode": "Verify Code",
|
||||
"WaitFileTransfer": "Wait file transfer to finish",
|
||||
"Warning": "Warning",
|
||||
"WebSocketClosed": "WebSocket closed",
|
||||
"WebSocketConnectionIsClosedHelpText": "WebSocket connection is closed, please refresh the page or reconnect.",
|
||||
"Writable": "Writable",
|
||||
"No Databases": "",
|
||||
"No kubernetes": "",
|
||||
"Page: %d, Count: %d, Total Page: %d, Total Count: %d": "",
|
||||
"Enter ID number directly login, multiple search use // + field, such as: //16": "",
|
||||
"Page up: b\tPage down: n": "",
|
||||
"No Assets": "",
|
||||
"ID": "",
|
||||
"Address": "",
|
||||
"Platform": "",
|
||||
"Organization": "",
|
||||
"Comment": "",
|
||||
"%s protocol client not installed.": "",
|
||||
"Terminal does not support protocol %s, please use web terminal to access": "",
|
||||
"Core API failed": "",
|
||||
"ACL reject": "",
|
||||
"Face ACL is not supported yet. Please use the WebTerminal to connect the asset.": "",
|
||||
"Unknown error code: %s, detail: %s": "",
|
||||
"get connect token err": "",
|
||||
"%s node has no assets": "",
|
||||
"Welcome to use JumpServer open source fortress system": "",
|
||||
"part IP, Hostname, Comment": "",
|
||||
"to search login if unique": "",
|
||||
"/ + IP, Hostname, Comment": "",
|
||||
"to search, such as: /192.168": "",
|
||||
"display the assets you have permission": "",
|
||||
"display the node that you have permission": "",
|
||||
"display the hosts that you have permission": "",
|
||||
"display the databases that you have permission": "",
|
||||
"display the kubernetes that you have permission": "",
|
||||
"refresh your assets and nodes": "",
|
||||
"language switch": "",
|
||||
"print help": "",
|
||||
"exit": "",
|
||||
"\t%2d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s": "",
|
||||
"Announcement: ": "",
|
||||
"No Account found.": "",
|
||||
"Username": "",
|
||||
"Tips: Enter asset[%s] account ID": "",
|
||||
"Back: B/b": "",
|
||||
"Hostname": "",
|
||||
"select one asset to login": "",
|
||||
"not found matched username %s": "",
|
||||
"Face verification is not supported yet. Please use the WebTerminal to connect the asset.": "",
|
||||
"Tips: switch language by ID (Current session only)": "",
|
||||
"Tips: To set a default language, go to Personal Settings → Preferences on Web": "",
|
||||
"Invalid ID": "",
|
||||
"Switch language successfully": "",
|
||||
"Node: [ ID.Name(Asset amount) ]": "",
|
||||
"Tips: Enter g+NodeID to display the host under the node, such as g1": "",
|
||||
"Connect idle more than %d minutes, disconnect": "",
|
||||
"No account found.": "",
|
||||
"Select account exceed max retry times.": "",
|
||||
"No protocol found.": "",
|
||||
"Protocol": "",
|
||||
"Tips: Enter protocol ID": "",
|
||||
"Select protocol exceed max retry times.": "",
|
||||
"Refresh done": "",
|
||||
"Need ACL review, continue? (y/n): ": "",
|
||||
"Cancel to login asset or max 3 retry": "",
|
||||
"Need ticket confirm to login, already send email to the reviewers": "",
|
||||
"Ticket Reviewers: %s": "",
|
||||
"Could copy website URL to notify reviewers: %s": "",
|
||||
"Please waiting for the reviewers to confirm, enter q to exit. ": "",
|
||||
"Unknown status": "",
|
||||
"%s approved": "",
|
||||
"%s rejected": "",
|
||||
"Cancel confirm": "",
|
||||
"Search: %s": "",
|
||||
"Must be unique asset for %s": "",
|
||||
"Must be unique account for %s": "",
|
||||
"Must be auto login account for %s": "",
|
||||
"No found asset": "",
|
||||
"Create k8s client err: %s": "",
|
||||
"have no permission to upload file": "",
|
||||
"The command you executed is risky and an alert notification will be sent to the administrator. Do you want to continue?[Y/N]": "",
|
||||
"The command '%s' requires review. Continue or not [Y/n]?": "",
|
||||
"Command `%s` is forbidden": "",
|
||||
"have no permission to download file": "",
|
||||
"Please waiting for the reviewers to confirm command `%s`, cancel by CTRL+C or CTRL+D.": "",
|
||||
"Need ticket confirm to execute command, already send email to the reviewers": "",
|
||||
"HandleTask does not support protocol %s, please use web terminal to access": "",
|
||||
"Account <%s> and asset <%s> protocol are inconsistent.": "",
|
||||
"Writable": "Writable",
|
||||
"You don't have permission login %s": "",
|
||||
"You get auth token failed": "",
|
||||
"Get auth password failed": "",
|
||||
"Reuse SSH connections (%s@%s) [Number of connections: %d]": "",
|
||||
"Switched to %s": "",
|
||||
"Connect with api server failed": "",
|
||||
"Start domain gateway failed %s": "",
|
||||
"Manual": "",
|
||||
"Dynamic": "",
|
||||
"Connecting to %s@%s": "",
|
||||
"Connecting to Database %s": "",
|
||||
"Connecting to Kubernetes %s": "",
|
||||
"Connecting to Kubernetes %s container %s": "",
|
||||
"Session max time reached, disconnect": "",
|
||||
"Permission has expired, disconnect": "",
|
||||
"Terminated by admin %s": "",
|
||||
"Authentication failed": "",
|
||||
"Connection refused": "",
|
||||
"display the assets you have permission": "",
|
||||
"display the databases that you have permission": "",
|
||||
"display the hosts that you have permission": "",
|
||||
"display the kubernetes that you have permission": "",
|
||||
"display the node that you have permission": "",
|
||||
"exit": "",
|
||||
"get connect token err": "",
|
||||
"have no permission to download file": "",
|
||||
"have no permission to upload file": "",
|
||||
"i/o timeout": "",
|
||||
"No route to host": "",
|
||||
"network is unreachable": ""
|
||||
"language switch": "",
|
||||
"network is unreachable": "",
|
||||
"not found matched username %s": "",
|
||||
"part IP, Hostname, Comment": "",
|
||||
"print help": "",
|
||||
"refresh your assets and nodes": "",
|
||||
"select one asset to login": "",
|
||||
"to search login if unique": "",
|
||||
"to search, such as: /192.168": ""
|
||||
}
|
||||
@@ -1,47 +1,47 @@
|
||||
{
|
||||
"\t%2d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s": "%d) Введите {{.GreenBoldColor}}%s{{.ColorEnd}} для %s.%s",
|
||||
"%s approved": "%s审核通过",
|
||||
"%s node has no assets": "У узла %s нет активов",
|
||||
"\t%2d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s": "\t%d) Введите {{.GreenBoldColor}}%s{{.ColorEnd}} чтобы %s.%s",
|
||||
"%s approved": "%s одобрено",
|
||||
"%s node has no assets": "В папке %s нет активов",
|
||||
"%s protocol client not installed.": "Клиент протокола %s не установлен",
|
||||
"%s rejected": "%s Проверка отклонена",
|
||||
"ACL reject": "Доступ для данного логина отклонен из-за ограничений политики доступа",
|
||||
"Account <%s> and asset <%s> protocol are inconsistent.": "Системный пользователь <%s> и актив < %s> не согласованы",
|
||||
"ActionPerm": "Права доступа",
|
||||
"%s rejected": "%s отклонено",
|
||||
"ACL reject": "Вход запрещен: ограничение политики контроля доступа",
|
||||
"Account <%s> and asset <%s> protocol are inconsistent.": "Протокол системного пользователя <%s> и актива <%s> не совпадают",
|
||||
"ActionPerm": "Разрешения на действия",
|
||||
"Address": "Адрес",
|
||||
"AlreadyExistsPleaseRename": "Создать папку",
|
||||
"Announcement: ": "Объявление:",
|
||||
"Authentication failed": "Ошибка аутентификации (неправильное имя пользователя или пароль)",
|
||||
"AlreadyExistsPleaseRename": "Файл уже существует, пожалуйста, переименуйте его",
|
||||
"Announcement: ": "Объявление: ",
|
||||
"Authentication failed": "Ошибка аутентификации: неверное имя пользователя или пароль",
|
||||
"AvailableShortcutKey": "Доступные горячие клавиши",
|
||||
"Back": "Вернуться",
|
||||
"Back: B/b": "Возврат: B/b",
|
||||
"Back": "Назад",
|
||||
"Back: B/b": "Назад: B/b",
|
||||
"Cancel": "Отмена",
|
||||
"Cancel confirm": "Отменить проверку входа",
|
||||
"Cancel to login asset or max 3 retry": "Отмена входа в актив или достижение 3-х попыток",
|
||||
"Cancel confirm": "Отмена подтверждения входа",
|
||||
"Cancel to login asset or max 3 retry": "Вход на актив отменён или превышен лимит 3 попыток",
|
||||
"CancelFileUpload": "Отменить передачу файла",
|
||||
"CaseSensitive": "Учитывать регистрар",
|
||||
"CaseSensitive": "Учитывать регистр",
|
||||
"Clone Connect": "Клонировать окно",
|
||||
"Close All Tabs": "Закрыть все вкладки",
|
||||
"Close Current Tab": "Закрыть эту вкладку",
|
||||
"Command `%s` is forbidden": "Команда `%s` запрещена ...",
|
||||
"Command `%s` is forbidden": "Команда %s запрещена",
|
||||
"Comment": "Примечание",
|
||||
"Confirm": "Подтвердить",
|
||||
"ConfirmBtn": "Подтвердить",
|
||||
"ConfirmDelete": "Вы уверены, что хотите удалить этот файл?",
|
||||
"ConfirmDelete": "Вы действительно хотите удалить этот файл?",
|
||||
"Connect": "Подключение",
|
||||
"Connect idle more than %d minutes, disconnect": "Время простоя превысило %d минут, соединение разорвано",
|
||||
"Connect with api server failed": "Не удалось подключиться к API сервису",
|
||||
"Connecting to %s@%s": "Начать подключение к %s@%s",
|
||||
"Connecting to Database %s": "Начинаю подключение к базе данных %s",
|
||||
"Connecting to Kubernetes %s": "Начало подключения к Kubernetes %s",
|
||||
"Connecting to Kubernetes %s container %s": "Начинаю подключение к Kubernetes %s контейнеру %s",
|
||||
"Connect idle more than %d minutes, disconnect": "Превышено время простоя (%d минут). Соединение разорвано",
|
||||
"Connect with api server failed": "Не удалось подключиться к API-серверу",
|
||||
"Connecting to %s@%s": "Подключение к %s@%s…",
|
||||
"Connecting to Database %s": "Подключение к базе данных %s...",
|
||||
"Connecting to Kubernetes %s": "Подключение к Kubernetes %s...",
|
||||
"Connecting to Kubernetes %s container %s": "Подключение к Kubernetes %s, контейнер %s…",
|
||||
"Connection refused": "Нет соединения (соединение отклонено)",
|
||||
"CopyLink": "Скопировать ссылку и код подтверждения",
|
||||
"CopyLink": "Копировать ссылку и код",
|
||||
"CopyShareURLSuccess": "URL общего доступа успешно скопирован",
|
||||
"Core API failed": "Произошла ошибка Core API",
|
||||
"Could copy website URL to notify reviewers: %s": "Копируемый адрес для аудита, уведомите аудитора: %s",
|
||||
"Core API failed": "Ошибка Core API",
|
||||
"Could copy website URL to notify reviewers: %s": "Можно скопировать URL для проверки и уведомить проверяющего: %s",
|
||||
"Create k8s client err: %s": "Ошибка создания клиента k8s: %s",
|
||||
"CreateFolder": "Пользователи для分享",
|
||||
"CreateLink": "Создать ссылку",
|
||||
"CreateFolder": "Создать папку",
|
||||
"CreateLink": "Создать ссылку для общего доступа",
|
||||
"CreateSuccess": "Успешно создано",
|
||||
"CurrentUser": "Текущий пользователь",
|
||||
"Custom Setting": "Пользовательские настройки",
|
||||
@@ -49,92 +49,92 @@
|
||||
"Delete": "Удалить",
|
||||
"DownArrow": "Стрелка вниз",
|
||||
"Download": "Скачать",
|
||||
"DownloadProgress": "Прогресс загрузки",
|
||||
"DownloadProgress": "Прогресс скачивания",
|
||||
"DownloadSuccess": "Успешное скачивание",
|
||||
"Downloading": "Ведётся загрузка",
|
||||
"Dynamic": "Динамический аккаунт",
|
||||
"Downloading": "Скачивается",
|
||||
"Dynamic": "Своя учетная запись",
|
||||
"EndFileTransfer": "Передача файла завершена",
|
||||
"Enter ID number directly login, multiple search use // + field, such as: //16": "Подсказка: вводите ID актива для прямого входа, для второго уровня поиска используйте // + поле, например: //192",
|
||||
"Enter ID number directly login, multiple search use // + field, such as: //16": "Подсказка: введите ID ресурса для прямого входа, для расширенного поиска используйте // + значение, например: //192",
|
||||
"ExceedTransferSize": "Превышен максимальный размер передачи",
|
||||
"Expand": "Развернуть",
|
||||
"ExpiredTime": "Срок действия",
|
||||
"Face ACL is not supported yet. Please use the WebTerminal to connect the asset.": "Данный терминал не поддерживает правила доступа по лицу, пожалуйста, войдите через веб-терминал",
|
||||
"Face verification is not supported yet. Please use the WebTerminal to connect the asset.": "Этот терминал не поддерживает аутентификацию по распознаванию лиц, пожалуйста, выполните вход через веб-терминал.",
|
||||
"FailedCreateConnection": "Ошибка создания соединения",
|
||||
"FileAlreadyExists": "Только для чтения",
|
||||
"FileListError": "Не удалось получить информацию о списке файлов",
|
||||
"Face ACL is not supported yet. Please use the WebTerminal to connect the asset.": "Этот терминал пока не поддерживает правила доступа по лицу, пожалуйста, войдите через веб-терминал",
|
||||
"Face verification is not supported yet. Please use the WebTerminal to connect the asset.": "Этот терминал не поддерживает аутентификацию по лицу, пожалуйста, войдите через веб-терминал",
|
||||
"FailedCreateConnection": "Не удалось создать соединение",
|
||||
"FileAlreadyExists": "Файл уже существует",
|
||||
"FileListError": "Не удалось получить список файлов",
|
||||
"FileManagement": "Управление файлами",
|
||||
"FileManagementExpired": "Текущая сессия управления файлами истекла.",
|
||||
"FileManagerTokenTimeout": "Получить токен файлового менеджера, время ожидания истекло",
|
||||
"FileManagerTokenTimeout": "Тайм-аут при получении токена файлового менеджера",
|
||||
"FileTransferInterrupted": "Передача файлов прервана",
|
||||
"FileUploadInterrupted": "Передача файла прервана",
|
||||
"FileUploadInterrupted": "Загрузка файла прервана",
|
||||
"Format": "Формат",
|
||||
"General": "Универсальный",
|
||||
"Get auth password failed": "Вы не смогли получить токен аутентификации",
|
||||
"GetFileManagerTokenTimeOut": "Таймаут получения токена для управления файлами",
|
||||
"General": "Основной",
|
||||
"Get auth password failed": "Не удалось получить пароль аутентификации",
|
||||
"GetFileManagerTokenTimeOut": "Тайм-аут при получении токена управления файлами",
|
||||
"GetShareUser": "Введите имя пользователя",
|
||||
"HandleTask does not support protocol %s, please use web terminal to access": "Этот терминал не поддерживает %s протокол, пожалуйста, войдите через веб-терминал",
|
||||
"HandleTask does not support protocol %s, please use web terminal to access": "Протокол %s не поддерживается этим терминалом. Используйте веб-терминал",
|
||||
"Hostname": "Имя хоста",
|
||||
"Hotkeys": "Горячие клавиши",
|
||||
"ID": "ID",
|
||||
"InputVerifyCode": "Введите код подтверждения",
|
||||
"Invalid ID": "Недействительный ID",
|
||||
"JoinShare": "Присоединиться к общей сессии",
|
||||
"Invalid ID": "Неверный ID",
|
||||
"JoinShare": "Присоединился к общей сессии",
|
||||
"JoinedWithSuccess": "Успешно присоединился",
|
||||
"KubernetesManagement": "Управление Kubernetes",
|
||||
"LastModified": "Последнее время изменения",
|
||||
"LeaveShare": "Покинуть общую сессию",
|
||||
"LeaveShare": "Покинул общую сессию",
|
||||
"LeftArrow": "Стрелка влево",
|
||||
"LinkAddr": "Адрес ссылки",
|
||||
"List": "Список",
|
||||
"Manual": "Ручной аккаунт",
|
||||
"MatchWholeWords": "Полное совпадение",
|
||||
"Minute": "Минуты",
|
||||
"Minutes": "Файл уже существует, пожалуйста, переименуйте",
|
||||
"Must be auto login account for %s": "Должен быть аккаунт для автоматического входа %s",
|
||||
"Must be unique account for %s": "Должен быть уникальный аккаунт %s",
|
||||
"Must be unique asset for %s": "должен быть уникальным активом %s",
|
||||
"Manual": "Ручной ввод",
|
||||
"MatchWholeWords": "Точное совпадение",
|
||||
"Minute": "Минута",
|
||||
"Minutes": "Минут",
|
||||
"Must be auto login account for %s": "УЗ %s должна быть учётной записью с автоматическим входом",
|
||||
"Must be unique account for %s": "УЗ %s должна быть уникальной",
|
||||
"Must be unique asset for %s": "Актив %s должен быть уникальным",
|
||||
"MustOneFile": "Можно выбрать только один файл",
|
||||
"MustSelectOneFile": "Необходимо выбрать файл",
|
||||
"Name": "Название",
|
||||
"Need ACL review, continue? (y/n): ": "требует проверки, продолжить? (y/n):",
|
||||
"Need ticket confirm to execute command, already send email to the reviewers": "Требуется команда рабочего задания для выполнения проверки, уведомление отправлено проверяющему",
|
||||
"Need ticket confirm to login, already send email to the reviewers": "Требуется вход в систему через заявку, уведомление отправлено аудитору",
|
||||
"Name": "Имя",
|
||||
"Need ACL review, continue? (y/n): ": "Требуется проверка правил доступа. Продолжить? (y/n):",
|
||||
"Need ticket confirm to execute command, already send email to the reviewers": "Выполнение команды требует проверки по заявке. Проверяющему отправлено письмо",
|
||||
"Need ticket confirm to login, already send email to the reviewers": "Требуется вход в систему через заявку, уведомление отправлено утверждающему",
|
||||
"NewFolder": "Создать папку",
|
||||
"No Account found.": "Учетная запись не найдена",
|
||||
"No Assets": "Активов нет",
|
||||
"No Databases": "Нет базы данных",
|
||||
"No found asset": "Не найдено подходящего актива %s",
|
||||
"No kubernetes": "Kubernetes отсутствует",
|
||||
"No Assets": "Нет активов",
|
||||
"No Databases": "База данных отсутствует",
|
||||
"No found asset": "Совпадений для актива %s не найдено",
|
||||
"No kubernetes": "Kubernetes не найден",
|
||||
"No protocol found.": "Нет протокола",
|
||||
"No route to host": "Проблемы с сетью (маршрутизация недоступна)",
|
||||
"No route to host": "Нет соединения (маршрут недоступен)",
|
||||
"NoActiveTerminalTabFound": "Активная вкладка терминала не найдена",
|
||||
"NoData": "Нет данных",
|
||||
"NoLink": "Без адреса",
|
||||
"NoRunningTerminalFound": "Не найдено работающего терминала",
|
||||
"Node: [ ID.Name(Asset amount) ]": "Узлы: [ ID.Имя(Количество активов) ]",
|
||||
"OnlineUser": "Онлайн пользователи",
|
||||
"NoRunningTerminalFound": "Активный терминал не найден",
|
||||
"Node: [ ID.Name(Asset amount) ]": "Папка: [ID.Название(кол-во активов)]",
|
||||
"OnlineUser": "Пользователи онлайн",
|
||||
"OperationSuccessful": "Операция выполнена успешно",
|
||||
"Organization": "Организация",
|
||||
"Owner": "Администратор",
|
||||
"Owner": "Владелец",
|
||||
"Page up: b\tPage down: n": "Предыдущая страница: b Следующая страница: n",
|
||||
"Page: %d, Count: %d, Total Page: %d, Total Count: %d": "Номер страницы: %d, количество строк на странице: %d, общее количество страниц: %d, общее количество: %d",
|
||||
"Page: %d, Count: %d, Total Page: %d, Total Count: %d": "Страница: %d, строк на странице: %d, всего страниц: %d, всего записей: %d",
|
||||
"Paste": "Вставить",
|
||||
"PauseSession": "Приостановить сессию",
|
||||
"PermissionDenied": "Нет разрешения",
|
||||
"PermissionDenied": "Доступ запрещен",
|
||||
"PermissionExpired": "Разрешение истекло",
|
||||
"PermissionValid": "Доступ разрешен",
|
||||
"Platform": "Платформа",
|
||||
"Please waiting for the reviewers to confirm command `%s`, cancel by CTRL+C or CTRL+D.": "Пожалуйста, дождитесь проверки команды проверяющим `%s`, отмените, нажав CTRL+C или CTRL+D.",
|
||||
"Please waiting for the reviewers to confirm, enter q to exit. ": "Ожидание подтверждения от проверяющего, нажмите q и Enter для отмены входа.",
|
||||
"Please waiting for the reviewers to confirm command `%s`, cancel by CTRL+C or CTRL+D.": "Пожалуйста, дождитесь проверки команды %s утверждающим. Для отмены нажмите CTRL+C или CTRL+D.",
|
||||
"Please waiting for the reviewers to confirm, enter q to exit. ": "Ждём подтверждения проверяющего. q + Enter — отмена входа.",
|
||||
"PleaseInput": "Пожалуйста, введите",
|
||||
"PleaseInputVerifyCode": "Введите код безопасности",
|
||||
"PrimaryUser": "Главный пользователь",
|
||||
"PleaseInputVerifyCode": "Пожалуйста, введите код подтверждения",
|
||||
"PrimaryUser": "Основной пользователь",
|
||||
"Protocol": "Протокол",
|
||||
"ReadOnly": "Минуты",
|
||||
"ReadOnly": "Только просмотр",
|
||||
"Reconnect": "Переподключение",
|
||||
"Refresh": "Обновить",
|
||||
"Refresh done": "Обновление завершено",
|
||||
"Refresh done": "Обновлено",
|
||||
"Remove": "Удалить",
|
||||
"RemoveShareUser": "Вас удалили из общей сессии",
|
||||
"RemoveUser": "Удалить пользователя",
|
||||
@@ -144,18 +144,18 @@
|
||||
"RightArrow": "Стрелка вправо",
|
||||
"Search": "Поиск",
|
||||
"Search: %s": "Поиск: %s",
|
||||
"Select account exceed max retry times.": "Вы выбрали аккаунт, превышающий максимальное количество попыток",
|
||||
"Select account exceed max retry times.": "Превышено максимальное число попыток выбора УЗ",
|
||||
"Select protocol exceed max retry times.": "Превышено максимальное количество попыток выбора протокола",
|
||||
"SelectAction": "Выберите действие",
|
||||
"SelectTheme": "Выберите тему",
|
||||
"Self": "Я",
|
||||
"Session max time reached, disconnect": "Сессия превысила максимальное время соединения, отключение",
|
||||
"SessionDetail": "Детали разговора",
|
||||
"Session max time reached, disconnect": "Превышено максимальное время сессии. Соединение разорвано",
|
||||
"SessionDetail": "Детали сессии",
|
||||
"SessionShare": "Поделиться сессией",
|
||||
"Settings": "Настройки",
|
||||
"Share": "Поделиться",
|
||||
"ShareLink": "Поделиться ссылкой",
|
||||
"ShareUser": "Поделиться пользователем",
|
||||
"ShareUser": "Поделиться с",
|
||||
"ShareUserHelpText": "Пустое поле означает, что присоединиться может каждый желающий.",
|
||||
"Size": "Размер",
|
||||
"Start domain gateway failed %s": "Не удалось запустить шлюз базы данных %s",
|
||||
@@ -164,30 +164,30 @@
|
||||
"Sync": "Синхронизация",
|
||||
"SyncUserPreferenceFailed": "Ошибка синхронизации настроек",
|
||||
"SyncUserPreferenceSuccess": "Настройки успешно синхронизированы",
|
||||
"Terminal does not support protocol %s, please use web terminal to access": "Данный терминал не поддерживает протокол %s, пожалуйста, войдите через веб-терминал",
|
||||
"TerminalInstanceNotFound": "Не найден экземпляр терминала для текущей вкладки",
|
||||
"TerminalInstanceNotFoundForCurrentTab": "Текущая вкладка не найдена экземпляр терминала",
|
||||
"The command '%s' requires review. Continue or not [Y/n]?": "Команда %s требует проверки, продолжить? [Y/N]",
|
||||
"The command you executed is risky and an alert notification will be sent to the administrator. Do you want to continue?[Y/N]": "Ваша команда содержит риски, уведомление будет отправлено администратору. Продолжить? [Y/N]",
|
||||
"TheCurrentTerminalInstanceWasNotFound": "Текущий экземпляр терминала не найден.",
|
||||
"Terminal does not support protocol %s, please use web terminal to access": "Этот терминал не поддерживает протокол %s, пожалуйста, войдите через веб-терминал",
|
||||
"TerminalInstanceNotFound": "Экземпляр терминала не найден",
|
||||
"TerminalInstanceNotFoundForCurrentTab": "Экземпляр терминала не найден в текущей вкладке",
|
||||
"The command '%s' requires review. Continue or not [Y/n]?": "Команда %s требует проверки. Продолжить? [Y/N]",
|
||||
"The command you executed is risky and an alert notification will be sent to the administrator. Do you want to continue?[Y/N]": "Выполняемая вами команда несёт риск, уведомление будет отправлено администратору. Продолжить? [Y/N]",
|
||||
"TheCurrentTerminalInstanceWasNotFound": "Текущий экземпляр терминала не найден",
|
||||
"Theme": "Тема",
|
||||
"ThemeColors": "Цвета темы",
|
||||
"ThemeConfig": "Настройки темы",
|
||||
"Ticket Reviewers: %s": "Проверяющий рабочей заявки: %s",
|
||||
"Tips: Enter asset[%s] account ID": "Подсказка: Введите ID аккаунта актива [%s]",
|
||||
"Tips: Enter g+NodeID to display the host under the node, such as g1": "Подсказка: введите g+ID узла для отображения хостов в узле, например: g1",
|
||||
"Ticket Reviewers: %s": "Утверждающий заявки: %s",
|
||||
"Tips: Enter asset[%s] account ID": "Подсказка: Введите ID учетной записи актива [%s]",
|
||||
"Tips: Enter g+NodeID to display the host under the node, such as g1": "Подсказка: введите g+ID папки, чтобы показать хосты внутри, например: g1",
|
||||
"Tips: Enter protocol ID": "Подсказка: введите ID протокола",
|
||||
"Tips: To set a default language, go to Personal Settings → Preferences on Web": "Подсказка: если хотите установить язык по умолчанию, перейдите на веб-версию в «Личные настройки → Предпочтения»",
|
||||
"Tips: switch language by ID (Current session only)": "Подсказка: введите ID для смены языка",
|
||||
"Tips: To set a default language, go to Personal Settings → Preferences on Web": "Подсказка: если хотите установить язык по умолчанию, перейдите в веб-версию в «Личные настройки → Предпочтения»",
|
||||
"Tips: switch language by ID (Current session only)": "Подсказка: введите ID, чтобы переключить язык (только для этой сессии)",
|
||||
"Transfer": "Передача",
|
||||
"Type": "Тип",
|
||||
"UnableToGenerateWebSocketURL": "Не удалось сгенерировать WebSocket URL, отсутствуют параметры",
|
||||
"UnableToGenerateWebSocketURL": "Не удалось создать URL WebSocket — отсутствуют параметры",
|
||||
"Unknown error code: %s, detail: %s": "Неизвестный код ошибки: %s, подробности: %s",
|
||||
"Unknown status": "Неизвестное состояние",
|
||||
"UpArrow": "Кнопка вверх",
|
||||
"Upload": "Загрузить",
|
||||
"UploadEnd": "Загрузка завершена, пожалуйста, подождите дальнейшей обработки",
|
||||
"UploadProgress": "Прогресс передачи",
|
||||
"UploadProgress": "Прогресс загрузки",
|
||||
"UploadStart": "Загрузка началась",
|
||||
"UploadSuccess": "Загрузка успешно завершена",
|
||||
"UploadTips": "Перетащите файл сюда или нажмите для загрузки",
|
||||
@@ -200,28 +200,28 @@
|
||||
"Warning": "Предупреждение",
|
||||
"WebSocketClosed": "WebSocket закрыт",
|
||||
"WebSocketConnectionIsClosedHelpText": "WebSocket соединение закрыто, пожалуйста, обновите страницу или переподключитесь.",
|
||||
"Welcome to use JumpServer open source fortress system": "Добро пожаловать в открытую систему JumpServer Bastion Host",
|
||||
"Writable": "Создать ссылку для分享",
|
||||
"You don't have permission login %s": "У вас нет полномочий для входа в %s",
|
||||
"You get auth token failed": "Вы не смогли получить токен аутентификации",
|
||||
"display the assets you have permission": "Показать активы, к которым у вас есть доступ",
|
||||
"display the databases that you have permission": "Показать базы данных, к которым у вас есть доступ",
|
||||
"display the hosts that you have permission": "Показать хосты, к которым у вас есть доступ",
|
||||
"display the kubernetes that you have permission": "Показать доступные вам Kubernetes",
|
||||
"display the node that you have permission": "Показать узлы, к которым у вас есть доступ",
|
||||
"exit": "Выход",
|
||||
"Welcome to use JumpServer open source fortress system": "Добро пожаловать в JumpServer",
|
||||
"Writable": "Просмотр и управление",
|
||||
"You don't have permission login %s": "У вас нет прав на вход в %s",
|
||||
"You get auth token failed": "Не удалось получить токен аутентификации",
|
||||
"display the assets you have permission": "посмотреть активы, к которым у вас есть доступ",
|
||||
"display the databases that you have permission": "посмотреть базы данных, к которым у вас есть доступ",
|
||||
"display the hosts that you have permission": "посмотреть хосты, к которым у вас есть доступ",
|
||||
"display the kubernetes that you have permission": "посмотреть доступные вам Kubernetes",
|
||||
"display the node that you have permission": "посмотреть папки, к которым у вас есть доступ",
|
||||
"exit": "выйти",
|
||||
"get connect token err": "Ошибка получения токена подключения",
|
||||
"have no permission to download file": "Нет прав для загрузки файла",
|
||||
"have no permission to upload file": "Нет прав для загрузки файла",
|
||||
"i/o timeout": "Проблемы с сетью (время соединения истекло)",
|
||||
"language switch": "Переключение языка",
|
||||
"network is unreachable": "Сеть недоступна (невозможно достичь сети)",
|
||||
"not found matched username %s": "Не найдено соответствующее имя пользователя %s",
|
||||
"part IP, Hostname, Comment": "Частичные IP-адреса, имена хостов, заметки",
|
||||
"part IP,, Hostname, Comment": "/ + IP, имя хоста, примечание",
|
||||
"print help": "Показать помощь",
|
||||
"refresh your assets and nodes": "Обновить актуальную информацию о машинах и узлах",
|
||||
"select one asset to login": "Выберите один из активов для входа в систему",
|
||||
"to search login if unique": "Поиск входа (если единственный)",
|
||||
"to search, such as: /192.168": "Поиск, например: /192.168"
|
||||
"have no permission to download file": "Нет прав на скачивание файлов",
|
||||
"have no permission to upload file": "Нет прав на загрузку файлов",
|
||||
"i/o timeout": "Нет соединения (тайм-аут соединения)",
|
||||
"language switch": "сменить язык",
|
||||
"network is unreachable": "Нет соединения (сеть недоступна)",
|
||||
"not found matched username %s": "совпадений для УЗ %s не найдено",
|
||||
"part IP, Hostname, Comment": "часть IP, имя хоста или примечание",
|
||||
"part IP,, Hostname, Comment": "/ + IP, имя хоста или примечание",
|
||||
"print help": "посмотреть помощь",
|
||||
"refresh your assets and nodes": "обновить информацию об активах и папках",
|
||||
"select one asset to login": "выберите один из активов для входа",
|
||||
"to search login if unique": "найти подключение (если результат уникальный)",
|
||||
"to search, such as: /192.168": "выполнить поиск, например: /192.168"
|
||||
}
|
||||
@@ -1,20 +1,45 @@
|
||||
{
|
||||
"\t%2d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s": "\t%d) 输入 {{.GreenBoldColor}}%s{{.ColorEnd}} 进行%s.%s",
|
||||
"%s approved": "%s 审核通过",
|
||||
"%s node has no assets": "%s节点没有资产",
|
||||
"%s protocol client not installed.": "%s 协议的客户端未安装",
|
||||
"%s rejected": "%s 审核拒绝",
|
||||
"ACL reject": "本次登录已拒绝,原因是访问控制策略的限制",
|
||||
"Account <%s> and asset <%s> protocol are inconsistent.": "系统用户<%s>和资产<%s>协议不一致",
|
||||
"ActionPerm": "操作权限",
|
||||
"Address": "地址",
|
||||
"AlreadyExistsPleaseRename": "文件已存在,请重命名",
|
||||
"Announcement: ": "公告:",
|
||||
"Authentication failed": "认证失败(用户名或密码错误)",
|
||||
"AvailableShortcutKey": "可用快捷键",
|
||||
"Back": "返回",
|
||||
"Back: B/b": "返回:B/b",
|
||||
"Cancel": "取消",
|
||||
"Cancel confirm": "取消登录复核",
|
||||
"Cancel to login asset or max 3 retry": "取消登录资产或达到3次重试",
|
||||
"CancelFileUpload": "取消文件上传",
|
||||
"CaseSensitive": "区分大小写",
|
||||
"Clone Connect": "复制窗口",
|
||||
"Close All Tabs": "关闭所有",
|
||||
"Close Current Tab": "关闭当前",
|
||||
"Command `%s` is forbidden": "命令 `%s` 是被禁止的 ...",
|
||||
"Comment": "备注",
|
||||
"Confirm": "确认",
|
||||
"ConfirmBtn": "确定",
|
||||
"ConfirmDelete": "您确定要删除该文件吗?",
|
||||
"Connect": "连接",
|
||||
"Connect idle more than %d minutes, disconnect": "空闲时间超过%d分钟,断开连接",
|
||||
"Connect with api server failed": "连接API服务失败",
|
||||
"Connecting to %s@%s": "开始连接到 %s@%s",
|
||||
"Connecting to Database %s": "开始连接数据库 %s",
|
||||
"Connecting to Kubernetes %s": "开始连接Kubernetes %s",
|
||||
"Connecting to Kubernetes %s container %s": "开始连接Kubernetes %s 容器 %s",
|
||||
"Connection refused": "网络不通(连接拒绝)",
|
||||
"CopyLink": "复制链接及验证码",
|
||||
"CopyShareURLSuccess": "复制分享地址成功",
|
||||
"Core API failed": "Core API 发生错误",
|
||||
"Could copy website URL to notify reviewers: %s": "可复制审核地址,通知审核人:%s",
|
||||
"Create k8s client err: %s": "创建 k8s 客户端错误:%s",
|
||||
"CreateFolder": "创建文件夹",
|
||||
"CreateLink": "创建链接",
|
||||
"CreateSuccess": "创建成功",
|
||||
@@ -27,10 +52,14 @@
|
||||
"DownloadProgress": "下载进度",
|
||||
"DownloadSuccess": "下载成功",
|
||||
"Downloading": "正在下载",
|
||||
"Dynamic": "动态账号",
|
||||
"EndFileTransfer": "文件传输结束",
|
||||
"Enter ID number directly login, multiple search use // + field, such as: //16": "提示:输入资产ID直接登录,二级搜索使用 // + 字段,如://192",
|
||||
"ExceedTransferSize": "超过最大传输大小",
|
||||
"Expand": "展开",
|
||||
"ExpiredTime": "有效期限",
|
||||
"Face ACL is not supported yet. Please use the WebTerminal to connect the asset.": "该终端不支持人脸访问规则,请使用web终端登录",
|
||||
"Face verification is not supported yet. Please use the WebTerminal to connect the asset.": "该终端不支持人脸识别认证,请使用web终端登录",
|
||||
"FailedCreateConnection": "创建连接失败",
|
||||
"FileAlreadyExists": "文件已存在",
|
||||
"FileListError": "获取文件列表信息失败",
|
||||
@@ -41,10 +70,15 @@
|
||||
"FileUploadInterrupted": "文件上传中断",
|
||||
"Format": "格式",
|
||||
"General": "通用",
|
||||
"Get auth password failed": "你获取认证令牌失败",
|
||||
"GetFileManagerTokenTimeOut": "获取文件管理 Token 超时",
|
||||
"GetShareUser": "输入用户名",
|
||||
"HandleTask does not support protocol %s, please use web terminal to access": "该终端不支持 %s 协议,请使用web终端登录",
|
||||
"Hostname": "主机名",
|
||||
"Hotkeys": "快捷键",
|
||||
"ID": "ID",
|
||||
"InputVerifyCode": "请输入验证码",
|
||||
"Invalid ID": "无效 ID",
|
||||
"JoinShare": "加入共享",
|
||||
"JoinedWithSuccess": "已成功加入",
|
||||
"KubernetesManagement": "Kubernetes 管理",
|
||||
@@ -53,41 +87,69 @@
|
||||
"LeftArrow": "后退箭头",
|
||||
"LinkAddr": "链接地址",
|
||||
"List": "列表",
|
||||
"Manual": "手动账号",
|
||||
"MatchWholeWords": "全字匹配",
|
||||
"Minute": "分钟",
|
||||
"Minutes": "分",
|
||||
"Must be auto login account for %s": "必须是自动登录账号 %s",
|
||||
"Must be unique account for %s": "必须是唯一的账号 %s",
|
||||
"Must be unique asset for %s": "必须是唯一的资产 %s",
|
||||
"MustOneFile": "只能选择一个文件",
|
||||
"MustSelectOneFile": "必须选择一个文件",
|
||||
"Name": "名称",
|
||||
"Need ACL review, continue? (y/n): ": "需要审核,继续?(y/n): ",
|
||||
"Need ticket confirm to execute command, already send email to the reviewers": "需要工单命令执行复核,已发邮件通知审核人",
|
||||
"Need ticket confirm to login, already send email to the reviewers": "需要工单登录复核,已发邮件通知审核人",
|
||||
"NewFolder": "新建文件夹",
|
||||
"No Account found.": "未发现账号",
|
||||
"No Assets": "没有资产",
|
||||
"No Databases": "无数据库",
|
||||
"No found asset": "未发现匹配的资产 %s",
|
||||
"No kubernetes": "没有kubernetes",
|
||||
"No protocol found.": "无协议",
|
||||
"No route to host": "网络不通(路由不通)",
|
||||
"NoActiveTerminalTabFound": "未找到活动终端标签页",
|
||||
"NoData": "无数据",
|
||||
"NoLink": "无地址",
|
||||
"NoRunningTerminalFound": "未找到正在运行的终端",
|
||||
"Node: [ ID.Name(Asset amount) ]": "节点:[ ID.名称(资产数量) ]",
|
||||
"OnlineUser": "在线用户",
|
||||
"OperationSuccessful": "操作成功",
|
||||
"Organization": "组织",
|
||||
"Owner": "管理者",
|
||||
"Page up: b\tPage down: n": "上一页:b 下一页:n",
|
||||
"Page: %d, Count: %d, Total Page: %d, Total Count: %d": "页码:%d,每页行数:%d,总页数:%d,总数量:%d",
|
||||
"Paste": "粘贴",
|
||||
"PauseSession": "暂停此会话",
|
||||
"PermissionDenied": "没有权限",
|
||||
"PermissionExpired": "权限已过期",
|
||||
"PermissionValid": "权限有效",
|
||||
"Platform": "平台",
|
||||
"Please waiting for the reviewers to confirm command `%s`, cancel by CTRL+C or CTRL+D.": "请等待审核人复核命令 `%s`,取消按 CTRL+C 或 CTRL+D。",
|
||||
"Please waiting for the reviewers to confirm, enter q to exit. ": "等待审核人复核确认,按 q 回车取消登录。",
|
||||
"PleaseInput": "请输入",
|
||||
"PleaseInputVerifyCode": "请输入验证码",
|
||||
"PrimaryUser": "主用户",
|
||||
"Protocol": "协议",
|
||||
"ReadOnly": "只读",
|
||||
"Reconnect": "重新连接",
|
||||
"Refresh": "刷新",
|
||||
"Refresh done": "刷新完成",
|
||||
"Remove": "移除",
|
||||
"RemoveShareUser": "你已经被移除共享会话",
|
||||
"RemoveUser": "移除用户",
|
||||
"Rename": "重命名",
|
||||
"ResumeSession": "恢复此会话",
|
||||
"Reuse SSH connections (%s@%s) [Number of connections: %d]": "复用SSH连接(%s@%s)[连接数量: %d]",
|
||||
"RightArrow": "前进箭头",
|
||||
"Search": "搜索",
|
||||
"Search: %s": "搜索:%s",
|
||||
"Select account exceed max retry times.": "选择账号超过最大重试次数",
|
||||
"Select protocol exceed max retry times.": "选择协议超过最大重试次数",
|
||||
"SelectAction": "请选择",
|
||||
"SelectTheme": "请选择主题",
|
||||
"Self": "我",
|
||||
"Session max time reached, disconnect": "会话超过最大连接时间,断开连接",
|
||||
"SessionDetail": "会话详情",
|
||||
"SessionShare": "会话分享",
|
||||
"Settings": "设置",
|
||||
@@ -96,18 +158,32 @@
|
||||
"ShareUser": "分享用户",
|
||||
"ShareUserHelpText": "未选择用户,即允许所有人加入",
|
||||
"Size": "大小",
|
||||
"Start domain gateway failed %s": "启动数据库网关失败%s",
|
||||
"Switch language successfully": "切换语言成功",
|
||||
"Switched to %s": "已切换至%s",
|
||||
"Sync": "同步",
|
||||
"SyncUserPreferenceFailed": "同步设置失败",
|
||||
"SyncUserPreferenceSuccess": "同步设置成功",
|
||||
"Terminal does not support protocol %s, please use web terminal to access": "该终端不支持 %s 协议,请使用web终端登录",
|
||||
"TerminalInstanceNotFound": "未找到当前选项卡的终端实例",
|
||||
"TerminalInstanceNotFoundForCurrentTab": "当前标签页未找到终端实例",
|
||||
"The command '%s' requires review. Continue or not [Y/n]?": "命令 %s 需要复核,是否继续?[Y/N]",
|
||||
"The command you executed is risky and an alert notification will be sent to the administrator. Do you want to continue?[Y/N]": "您执行的命令存在风险,告警通知将发送给管理员。是否继续?[Y/N]",
|
||||
"TheCurrentTerminalInstanceWasNotFound": "当前终端实例未找到",
|
||||
"Theme": "主题",
|
||||
"ThemeColors": "主题颜色",
|
||||
"ThemeConfig": "主题",
|
||||
"Ticket Reviewers: %s": "工单审核人:%s ",
|
||||
"Tips: Enter asset[%s] account ID": "提示:输入资产[%s]的账号ID",
|
||||
"Tips: Enter g+NodeID to display the host under the node, such as g1": "提示:输入 g+节点ID 显示节点下主机,如: g1",
|
||||
"Tips: Enter protocol ID": "提示:输入协议ID",
|
||||
"Tips: To set a default language, go to Personal Settings → Preferences on Web": "提示:如需设置默认语言,请前往 Web 端「个人设置 → 偏好设置」",
|
||||
"Tips: switch language by ID (Current session only)": "提示:输入ID切换语言",
|
||||
"Transfer": "传输",
|
||||
"Type": "类型",
|
||||
"UnableToGenerateWebSocketURL": "无法生成 WebSocket URL, 缺少参数",
|
||||
"Unknown error code: %s, detail: %s": "未知错误代码:%s,详情:%s",
|
||||
"Unknown status": "未知状态",
|
||||
"UpArrow": "向上箭头",
|
||||
"Upload": "上传",
|
||||
"UploadEnd": "上传已完成,请等待后续处理",
|
||||
@@ -117,111 +193,35 @@
|
||||
"UploadTips": "将文件拖到此处,或点击上传",
|
||||
"UploadTitle": "上传文件",
|
||||
"User": "用户",
|
||||
"Username": "用户名",
|
||||
"UsingRegularExpressions": "使用正则表达式",
|
||||
"VerifyCode": "验证码",
|
||||
"WaitFileTransfer": "等待文件传输结束",
|
||||
"Warning": "警告",
|
||||
"WebSocketClosed": "WebSocket 已关闭",
|
||||
"WebSocketConnectionIsClosedHelpText": "WebSocket 连接已关闭,请刷新页面或重新连接。",
|
||||
"Writable": "可写",
|
||||
"No Databases": "无数据库",
|
||||
"No kubernetes": "没有kubernetes",
|
||||
"Page: %d, Count: %d, Total Page: %d, Total Count: %d": "页码:%d,每页行数:%d,总页数:%d,总数量:%d",
|
||||
"Enter ID number directly login, multiple search use // + field, such as: //16": "提示:输入资产ID直接登录,二级搜索使用 // + 字段,如://192",
|
||||
"Page up: b\tPage down: n": "上一页:b 下一页:n",
|
||||
"No Assets": "没有资产",
|
||||
"ID": "ID",
|
||||
"Address": "地址",
|
||||
"Platform": "平台",
|
||||
"Organization": "组织",
|
||||
"Comment": "备注",
|
||||
"%s protocol client not installed.": "%s 协议的客户端未安装",
|
||||
"Terminal does not support protocol %s, please use web terminal to access": "该终端不支持 %s 协议,请使用web终端登录",
|
||||
"Core API failed": "Core API 发生错误",
|
||||
"ACL reject": "本次登录已拒绝,原因是访问控制策略的限制",
|
||||
"Face ACL is not supported yet. Please use the WebTerminal to connect the asset.": "该终端不支持人脸访问规则,请使用web终端登录",
|
||||
"Unknown error code: %s, detail: %s": "未知错误代码:%s,详情:%s",
|
||||
"get connect token err": "获取 connect token 错误",
|
||||
"%s node has no assets": "%s节点没有资产",
|
||||
"Welcome to use JumpServer open source fortress system": "欢迎使用JumpServer开源堡垒机系统",
|
||||
"part IP, Hostname, Comment": "部分IP,主机名,备注",
|
||||
"to search login if unique": "搜索登录(如果唯一)",
|
||||
"part IP,, Hostname, Comment": "/ + IP,主机名,备注",
|
||||
"to search, such as: /192.168": "搜索,如:/192.168",
|
||||
"display the assets you have permission": "显示您有权限的资产",
|
||||
"display the node that you have permission": "显示您有权限的节点",
|
||||
"display the hosts that you have permission": "显示您有权限的主机",
|
||||
"display the databases that you have permission": "显示您有权限的数据库",
|
||||
"display the kubernetes that you have permission": "显示您有权限的Kubernetes",
|
||||
"refresh your assets and nodes": "刷新最新的机器和节点信息",
|
||||
"language switch": "语言切换",
|
||||
"print help": "显示帮助",
|
||||
"exit": "退出",
|
||||
"\t%2d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s": "\t%d) 输入 {{.GreenBoldColor}}%s{{.ColorEnd}} 进行%s.%s",
|
||||
"Announcement: ": "公告:",
|
||||
"No Account found.": "未发现账号",
|
||||
"Username": "用户名",
|
||||
"Tips: Enter asset[%s] account ID": "提示:输入资产[%s]的账号ID",
|
||||
"Back: B/b": "返回:B/b",
|
||||
"Hostname": "主机名",
|
||||
"select one asset to login": "选择其中一个资产登录",
|
||||
"not found matched username %s": "未发现匹配的用户名 %s",
|
||||
"Face verification is not supported yet. Please use the WebTerminal to connect the asset.": "该终端不支持人脸识别认证,请使用web终端登录",
|
||||
"Tips: switch language by ID (Current session only)": "提示:输入ID切换语言",
|
||||
"Tips: To set a default language, go to Personal Settings → Preferences on Web": "提示:如需设置默认语言,请前往 Web 端「个人设置 → 偏好设置」",
|
||||
"Invalid ID": "无效 ID",
|
||||
"Switch language successfully": "切换语言成功",
|
||||
"Node: [ ID.Name(Asset amount) ]": "节点:[ ID.名称(资产数量) ]",
|
||||
"Tips: Enter g+NodeID to display the host under the node, such as g1": "提示:输入 g+节点ID 显示节点下主机,如: g1",
|
||||
"Connect idle more than %d minutes, disconnect": "空闲时间超过%d分钟,断开连接",
|
||||
"Select account exceed max retry times.": "选择账号超过最大重试次数",
|
||||
"No protocol found.": "无协议",
|
||||
"Protocol": "协议",
|
||||
"Tips: Enter protocol ID": "提示:输入协议ID",
|
||||
"Select protocol exceed max retry times.": "选择协议超过最大重试次数",
|
||||
"Refresh done": "刷新完成",
|
||||
"Need ACL review, continue? (y/n): ": "需要审核,继续?(y/n): ",
|
||||
"Cancel to login asset or max 3 retry": "取消登录资产或达到3次重试",
|
||||
"Need ticket confirm to login, already send email to the reviewers": "需要工单登录复核,已发邮件通知审核人",
|
||||
"Ticket Reviewers: %s": "工单审核人:%s ",
|
||||
"Could copy website URL to notify reviewers: %s": "可复制审核地址,通知审核人:%s",
|
||||
"Please waiting for the reviewers to confirm, enter q to exit. ": "等待审核人复核确认,按 q 回车取消登录。",
|
||||
"Unknown status": "未知状态",
|
||||
"%s approved": "%s 审核通过",
|
||||
"%s rejected": "%s 审核拒绝",
|
||||
"Cancel confirm": "取消登录复核",
|
||||
"Search: %s": "搜索:%s",
|
||||
"Must be unique asset for %s": "必须是唯一的资产 %s",
|
||||
"Must be unique account for %s": "必须是唯一的账号 %s",
|
||||
"Must be auto login account for %s": "必须是自动登录账号 %s",
|
||||
"No found asset": "未发现匹配的资产 %s",
|
||||
"Create k8s client err: %s": "创建 k8s 客户端错误:%s",
|
||||
"have no permission to upload file": "无权限上传文件",
|
||||
"The command you executed is risky and an alert notification will be sent to the administrator. Do you want to continue?[Y/N]": "您执行的命令存在风险,告警通知将发送给管理员。是否继续?[Y/N]",
|
||||
"The command '%s' requires review. Continue or not [Y/n]?": "命令 %s 需要复核,是否继续?[Y/N]",
|
||||
"Command `%s` is forbidden": "命令 `%s` 是被禁止的 ...",
|
||||
"have no permission to download file": "无权限下载文件",
|
||||
"Please waiting for the reviewers to confirm command `%s`, cancel by CTRL+C or CTRL+D.": "请等待审核人复核命令 `%s`,取消按 CTRL+C 或 CTRL+D。",
|
||||
"Need ticket confirm to execute command, already send email to the reviewers": "需要工单命令执行复核,已发邮件通知审核人",
|
||||
"HandleTask does not support protocol %s, please use web terminal to access": "该终端不支持 %s 协议,请使用web终端登录",
|
||||
"Account <%s> and asset <%s> protocol are inconsistent.": "系统用户<%s>和资产<%s>协议不一致",
|
||||
"Writable": "可写",
|
||||
"You don't have permission login %s": "你无权限登陆%s",
|
||||
"You get auth token failed": "你获取认证令牌失败",
|
||||
"Get auth password failed": "你获取认证令牌失败",
|
||||
"Reuse SSH connections (%s@%s) [Number of connections: %d]": "复用SSH连接(%s@%s)[连接数量: %d]",
|
||||
"Switched to %s": "已切换至%s",
|
||||
"Connect with api server failed": "连接API服务失败",
|
||||
"Start domain gateway failed %s": "启动数据库网关失败%s",
|
||||
"Manual": "手动账号",
|
||||
"Dynamic": "动态账号",
|
||||
"Connecting to %s@%s": "开始连接到 %s@%s",
|
||||
"Connecting to Database %s": "开始连接数据库 %s",
|
||||
"Connecting to Kubernetes %s": "开始连接Kubernetes %s",
|
||||
"Connecting to Kubernetes %s container %s": "开始连接Kubernetes %s 容器 %s",
|
||||
"Session max time reached, disconnect": "会话超过最大连接时间,断开连接",
|
||||
"Authentication failed": "认证失败(用户名或密码错误)",
|
||||
"Connection refused": "网络不通(连接拒绝)",
|
||||
"display the assets you have permission": "显示您有权限的资产",
|
||||
"display the databases that you have permission": "显示您有权限的数据库",
|
||||
"display the hosts that you have permission": "显示您有权限的主机",
|
||||
"display the kubernetes that you have permission": "显示您有权限的Kubernetes",
|
||||
"display the node that you have permission": "显示您有权限的节点",
|
||||
"exit": "退出",
|
||||
"get connect token err": "获取 connect token 错误",
|
||||
"have no permission to download file": "无权限下载文件",
|
||||
"have no permission to upload file": "无权限上传文件",
|
||||
"i/o timeout": "网络不通(连接超时)",
|
||||
"No route to host": "网络不通(路由不通)",
|
||||
"network is unreachable": "网络不通(网络不可达)"
|
||||
"language switch": "语言切换",
|
||||
"network is unreachable": "网络不通(网络不可达)",
|
||||
"not found matched username %s": "未发现匹配的用户名 %s",
|
||||
"part IP, Hostname, Comment": "部分IP,主机名,备注",
|
||||
"part IP,, Hostname, Comment": "/ + IP,主机名,备注",
|
||||
"print help": "显示帮助",
|
||||
"refresh your assets and nodes": "刷新最新的机器和节点信息",
|
||||
"select one asset to login": "选择其中一个资产登录",
|
||||
"to search login if unique": "搜索登录(如果唯一)",
|
||||
"to search, such as: /192.168": "搜索,如:/192.168"
|
||||
}
|
||||
@@ -121,7 +121,7 @@
|
||||
"AppletHelpText": "In the upload process, if the application does not exist, create the application; if it exists, update the application.",
|
||||
"AppletHostCreate": "Add RemoteApp machine",
|
||||
"AppletHostDetail": "RemoteApp machine",
|
||||
"AppletHostSelectHelpMessage": "When connecting to an asset, the selection of the application publishing machine is random (but the last used one is preferred). if you want to assign a specific publishing machine to an asset, you can tag it as [publishing machine: publishing machine name] or [AppletHost: publishing machine name]; <br>when selecting an account for the publishing machine, the following situations will choose the user's own <b>account with the same name or proprietary account (starting with js)</b>, otherwise use a public account (starting with jms):<br> 1. both the publishing machine and application support concurrent;<br> 2. the publishing machine supports concurrent, but the application does not, and the current application does not use a proprietary account;<br> 3. the publishing machine does not support concurrent, the application either supports or does not support concurrent, and no application uses a proprietary account;<br> note: whether the application supports concurrent connections is decided by the developer, and whether the host supports concurrent connections is decided by the single user single session setting in the publishing machine configuration",
|
||||
"AppletHostSelectHelpMessage": "When connecting to an asset, the selection of the application publishing machine is random (but the last used one is preferred). if you want to assign a specific publishing machine to an asset, you can tag it as [发布机: publishing machine name] [AppletHost: publishing machine name] [仅发布机: publishing machine name] [AppletHostOnly: publishing machine name]; <br>when selecting an account for the publishing machine, the following situations will choose the user's own <b>account with the same name or proprietary account (starting with js)</b>, otherwise use a public account (starting with jms):<br> 1. both the publishing machine and application support concurrent;<br> 2. the publishing machine supports concurrent, but the application does not, and the current application does not use a proprietary account;<br> 3. the publishing machine does not support concurrent, the application either supports or does not support concurrent, and no application uses a proprietary account;<br> note: whether the application supports concurrent connections is decided by the developer, and whether the host supports concurrent connections is decided by the single user single session setting in the publishing machine configuration",
|
||||
"AppletHostUpdate": "Update the remote app publishing machine",
|
||||
"AppletHostZoneHelpText": "This domain belongs to the system organization",
|
||||
"AppletHosts": "RemoteApp machine",
|
||||
@@ -199,7 +199,7 @@
|
||||
"AuditsDashboard": "Audits dashboard",
|
||||
"Auth": "Authentication",
|
||||
"AuthConfig": "Authentication",
|
||||
"AuthIntegration": "AuthIntegration",
|
||||
"AuthIntegration": "Auth Integration",
|
||||
"AuthLimit": "Login restriction",
|
||||
"AuthSAMLCertHelpText": "Save after uploading the certificate key, then view sp metadata",
|
||||
"AuthSAMLKeyHelpText": "Sp certificates and keys are used for encrypted communication with idp",
|
||||
@@ -448,6 +448,10 @@
|
||||
"DangerCommand": "Dangerous command",
|
||||
"DangerousCommandNum": "Total dangerous commands",
|
||||
"Dashboard": "Dashboard",
|
||||
"DataMasking": "DataMasking",
|
||||
"DataMaskingFieldsPatternHelpTip": "Supports multiple field names separated by commas, and supports wildcard *\n For example:\nSingle field name: password — only masks the\"password\" field\nMultiple field names: password,secret — masks both\"password\" and\"secret\" fields\nWildcard : password — masks fields whose names start with\"password\"\nWildcard *: .*password — masks fields whose names end with\"password\"\n",
|
||||
"DataMaskingRuleHelpHelpMsg": "When connecting to database assets, you can anonymize query results based on this rule.",
|
||||
"DataMaskingRuleHelpHelpText": "When connecting to database assets, you can anonymize query results based on this rule.",
|
||||
"Database": "Database",
|
||||
"DatabaseCreate": "Create asset - database",
|
||||
"DatabasePort": "Database protocol port",
|
||||
@@ -573,9 +577,9 @@
|
||||
"EsUrl": "Cannot include special char `#`; eg: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "Every",
|
||||
"Exclude": "Does not include",
|
||||
"ExcludeAccount": "Exclude accounts",
|
||||
"ExcludeAsset": "Skipped assets",
|
||||
"ExcludeSymbol": "Exclude char",
|
||||
"ExcludeAccount": "Exclude accounts",
|
||||
"ExecCloudSyncErrorMsg": "The cloud account configuration is incomplete, please update and try again.",
|
||||
"Execute": "Execute",
|
||||
"ExecuteAfterSaving": "Execute after saving",
|
||||
@@ -769,6 +773,7 @@
|
||||
"LdapBulkImport": "User import",
|
||||
"LdapConnectTest": "Test connection",
|
||||
"LdapLoginTest": "Test login",
|
||||
"LeakPasswordList": "Leaked password list",
|
||||
"LeakedPassword": "Leaked password",
|
||||
"Length": "Length",
|
||||
"LessEqualThan": "Less than or equal to",
|
||||
@@ -865,6 +870,7 @@
|
||||
"MoveAssetToNode": "Move assets to node",
|
||||
"MoveToAsset": "Move to asset",
|
||||
"MsgSubscribe": "Subscription",
|
||||
"MsgTemplate": "Msg Template",
|
||||
"MyAssets": "My assets",
|
||||
"MyTickets": "Submitted",
|
||||
"NUMBER_REQUIRED": "Must contain numbers",
|
||||
@@ -1019,6 +1025,7 @@
|
||||
"PleaseAgreeToTheTerms": "Please agree to the terms",
|
||||
"PleaseEnterReason": "Please enter a reason",
|
||||
"PleaseSelect": "Please select ",
|
||||
"PleaseSelectAssetOrNode": "Please select at least one asset or node",
|
||||
"PleaseSelectTheDataYouWantToCheck": "Please select the data you want to check",
|
||||
"PolicyName": "Policy name",
|
||||
"Port": "Port",
|
||||
@@ -1035,6 +1042,7 @@
|
||||
"PrivilegedOnly": "Privileged only",
|
||||
"PrivilegedTemplate": "Privileged",
|
||||
"Processing": "Processing",
|
||||
"ProcessingMessage": "Processing, please wait...",
|
||||
"Product": "Product",
|
||||
"ProfileSetting": "Profile info",
|
||||
"Project": "Project name",
|
||||
@@ -1148,6 +1156,7 @@
|
||||
"ResetSSHKeySuccessMsg": "Email task submitted, user will receive a url to reset shortly",
|
||||
"ResetSSHKeyWarningMsg": "Are you sure you want to send a reset ssh key email to the user?",
|
||||
"ResetSecret": "Secret can be changed",
|
||||
"ResetSuccessfully": "Reset successfully",
|
||||
"ResolveSelected": "Resolve selected",
|
||||
"Resource": "Resources",
|
||||
"ResourceType": "Resource type",
|
||||
@@ -1406,6 +1415,7 @@
|
||||
"TemplateCreate": "Create template",
|
||||
"TemplateHelpText": "When selecting a template to add, accounts that do not exist under the asset will be automatically created and pushed",
|
||||
"TemplateManagement": "Templates",
|
||||
"TemplateVariablesHelpText": "You can select a template and use {{ key }} within the template content to read built-in variables.Note: only the {{ }} syntax is supported; other syntaxes such as {% if title %} are not supported.",
|
||||
"Templates": "Templates",
|
||||
"TencentCloud": "Tencent cloud",
|
||||
"Terminal": "Components",
|
||||
@@ -1520,12 +1530,12 @@
|
||||
"UserCreate": "Create user",
|
||||
"UserData": "User data",
|
||||
"UserDetail": "User details",
|
||||
"UserGroup": "User group",
|
||||
"UserGroupCreate": "Create user group",
|
||||
"UserGroupDetail": "User group details",
|
||||
"UserGroupList": "Groups",
|
||||
"UserGroupUpdate": "Update the user group",
|
||||
"UserGroups": "Groups",
|
||||
"UserGroup": "User group",
|
||||
"UserList": "Users",
|
||||
"UserLogin": "User login",
|
||||
"UserLoginACL": "User login ACL",
|
||||
@@ -1611,16 +1621,18 @@
|
||||
"assetAddress": "Asset address",
|
||||
"assetId": "Asset ID",
|
||||
"assetName": "Asset name",
|
||||
"clickToAdd": "Click to add",
|
||||
"currentTime": "Current time",
|
||||
"description": "No data yet",
|
||||
"disallowSelfUpdateFields": "Not allowed to modify the current fields yourself",
|
||||
"forceEnableMFAHelpText": "If force enable, user can not disable by themselves",
|
||||
"isConsoleCanUse": "is Console page can use",
|
||||
"overwriteProtocolsAndPortsMsg": "This operation will overwrite the protocols and ports of the selected assets. Are you sure you want to continue?",
|
||||
"pleaseSelectAssets": "Please select assets",
|
||||
"removeWarningMsg": "Are you sure you want to remove",
|
||||
"selectFiles": "Selected {number} files",
|
||||
"selectedAssets": "Selected assets",
|
||||
"setVariable": "Set variable",
|
||||
"userId": "User ID",
|
||||
"userName": "User name",
|
||||
"description": "No data yet",
|
||||
"MsgTemplate": "Msg Template",
|
||||
"TemplateVariablesHelpText": "You can select a template and use {{ key }} within the template content to read built-in variables.Note: only the {{ }} syntax is supported; other syntaxes such as {% if title %} are not supported.",
|
||||
"ResetSuccessfully": "Reset successfully"
|
||||
}
|
||||
"userName": "User name"
|
||||
}
|
||||
@@ -447,6 +447,10 @@
|
||||
"DangerCommand": "Orden de peligro",
|
||||
"DangerousCommandNum": "Número de comandos peligrosos",
|
||||
"Dashboard": "panel de control",
|
||||
"DataMasking": "Desensibilización de datos",
|
||||
"DataMaskingFieldsPatternHelpTip": "Soporte para múltiples nombres de campo, separados por comas, con soporte para comodines *\nPor ejemplo:\nNombre de campo único: password indica que solo se desensibiliza el campo password.\nMúltiples nombres de campo: password,secret indica que se desensibilizan los campos password y secret.\nComodín*: password* indica que se desensibilizan los campos que contienen el prefijo password.\nComodín*: .*password indica que se desensibilizan los campos que contienen el sufijo password.",
|
||||
"DataMaskingRuleHelpHelpMsg": "Al conectar con la base de datos de activos, se puede aplicar esta regla para desensibilizar los resultados de la consulta.",
|
||||
"DataMaskingRuleHelpHelpText": "Al conectar activos de la base de datos, se puede llevar a cabo la desensibilización de los resultados de la consulta según esta regla.",
|
||||
"Database": "Base de datos",
|
||||
"DatabaseCreate": "crear activo - base de datos",
|
||||
"DatabasePort": "Puerto del protocolo de la base de datos",
|
||||
@@ -571,6 +575,7 @@
|
||||
"EsIndex": "es proporcionando el índice predeterminado: jumpserver. Si se habilita el índice por fecha, el valor ingresado se usará como prefijo del índice",
|
||||
"EsUrl": "No puede incluir caracteres especiales `#`; ej: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "Cada",
|
||||
"Example": "Ejemplo de valor",
|
||||
"Exclude": "No incluir",
|
||||
"ExcludeAccount": "Excluir cuenta",
|
||||
"ExcludeAsset": "Activos omitidos",
|
||||
@@ -866,6 +871,7 @@
|
||||
"MoveAssetToNode": "Mover activos al nodo",
|
||||
"MoveToAsset": "Mover a activos",
|
||||
"MsgSubscribe": "Suscripción de mensajes",
|
||||
"MsgTemplate": "Plantilla de mensajes",
|
||||
"MyAssets": "Mis activos",
|
||||
"MyTickets": "Iniciados por mí",
|
||||
"NUMBER_REQUIRED": "Debe incluir números",
|
||||
@@ -1022,6 +1028,7 @@
|
||||
"PleaseAgreeToTheTerms": "Por favor, acepte los términos",
|
||||
"PleaseEnterReason": "Por favor, introduzca el motivo",
|
||||
"PleaseSelect": "Por favor seleccione",
|
||||
"PleaseSelectAssetOrNode": "Por favor, seleccione un activo o nodo",
|
||||
"PleaseSelectTheDataYouWantToCheck": "Por favor, seleccione los datos que necesita marcar.",
|
||||
"PolicyName": "Nombre de la estrategia",
|
||||
"Port": "Puerto",
|
||||
@@ -1039,6 +1046,7 @@
|
||||
"PrivilegedOnly": "Solo cuentas privilegiadas",
|
||||
"PrivilegedTemplate": "Privilegiado",
|
||||
"Processing": "En proceso",
|
||||
"ProcessingMessage": "Tarea en progreso, por favor, espere ⏳",
|
||||
"Product": "Productos",
|
||||
"ProfileSetting": "Configuración de información personal",
|
||||
"Project": "Nombre del proyecto",
|
||||
@@ -1153,6 +1161,7 @@
|
||||
"ResetSSHKeySuccessMsg": "Tarea de envío de correo electrónico ha sido enviada, el usuario recibirá más tarde un correo electrónico con la clave de reinicio",
|
||||
"ResetSSHKeyWarningMsg": "¿Está seguro de que desea enviar el correo para restablecer la clave SSH del usuario?",
|
||||
"ResetSecret": "Cambio de contraseña permitido",
|
||||
"ResetSuccessfully": "Restablecimiento exitoso.",
|
||||
"ResolveSelected": "Resolver lo establecido",
|
||||
"Resource": "Recursos",
|
||||
"ResourceType": "Tipo de recurso",
|
||||
@@ -1414,6 +1423,7 @@
|
||||
"TemplateCreate": "Crear plantilla",
|
||||
"TemplateHelpText": "Al seleccionar una plantilla para añadir, se crearán automáticamente cuentas que no existen en el activo y se enviarán",
|
||||
"TemplateManagement": "Gestión de plantillas",
|
||||
"TemplateVariablesHelpText": "Puede elegir una plantilla y utilizar {{ key }} en el contenido de la plantilla para leer variables incorporadas. Tenga en cuenta que solo se admite la sintaxis {{ }}, no se admite ninguna otra. Por ejemplo, {% if title %}",
|
||||
"Templates": "Plantilla",
|
||||
"TencentCloud": "Tencent Cloud",
|
||||
"Terminal": "Configuración de componentes",
|
||||
@@ -1619,13 +1629,17 @@
|
||||
"assetAddress": "Dirección de activo",
|
||||
"assetId": "ID de activo",
|
||||
"assetName": "Nombre de activo",
|
||||
"clickToAdd": "Haga clic en agregar",
|
||||
"currentTime": "Hora actual",
|
||||
"description": "Sin datos disponibles.",
|
||||
"disallowSelfUpdateFields": "No se permite modificar el campo actual.",
|
||||
"forceEnableMFAHelpText": "Si se habilita forzosamente, el usuario no podrá desactivarlo por sí mismo",
|
||||
"isConsoleCanUse": "¿Está disponible la página de gestión?< -SEP->Agregar puerta de enlace al dominio",
|
||||
"name": "Nombre de usuario",
|
||||
"overwriteProtocolsAndPortsMsg": "Esta acción reemplazará todos los protocolos y puertos, ¿continuar?",
|
||||
"pleaseSelectAssets": "Por favor, seleccione un activo.",
|
||||
"removeWarningMsg": "¿Está seguro de que desea eliminar?",
|
||||
"selectedAssets": "Activos seleccionados",
|
||||
"setVariable": "configurar parámetros",
|
||||
"userId": "ID de usuario",
|
||||
"userName": "Nombre de usuario"
|
||||
|
||||
@@ -452,6 +452,10 @@
|
||||
"DangerousCommandNum": "危険なコマンド数",
|
||||
"Dashboard": "ダッシュボード",
|
||||
"DataLastUsed": "さいごしようび",
|
||||
"DataMasking": "データマスキング",
|
||||
"DataMaskingFieldsPatternHelpTip": "複数のフィールド名をサポートし、カンマで区切ります。ワイルドカード*もサポートしています。\n例えば:\n単一フィールド名: password は password フィールドのみマスキングします。\n複数フィールド名: password, secret は password と secret フィールドの両方をマスキングします。\nワイルドカード*: password* は、password プレフィックスを含むフィールド名をマスキングします。\nワイルドカード*: .*password は、password サフィックスを含むフィールド名をマスキングします。",
|
||||
"DataMaskingRuleHelpHelpMsg": "データベース資産に接続する際、これに基づいてクエリ結果をマスキングすることができます。",
|
||||
"DataMaskingRuleHelpHelpText": "データベース資産に接続する際は、このルールに従ってクエリ結果をマスキングできます。",
|
||||
"Database": "データベース",
|
||||
"DatabaseCreate": "資産-データベースの作成",
|
||||
"DatabasePort": "データベースプロトコルポート",
|
||||
@@ -576,6 +580,7 @@
|
||||
"EsIndex": "es はデフォルト index:jumpserverを提供します。日付でインデックスを作成する設定が有効な場合、入力値はインデックスのプレフィックスとして使用されます",
|
||||
"EsUrl": "特殊文字 `#` は含むことができません;例: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "毎",
|
||||
"Example": "例の値",
|
||||
"Exclude": "除外",
|
||||
"ExcludeAccount": "アカウント除外",
|
||||
"ExcludeAsset": "スキップされた資産",
|
||||
@@ -871,6 +876,7 @@
|
||||
"MoveAssetToNode": "アセットをノードに移動",
|
||||
"MoveToAsset": "資産へ移動",
|
||||
"MsgSubscribe": "メッセージの購読",
|
||||
"MsgTemplate": "メッセージテンプレート",
|
||||
"MyAssets": "私の資産",
|
||||
"MyTickets": "私が始めた",
|
||||
"NUMBER_REQUIRED": "数字を含める必要があります",
|
||||
@@ -1027,6 +1033,7 @@
|
||||
"PleaseAgreeToTheTerms": "規約に同意してください",
|
||||
"PleaseEnterReason": "理由を入力してください",
|
||||
"PleaseSelect": "選択してくださ",
|
||||
"PleaseSelectAssetOrNode": "資産またはノードを選択してください",
|
||||
"PleaseSelectTheDataYouWantToCheck": "選択するデータをチェックしてください",
|
||||
"PolicyName": "ポリシー名称",
|
||||
"Port": "ポート",
|
||||
@@ -1044,6 +1051,7 @@
|
||||
"PrivilegedOnly": "特権アカウントのみ",
|
||||
"PrivilegedTemplate": "特別な権限の",
|
||||
"Processing": "処理中",
|
||||
"ProcessingMessage": "タスクを実行中です。しばらくお待ちください ⏳",
|
||||
"Product": "商品",
|
||||
"ProfileSetting": "個人情報設定",
|
||||
"Project": "プロジェクト名",
|
||||
@@ -1158,6 +1166,7 @@
|
||||
"ResetSSHKeySuccessMsg": "メール送信タスクが提出されました。ユーザーは後でリセットキーのメールを受け取ります",
|
||||
"ResetSSHKeyWarningMsg": "ユーザーのSSH Keyをリセットするメールを送信してもよろしいですか?",
|
||||
"ResetSecret": " パスワード変更可能 ",
|
||||
"ResetSuccessfully": "リセット成功",
|
||||
"ResolveSelected": "解決選択",
|
||||
"Resource": "リソース",
|
||||
"ResourceType": "リソースタイプ",
|
||||
@@ -1419,6 +1428,7 @@
|
||||
"TemplateCreate": "テンプレート作成",
|
||||
"TemplateHelpText": "テンプレートを選択して追加すると、資産の下に存在しないアカウントが自動的に作成され、プッシュされます",
|
||||
"TemplateManagement": "テンプレート一覧",
|
||||
"TemplateVariablesHelpText": "テンプレートの中で {{ key }} を使って組み込み変数を読み取るテンプレートを選択できます。注意:サポートされているのは {{ }} 構文のみで、他の構文はサポートされていません。例えば {% if title %}",
|
||||
"Templates": "テンプレート",
|
||||
"TencentCloud": "テンセントクラウド",
|
||||
"Terminal": "コンポーネント設定",
|
||||
@@ -1624,13 +1634,17 @@
|
||||
"assetAddress": "資産アドレス",
|
||||
"assetId": "資産ID",
|
||||
"assetName": "資産名",
|
||||
"clickToAdd": "追加をクリック",
|
||||
"currentTime": "現在の時間",
|
||||
"description": "データはありません。",
|
||||
"disallowSelfUpdateFields": "現在のフィールドを自分で変更することは許可されていません",
|
||||
"forceEnableMFAHelpText": "強制的に有効化すると、ユーザーは自分で無効化することができません。",
|
||||
"isConsoleCanUse": "管理ページの利用可能性",
|
||||
"name": "ユーザー名",
|
||||
"overwriteProtocolsAndPortsMsg": "この操作はすべてのプロトコルとポートを上書きしますが、続行してよろしいですか?",
|
||||
"pleaseSelectAssets": "資産を選択してください",
|
||||
"removeWarningMsg": "削除してもよろしいですか",
|
||||
"selectedAssets": "選択した資産",
|
||||
"setVariable": "パラメータ設定",
|
||||
"userId": "ユーザーID",
|
||||
"userName": "ユーザー名"
|
||||
|
||||
@@ -447,6 +447,10 @@
|
||||
"DangerCommand": "위험 명령",
|
||||
"DangerousCommandNum": "위험 명령 수",
|
||||
"Dashboard": "대시보드",
|
||||
"DataMasking": "데이터 탈민",
|
||||
"DataMaskingFieldsPatternHelpTip": "여러 개의 필드명을 지원하며, 쉼표로 구분됩니다. 또한 와일드카드*도 지원합니다. \n예를 들어: \n단일 필드명: password는 password 필드만 비식별화합니다. \n다수의 필드명: password,secret은 password와 secret 필드를 비식별화합니다. \n와일드카드*: password*는 password 접두사가 포함된 필드명을 비식별화합니다. \n와일드카드*: .*password는 password 접미사가 포함된 필드명을 비식별화합니다.",
|
||||
"DataMaskingRuleHelpHelpMsg": "데이터베이스 자산에 연결할 때, 이 규칙에 따라 쿼리 결과를 비감시 처리할 수 있습니다.",
|
||||
"DataMaskingRuleHelpHelpText": "데이터베이스 자산에 연결할 때, 이 규칙에 따라 조회 결과의 민감 정보를 비공개 처리할 수 있습니다.",
|
||||
"Database": "데이터베이스",
|
||||
"DatabaseCreate": "로그인 검토",
|
||||
"DatabasePort": "라벨",
|
||||
@@ -571,6 +575,7 @@
|
||||
"EsIndex": "es 기본 인덱스 제공: jumpserver. 날짜별 인덱스 생성을 활성화하면 입력된 값이 인덱스 접두사로 사용됩니다",
|
||||
"EsUrl": "특수 문자 `#`를 포함할 수 없습니다; 예: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "매우",
|
||||
"Example": "예시 값",
|
||||
"Exclude": "포함되지 않음",
|
||||
"ExcludeAccount": "계정 제외",
|
||||
"ExcludeAsset": "건너뛴 자산",
|
||||
@@ -866,6 +871,7 @@
|
||||
"MoveAssetToNode": "이동 자산을 노드로< -SEP-> 역사 기록< -SEP-> 생체 정보< -SEP-> 자산을 노드에 추가< -SEP-> 포함< -SEP-> 장시간 미로그인< -SEP-> 권한 있는 자산< -SEP-> 정기적으로< -SEP-> 변경 후< -SEP-> 연결 가능< -SEP-> 위험< -SEP-> 자산 업데이트 - 데이터베이스< -SEP-> 동기화< -SEP-> 녹화 다운로드< -SEP-> 실행 경로 입력",
|
||||
"MoveToAsset": "자산으로 이동",
|
||||
"MsgSubscribe": "메시지 구독",
|
||||
"MsgTemplate": "메시지 템플릿",
|
||||
"MyAssets": "템플릿",
|
||||
"MyTickets": "내가 시작한",
|
||||
"NUMBER_REQUIRED": "숫자를 포함해야 합니다",
|
||||
@@ -1022,6 +1028,7 @@
|
||||
"PleaseAgreeToTheTerms": "약관 동의 부탁드립니다",
|
||||
"PleaseEnterReason": "이유를 입력하세요",
|
||||
"PleaseSelect": "선택해주세요",
|
||||
"PleaseSelectAssetOrNode": "자산 또는 노드를 선택해 주세요",
|
||||
"PleaseSelectTheDataYouWantToCheck": "선택할 데이터를 선택해 주십시오.",
|
||||
"PolicyName": "전략 이름",
|
||||
"Port": "포트",
|
||||
@@ -1039,6 +1046,7 @@
|
||||
"PrivilegedOnly": "특권 계정만",
|
||||
"PrivilegedTemplate": "특권의",
|
||||
"Processing": "처리 중",
|
||||
"ProcessingMessage": "작업 진행 중입니다. 잠시만 기다려 주십시오 ⏳",
|
||||
"Product": "제품",
|
||||
"ProfileSetting": "개인 정보 설정",
|
||||
"Project": "프로젝트 이름",
|
||||
@@ -1153,6 +1161,7 @@
|
||||
"ResetSSHKeySuccessMsg": "메일 전송 작업이 제출되었습니다. 사용자는 나중에 재설정 키 메일을 받게 될 것입니다.",
|
||||
"ResetSSHKeyWarningMsg": "사용자의 SSH 키를 재설정하는 이메일을 전송하시겠습니까?",
|
||||
"ResetSecret": "변경 가능 비밀번호",
|
||||
"ResetSuccessfully": "재설정 성공",
|
||||
"ResolveSelected": "선택한 사항 해결",
|
||||
"Resource": "리소스",
|
||||
"ResourceType": "자원 유형",
|
||||
@@ -1414,6 +1423,7 @@
|
||||
"TemplateCreate": "템플릿 생성",
|
||||
"TemplateHelpText": "템플릿 선택 시, 자산에 존재하지 않는 계정을 자동으로 생성하고 전송",
|
||||
"TemplateManagement": "템플릿 관리",
|
||||
"TemplateVariablesHelpText": "템플릿에서 {{ key }}를 사용하여 내장 변수를 읽어올 수 있는 템플릿을 선택할 수 있습니다. 주의: {{ }} 문법만 지원하며, 다른 문법은 지원하지 않습니다. 예를 들어 {% if title %}",
|
||||
"Templates": "기업 버전",
|
||||
"TencentCloud": "텐센트 클라우드",
|
||||
"Terminal": "컴포넌트 설정",
|
||||
@@ -1619,13 +1629,17 @@
|
||||
"assetAddress": "자산 주소",
|
||||
"assetId": "자산 ID",
|
||||
"assetName": "자산 이름",
|
||||
"clickToAdd": "추가를 클릭해 주세요",
|
||||
"currentTime": "현재 시간",
|
||||
"description": "데이터가 없습니다.",
|
||||
"disallowSelfUpdateFields": "현재 필드를 스스로 수정할 수 없음",
|
||||
"forceEnableMFAHelpText": "강제로 활성화하면 사용자가 스스로 비활성화할 수 없음",
|
||||
"isConsoleCanUse": "관리 페이지 사용 가능 여부",
|
||||
"name": "사용자 이름",
|
||||
"overwriteProtocolsAndPortsMsg": "이 작업은 모든 프로토콜과 포트를 덮어씌우게 됩니다. 계속하시겠습니까?",
|
||||
"pleaseSelectAssets": "자산을 선택해 주세요",
|
||||
"removeWarningMsg": "제거할 것인지 확실합니까?",
|
||||
"selectedAssets": "선택한 자산",
|
||||
"setVariable": "설정 매개변수",
|
||||
"userId": "사용자 ID",
|
||||
"userName": "사용자명"
|
||||
|
||||
@@ -448,6 +448,10 @@
|
||||
"DangerCommand": "Comando perigoso",
|
||||
"DangerousCommandNum": "Número de Comandos Perigosos",
|
||||
"Dashboard": "Painel ",
|
||||
"DataMasking": "Desensibilização de dados",
|
||||
"DataMaskingFieldsPatternHelpTip": "Suporta múltiplos nomes de campo, separados por vírgula, e aceita caracteres coringa *\nPor exemplo:\nNome de campo único: password indica que apenas o campo password será desensibilizado.\nMúltiplos nomes de campo: password, secret indica que os campos password e secret serão desensibilizados.\nCaractere coringa *: password* indica que todos os campos que contêm o prefixo password serão desensibilizados.\nCaractere coringa *: .*password indica que todos os campos que contêm o sufixo password serão desensibilizados.",
|
||||
"DataMaskingRuleHelpHelpMsg": "Ao conectar ao banco de dados de ativos, é possível aplicar esta regra para desensibilizar os resultados da consulta.",
|
||||
"DataMaskingRuleHelpHelpText": "Ao conectar-se ao ativo do banco de dados, é possível aplicar esta regra para mascarar os resultados da consulta.",
|
||||
"Database": "Banco de dados",
|
||||
"DatabaseCreate": "Criar ativo - Banco de dados",
|
||||
"DatabasePort": "Porta do protocolo do banco de dados",
|
||||
@@ -572,6 +576,7 @@
|
||||
"EsIndex": "'es' fornece o índice padrão: 'jumpserver'. Se você ativar a criação de índices por data, o valor inserido será usado como prefixo do índice.",
|
||||
"EsUrl": "Não pode conter o caractere especial `#`; ex: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "Cada",
|
||||
"Example": "Exemplo de valor",
|
||||
"Exclude": "Não inclui",
|
||||
"ExcludeAccount": "Excluir conta",
|
||||
"ExcludeAsset": "Ativos ignorados",
|
||||
@@ -867,6 +872,7 @@
|
||||
"MoveAssetToNode": "Mover ativos para o nó",
|
||||
"MoveToAsset": "Mover para ativos ",
|
||||
"MsgSubscribe": "Assinatura de mensagem",
|
||||
"MsgTemplate": "Modelo de mensagem",
|
||||
"MyAssets": "Meus ativos",
|
||||
"MyTickets": "Iniciados por Mim",
|
||||
"NUMBER_REQUIRED": "Deve conter números",
|
||||
@@ -1023,6 +1029,7 @@
|
||||
"PleaseAgreeToTheTerms": "Por favor, concorde com os termos",
|
||||
"PleaseEnterReason": "Por favor, insira o motivo",
|
||||
"PleaseSelect": "Por favor, selecione",
|
||||
"PleaseSelectAssetOrNode": "Por favor, selecione um ativo ou nó",
|
||||
"PleaseSelectTheDataYouWantToCheck": "Por favor, selecione os dados que deseja marcar.",
|
||||
"PolicyName": "Nome da Política",
|
||||
"Port": "Porta",
|
||||
@@ -1040,6 +1047,7 @@
|
||||
"PrivilegedOnly": "Apenas contas privilegiadas",
|
||||
"PrivilegedTemplate": "Privilégios",
|
||||
"Processing": "Em processamento",
|
||||
"ProcessingMessage": "Tarefa em andamento, por favor, aguarde ⏳",
|
||||
"Product": "Produto",
|
||||
"ProfileSetting": "Configurações de Informações Pessoais",
|
||||
"Project": "Nome do Projeto",
|
||||
@@ -1154,6 +1162,7 @@
|
||||
"ResetSSHKeySuccessMsg": "A tarefa de envio de e-mail foi enviada, o usuário receberá um e-mail de chave de redefinição mais tarde",
|
||||
"ResetSSHKeyWarningMsg": "Você tem certeza que deseja enviar o e-mail para resetar a chave SSH do usuário?",
|
||||
"ResetSecret": " Pode alterar a senha ",
|
||||
"ResetSuccessfully": "Redefinição bem-sucedida.",
|
||||
"ResolveSelected": "Resolver seleção",
|
||||
"Resource": "Recursos",
|
||||
"ResourceType": "Tipo de recurso",
|
||||
@@ -1415,6 +1424,7 @@
|
||||
"TemplateCreate": "Criar modelo",
|
||||
"TemplateHelpText": "Ao selecionar para adicionar um modelo, uma conta que não existe no ativo será criativamente criada e impulsionada",
|
||||
"TemplateManagement": "Lista de Modelos",
|
||||
"TemplateVariablesHelpText": "Você pode escolher um modelo e usar {{ key }} no conteúdo do modelo para ler variáveis incorporadas. Observe que apenas a sintaxe {{ }} é suportada; outras sintaxes não são. Por exemplo {% if title %}",
|
||||
"Templates": "Modelo",
|
||||
"TencentCloud": "Nuvem Tencent",
|
||||
"Terminal": "Configuração do componente",
|
||||
@@ -1620,13 +1630,17 @@
|
||||
"assetAddress": "Endereço do ativo",
|
||||
"assetId": "ID do ativo",
|
||||
"assetName": "Nome do ativo",
|
||||
"clickToAdd": "Clique para adicionar",
|
||||
"currentTime": "Hora atual",
|
||||
"description": "Nenhum dado disponível.",
|
||||
"disallowSelfUpdateFields": "Não é permitido alterar o campo atual",
|
||||
"forceEnableMFAHelpText": "Se for habilitado forçosamente, o usuário não pode desativar por conta própria",
|
||||
"isConsoleCanUse": "Se a página de gerenciamento está disponível",
|
||||
"name": "Nome de usuário",
|
||||
"overwriteProtocolsAndPortsMsg": "Esta ação substituirá todos os protocolos e portas. Deseja continuar?",
|
||||
"pleaseSelectAssets": "Por favor, selecione um ativo.",
|
||||
"removeWarningMsg": "Tem certeza de que deseja remover",
|
||||
"selectedAssets": "Ativos selecionados",
|
||||
"setVariable": "Parâmetros de configuração",
|
||||
"userId": "ID do usuário",
|
||||
"userName": "Usuário"
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
"AWS_Int": "AWS (Международный)",
|
||||
"About": "О программе",
|
||||
"Accept": "Принять",
|
||||
"AccessDistribution": "Распределение посещений",
|
||||
"AccessDistribution": "Распределение доступа",
|
||||
"AccessIP": "Белый список IP",
|
||||
"AccessKey": "Ключ доступа",
|
||||
"Account": "Информация об УЗ",
|
||||
"AccountActivities": "Активность учетной записи",
|
||||
"AccountAndPasswordChangeRank": "Изменение пароля аккаунта по рейтингу",
|
||||
"AccountAutomationOverview": "Обзор автоматизации",
|
||||
"AccountAutomationReport": "Автоматизированный отчет по аккаунтам",
|
||||
"AccountAndPasswordChangeRank": "Рейтинг изменений паролей и учётных записей",
|
||||
"AccountAutomationOverview": "Обзор автоматизации УЗ",
|
||||
"AccountAutomationReport": "Отчет об автоматизации учетных записей",
|
||||
"AccountBackup": "Резервное копирование УЗ",
|
||||
"AccountBackupCreate": "Создать резервную копию УЗ",
|
||||
"AccountBackupDetail": "Подробности резервного копирования УЗ",
|
||||
@@ -21,8 +21,8 @@
|
||||
"AccountBackupUpdate": "Обновить резервную копию УЗ",
|
||||
"AccountChangeSecret": "Изменение пароля учетной записи",
|
||||
"AccountChangeSecretDetail": "Подробности изменения пароля учетной записи",
|
||||
"AccountConnectivityStatusDistribution": "Распределение состояния доступности аккаунтов",
|
||||
"AccountCreationSourceDistribution": "Распределение источников создания аккаунтов",
|
||||
"AccountConnectivityStatusDistribution": "Распределение статусов подключаемости УЗ",
|
||||
"AccountCreationSourceDistribution": "Распределение источников создания УЗ",
|
||||
"AccountData": "Данные учетной записи",
|
||||
"AccountDeleteConfirmMsg": "Удаление учетной записи. Продолжить?",
|
||||
"AccountDeleted": "Удаление учетной записи",
|
||||
@@ -33,7 +33,7 @@
|
||||
"AccountDiscoverTaskUpdate": "Обновить задачу обнаружения учетной записи",
|
||||
"AccountExportTips": "Экспортируемая информация содержит зашифрованные данные учетных записей, что относится к конфиденциальной информации. Формат экспорта — зашифрованный zip-файл (если не установлен пароль шифрования, пожалуйста, настройте его в Персональной информации).",
|
||||
"AccountList": "Учетные записи",
|
||||
"AccountOverview": "Обзор аккаунтов",
|
||||
"AccountOverview": "Обзор учетных записей",
|
||||
"AccountPasswordChangeTrends": "Тенденции изменения паролей аккаунтов",
|
||||
"AccountPolicy": "Политика учетной записи",
|
||||
"AccountPolicyHelpText": "При создании учетных записей, не соответствующих требованиям, например: несоответствующий тип ключа, ограничение уникальности, можно выбрать одну из вышеуказанных стратегий.",
|
||||
@@ -42,11 +42,11 @@
|
||||
"AccountPushList": "Публикация УЗ",
|
||||
"AccountPushTask": "Задача публикации учетной записи",
|
||||
"AccountPushUpdate": "Обновление УЗ для публикации",
|
||||
"AccountReport": "Отчет по аккаунтам",
|
||||
"AccountReport": "Отчет по УЗ",
|
||||
"AccountResult": "Успешное или неудачное изменение секрета УЗ",
|
||||
"AccountSelectHelpText": "В список учетных записей добавляется имя пользователя",
|
||||
"AccountSelectHelpText": "В списке учетных записей отображается имя пользователя",
|
||||
"AccountSessions": "Сессии учетной записи",
|
||||
"AccountStatisticsReport": "Отчет по учетным записяч",
|
||||
"AccountStatisticsReport": "Отчет по учетным записям",
|
||||
"AccountStorage": "Хранилище учетных записей",
|
||||
"AccountSummary": "Сводная информация об УЗ",
|
||||
"AccountTemplate": "Шаблон учетной записи",
|
||||
@@ -122,14 +122,13 @@
|
||||
"AppletHelpText": "В процессе загрузки, если приложение отсутствует, оно будет создано; если уже существует, будет выполнено обновление.",
|
||||
"AppletHostCreate": "Добавить сервер RemoteApp",
|
||||
"AppletHostDetail": "Подробности о хосте RemoteApp",
|
||||
"AppletHostSelectHelpMessage": "При подключении к активу выбор машины публикации приложения происходит случайным образом (но предпочтение отдается последней использованной). Если вы хотите назначить активу определенную машину, вы можете использовать следующие теги: [publishing machine: имя машины публикации] или [AppletHost: имя машины публикации]; <br>при выборе учетной записи для машины публикации в следующих ситуациях будет выбрана собственная <b>учетная запись пользователя с тем же именем или собственная учетная запись (начинающаяся с js)</b>, в противном случае будет использоваться публичная учетная запись (начинающаяся с jms):<br> 1. И машина публикации, и приложение поддерживают одновременные подключения; <br> 2. Машина публикации поддерживает одновременные подключения, а приложение — нет, и текущее приложение не использует специализированную учетную запись; <br> 3. Машина публикации не поддерживает одновременные подключения, а приложение может как поддерживать, так и не поддерживать одновременные подключения, и ни одно приложение не использует специализированную учетную запись; <br> Примечание: поддержка одновременных подключений со стороны приложения определяется разработчиком, а поддержка одновременных подключений со стороны хоста определяется настройкой «один пользователь — одна сессия» в конфигурации машины публикации.",
|
||||
"AppletHostSelectHelpMessage": "При подключении к активу выбор сервера публикации приложения происходит случайным образом (но предпочтение отдается последнему использованному).<br>\nЕсли необходимо закрепить сервер публикации за конкретным активом, можно указать один из тегов:\n[AppletHost:имя_сервера], [AppletHostOnly:имя_сервера].<br>\nПри подключении к выбранному серверу публикации и выборе учётной записи применяются следующие правила:<br>\nв перечисленных ниже случаях будет использована <b>учетная запись пользователя с тем же именем</b> или <b>специальная учётная запись (начинающаяся с js)</b>,<br>\nв противном случае будет использоваться общая учётная запись (начинающаяся с jms):<br>\n1. И сервер публикации, и приложение поддерживают одновременные подключения;<br>\n2. Сервер публикации поддерживает одновременные подключения, а приложение — нет, и текущее приложение не использует специальную учётную запись;<br>\n3. Сервер публикации не поддерживает одновременные подключения, а приложение может как поддерживать, так и не поддерживать одновременные подключения, и ни одно приложение не использует специализированную учетную запись;<br>\n\nПримечание: поддержка одновременных подключений со стороны приложения определяется разработчиком,<br>\nа поддержка одновременных подключений со стороны хоста определяется настройкой «один пользователь — одна сессия» в настройках сервера публикации.",
|
||||
"AppletHostUpdate": "Обновить машину публикации RemoteApp",
|
||||
"AppletHostZoneHelpText": "Эта зона принадлежит Системной организации",
|
||||
"AppletHosts": "Хост RemoteApp",
|
||||
"Applets": "RemoteApp",
|
||||
"Applicant": "Заявитель",
|
||||
"ApplicationDetail": "Информация о приложении",
|
||||
"Applicationes": "ApplicationHello",
|
||||
"Applications": "Приложения",
|
||||
"ApplyAsset": "Запрос актива",
|
||||
"ApplyFromCMDFilterRule": "Правила фильтрации команд",
|
||||
@@ -153,10 +152,10 @@
|
||||
"AssetACLCreate": "Создать правило подключения активов",
|
||||
"AssetACLDetail": "Подробности правила подключения активов",
|
||||
"AssetACLUpdate": "Обновить правила подключения активов",
|
||||
"AssetACLs": "Связь активов",
|
||||
"AssetACLs": "Правила подключения активов",
|
||||
"AssetAccount": "Учетные записи",
|
||||
"AssetAccountDetail": "Подробности учетной записи",
|
||||
"AssetActivityReport": "Отчет о деятельности активов",
|
||||
"AssetActivityReport": "Отчет по использованию активов",
|
||||
"AssetAddress": "Активы (IP/Имя хоста)",
|
||||
"AssetAmount": "Количество активов",
|
||||
"AssetAndNode": "Активы/Папки",
|
||||
@@ -170,7 +169,7 @@
|
||||
"AssetListHelpMessage": "Слева расположено дерево активов, правый клик позволяет создать, удалить или изменить папки дерева. Управление активами также организовано в виде папок, справа находятся активы, относящиеся к данной папке",
|
||||
"AssetLoginACLHelpMsg": "Подключение к активам можно контролировать, исходя из IP-адреса пользователя и временного интервала, чтобы определить возможность подключения к активу.",
|
||||
"AssetLoginACLHelpText": "Подключение к активам можно контролировать, исходя из IP-адреса пользователя и временного интервала, чтобы определить возможность подключения к активу.",
|
||||
"AssetLoginTrends": "Тенденция входа в активы",
|
||||
"AssetLoginTrends": "Тенденции входа в активы",
|
||||
"AssetName": "Название актива",
|
||||
"AssetOverview": "Обзор активов",
|
||||
"AssetPermission": "Доступ к активам",
|
||||
@@ -181,8 +180,8 @@
|
||||
"AssetPermissionUpdate": "Обновить правила доступа к активам",
|
||||
"AssetPermsAmount": "Количество правил доступа",
|
||||
"AssetProtocolHelpText": "! Протоколы, поддерживаемые активами, ограничены платформой, нажмите кнопку настроек, чтобы просмотреть настройки протоколов. Если требуется обновление, пожалуйста, обновите платформу",
|
||||
"AssetReport": "Отчет об активах",
|
||||
"AssetStatisticsReport": "Отчет по статистике активов",
|
||||
"AssetReport": "Отчет по активам",
|
||||
"AssetStatisticsReport": "Статистика активов",
|
||||
"AssetTree": "Дерево активов",
|
||||
"AssetTypeDistribution": "Распределение типов активов",
|
||||
"Assets": "Активы",
|
||||
@@ -191,7 +190,7 @@
|
||||
"AssetsSelected": " Активов Выбрано",
|
||||
"AssetsTotal": "Всего активов",
|
||||
"AssignedInfo": "Информация об одобрении",
|
||||
"Assignee": "Ответсвенный исполнитель",
|
||||
"Assignee": "Ответственный исполнитель",
|
||||
"Assignees": "Ожидающие исполнители",
|
||||
"AttrName": "Имя атрибута",
|
||||
"AttrValue": "Значение атрибута",
|
||||
@@ -300,10 +299,10 @@
|
||||
"ChangePasswordOverview": "Обзор изменения паролей",
|
||||
"ChangeSecret": "Изменить секрет",
|
||||
"ChangeSecretAccountHelpText": "Для учетных записей в одном активе, если существует связь переключения su, смена пароля не должна выполняться в одной задаче, а должна быть разделена на две задачи для выполнения отдельно.",
|
||||
"ChangeSecretDashboard": "Панель смены пароля аккаунта",
|
||||
"ChangeSecretDashboard": "Панель смены секрета УЗ",
|
||||
"ChangeSecretFailAccounts": "Неудачная смена секрета УЗ",
|
||||
"ChangeSecretParams": "Параметры изменения секрета",
|
||||
"ChangeSecretStatus": "Состояние смены пароля",
|
||||
"ChangeSecretStatus": "Состояние смены секрета",
|
||||
"ChangeSecretTask": "Задача смены секрета УЗ",
|
||||
"ChangeViewHelpText": "Нажмите, чтобы переключиться на другой вид",
|
||||
"Chat": "Чат",
|
||||
@@ -352,7 +351,6 @@
|
||||
"CommandFilterACLHelpMsg": "С помощью фильтрации команд вы можете контролировать возможность отправки команд активам. В зависимости от установленных вами правил некоторые команды могут быть разрешены, а другие запрещены.",
|
||||
"CommandFilterACLHelpText": "С помощью фильтрации команд вы можете контролировать возможность отправки команд активам. В зависимости от установленных вами правил некоторые команды могут быть разрешены, а другие запрещены.",
|
||||
"CommandFilterACLUpdate": "Обновление правила фильтрации команд",
|
||||
"CommandFilterACLs": "Правила фильтрации команд",
|
||||
"CommandFilterRuleContentHelpText": "По одной команде в строке",
|
||||
"CommandFilterRules": "Правила фильтрации команд",
|
||||
"CommandGroup": "Группа команд",
|
||||
@@ -366,7 +364,7 @@
|
||||
"CommandsTotal": "Всего команд",
|
||||
"Comment": "Описание",
|
||||
"CommentHelpText": "Описание будет отображаться при наведении курсора на дереве активов пользователя на странице Luna. Обычные пользователи могут просматривать эти комментарии, поэтому не указывайте конфиденциальную информацию.",
|
||||
"Common": "Универсальный",
|
||||
"Common": "Общий",
|
||||
"CommunityEdition": "Редакция Community",
|
||||
"Component": "Компонент",
|
||||
"ComponentMonitor": "Мониторинг",
|
||||
@@ -390,13 +388,13 @@
|
||||
"ConnectMethodAclDetail": "Детали правила метода подключения",
|
||||
"ConnectWebSocketError": "Соединение с веб-сокетом не удалось",
|
||||
"Connectable": "Возможно подключение",
|
||||
"ConnectedDirectoryServices": "Связанная служба каталога",
|
||||
"ConnectionCount": "Количество соединений",
|
||||
"ConnectedDirectoryServices": "Связанные службы каталога",
|
||||
"ConnectionCount": "Количество подключений",
|
||||
"ConnectionDropped": "Соединение разорвано",
|
||||
"ConnectionToken": "Токен подключения",
|
||||
"ConnectionTokenList": "Токен подключения - это информация для аутентификации, которая используется для совмещения аутентификации и подключения активов. Он поддерживает вход пользователя в активы одним щелчком мыши. В настоящее время поддерживаются следующие компоненты: koko, lion, magnus, razor и т.д.",
|
||||
"Console": "Консоль",
|
||||
"ConsoleDashboard": "Консольная панель управления",
|
||||
"ConsoleDashboard": "Панель управления",
|
||||
"Consult": "Консультация",
|
||||
"ContainAttachment": "Содержит вложение",
|
||||
"Containers": "Контейнер",
|
||||
@@ -449,6 +447,10 @@
|
||||
"DangerCommand": "Опасная команда",
|
||||
"DangerousCommandNum": "Всего опасных команд",
|
||||
"Dashboard": "Панель инструментов",
|
||||
"DataMasking": "Маскирование данных",
|
||||
"DataMaskingFieldsPatternHelpTip": "Поддерживается нескольких имён полей, разделённых запятыми, а также использование подстановочного знака *.\nПримеры:\nОдно имя поля: password — выполняется маскирование только поля password.\nНесколько имён полей: password,secret — выполняется маскирование полей password и secret.\nПодстановочный знак *: password* — выполняется маскирование всех полей, имя которых начинается с password.\nПодстановочный знак .*: .*password — выполняется маскирование всех полей, имя которых оканчивается на password",
|
||||
"DataMaskingRuleHelpHelpMsg": "При подключении к активу базы данных результаты запросов могут быть подвергнуты маскированию в соответствии с этим правилом",
|
||||
"DataMaskingRuleHelpHelpText": "При подключении к активу базы данных можно выполнять маскирование результатов запросов в соответствии с этим правилом",
|
||||
"Database": "База данных",
|
||||
"DatabaseCreate": "Создать актив - база данных",
|
||||
"DatabasePort": "Порт протокола базы данных",
|
||||
@@ -489,7 +491,7 @@
|
||||
"DeleteOrgMsg": "Пользователь, Группа пользователей, Актив, Папка, Тег, Зона, Разрешение",
|
||||
"DeleteOrgTitle": "Пожалуйста, сначала удалите следующие ресурсы в организации",
|
||||
"DeleteReleasedAssets": "Удалить освобожденные активы",
|
||||
"DeleteRemoteAccount": "Удалить удаленный аккаунт",
|
||||
"DeleteRemoteAccount": "Удалить УЗ на активе",
|
||||
"DeleteSelected": "Удалить выбранное",
|
||||
"DeleteSuccess": "Успешно удалено",
|
||||
"DeleteSuccessMsg": "Успешно удалено",
|
||||
@@ -539,10 +541,10 @@
|
||||
"EditRecipient": "Изменить получателя",
|
||||
"EditSecret": "Изменить секрет",
|
||||
"Edition": "Версия",
|
||||
"Effective": "Вступить в силу",
|
||||
"Effective": "Активно",
|
||||
"Email": "Электронная почта",
|
||||
"EmailContent": "Настройка содержания",
|
||||
"EmailHelpText": "Пожалуйста, нажмите кнопку 'Отправить', чтобы сохранить текущую конфигурацию, а затем нажмите 'Проверить соединение', чтобы убедиться, что информация вступила в силу",
|
||||
"EmailHelpText": "Пожалуйста, нажмите кнопку 'Отправить', чтобы сохранить текущую конфигурацию, а затем нажмите 'Тестовое соединение', чтобы убедиться, чтобы настройки вступили в силу",
|
||||
"EmailTemplate": "Шаблон",
|
||||
"EmailTemplateHelpTip": "Шаблон электронной почты используется для отправки электронных писем и включает префикс темы электронного письма и содержимое электронного письма.",
|
||||
"EmailTest": "Тестовое соединение",
|
||||
@@ -573,8 +575,9 @@
|
||||
"EsIndex": "es предоставляет индекс по умолчанию: jumpserver. Если включено создание индекса по дате, то введенное значение будет использоваться в качестве префикса индекса",
|
||||
"EsUrl": "Не может содержать специальные символы `#`; например: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "Каждый",
|
||||
"Example": "Пример",
|
||||
"Exclude": "Не содержит",
|
||||
"ExcludeAccount": "Исключенные аккаунты",
|
||||
"ExcludeAccount": "Исключить учетную запись",
|
||||
"ExcludeAsset": "Пропущенные активы",
|
||||
"ExcludeSymbol": "Исключить символ",
|
||||
"ExecCloudSyncErrorMsg": "Выполнить",
|
||||
@@ -673,7 +676,7 @@
|
||||
"HistoryDate": "Дата",
|
||||
"HistoryPassword": "История паролей",
|
||||
"HistoryRecord": "История записей",
|
||||
"Host": "Хост.",
|
||||
"Host": "Хост",
|
||||
"HostCreate": "Создать актив - хост",
|
||||
"HostDeployment": "Развертывание машины публикации",
|
||||
"HostUpdate": "Обновить актив - хост",
|
||||
@@ -689,7 +692,7 @@
|
||||
"IPNetworkSegment": "Диапазон IP",
|
||||
"IPType": "Тип IP",
|
||||
"Id": "ID",
|
||||
"IdP": "Поставщик идентификации",
|
||||
"IdP": "Поставщик аутентификации",
|
||||
"IdeaContent": "Я хочу, чтобы ты выступил в роли терминала Linux. Я буду вводить команды, а ты будешь отвечать тем, что должен отображать терминал. Я прошу отвечать только в одном уникальном блоке кода, без дополнительных комментариев. Когда мне нужно будет что-то тебе сказать, я помещу текст в фигурные скобки{замечание текста}.",
|
||||
"IdeaTitle": "🌱 терминал Linux",
|
||||
"IdentityDomain": "Домен идентификации",
|
||||
@@ -726,7 +729,7 @@
|
||||
"InstanceName": "Название экземпляра",
|
||||
"InstanceNamePartIp": "Название экземпляра и часть IP",
|
||||
"InstancePlatformName": "Название платформы экземпляра",
|
||||
"Integration": "Интеграция приложений",
|
||||
"Integration": "Интеграция",
|
||||
"Interface": "Сетевой интерфейс",
|
||||
"InterfaceSettings": "Настройки интерфейса",
|
||||
"Interval": "Интервал",
|
||||
@@ -782,7 +785,7 @@
|
||||
"LicenseForTest": "Лицензия на тестирование, данная лицензия предназначена только для тестирования (poc) и демонстрации",
|
||||
"LicenseReachedAssetAmountLimit": "Количество активов превышает лимит, установленный лицензией",
|
||||
"LicenseWillBe": "Лицензия истекает через",
|
||||
"LinkedDomains": "Связанный домен",
|
||||
"LinkedDomains": "Связанные домены",
|
||||
"ListPreference": "Настройка списка",
|
||||
"LoadTemplate": "Загрузить из шаблона",
|
||||
"Loading": "Загрузка",
|
||||
@@ -798,7 +801,7 @@
|
||||
"LoginConfirm": "Проверка входа",
|
||||
"LoginConfirmUser": "Подтверждает вход",
|
||||
"LoginCount": "Количество входов",
|
||||
"LoginCtyDistribution": "Распределение городов входа",
|
||||
"LoginCtyDistribution": "Вход по городам",
|
||||
"LoginDate": "Дата входа",
|
||||
"LoginFailed": "Вход не удался",
|
||||
"LoginFrom": "Источник входа",
|
||||
@@ -858,7 +861,7 @@
|
||||
"Min": "Мин.",
|
||||
"MinNumber30": "Число должно быть больше или равно 30",
|
||||
"Modify": "Изменить",
|
||||
"ModifyTheTargetUserTopTank": "Изменение рейтинга целевых пользователей",
|
||||
"ModifyTheTargetUserTopTank": "Топ пользователей с измененными паролями",
|
||||
"Module": "Модуль",
|
||||
"Monday": "Понедельник",
|
||||
"Monitor": "Мониторинг",
|
||||
@@ -868,6 +871,7 @@
|
||||
"MoveAssetToNode": "Переместить актив в папку",
|
||||
"MoveToAsset": "Переместить в активы",
|
||||
"MsgSubscribe": "Подписка",
|
||||
"MsgTemplate": "Шаблон сообщения",
|
||||
"MyAssets": "Мои активы",
|
||||
"MyTickets": "Мои запросы",
|
||||
"NUMBER_REQUIRED": "Должен содержать цифры",
|
||||
@@ -875,7 +879,7 @@
|
||||
"NavHelp": "Ссылки на навигационную панель",
|
||||
"Navigation": "Навигация",
|
||||
"NeedReLogin": "Необходимо войти повторно",
|
||||
"NeedUpdatePassword": "Необходимость изменения пароля",
|
||||
"NeedUpdatePassword": "Требуется смена пароля",
|
||||
"NeverLogin": "Никогда не входил",
|
||||
"New": "Создать",
|
||||
"NewAccountsFound": "Найдены новые УЗ",
|
||||
@@ -912,7 +916,7 @@
|
||||
"NonRunnableAssets": "Неработающие активы",
|
||||
"None": "Нет",
|
||||
"NormalLoad": "Нормальная",
|
||||
"NotEnableMfa": "MFA не активирована",
|
||||
"NotEnableMfa": "МФА не активирована",
|
||||
"NotEqual": "Не равно",
|
||||
"NotSet": "Не установлено",
|
||||
"NotSpecialEmoji": "Ввод специальных эмодзи не допускается",
|
||||
@@ -950,10 +954,10 @@
|
||||
"OpenStatus": "На одобрении",
|
||||
"OpenTicket": "Создать заявку",
|
||||
"OperateLog": "Журналы операций",
|
||||
"OperatingSystemDistributionOfLoginAssets": "Распределение операционных систем для входа в активы",
|
||||
"OperatingSystemDistributionOfLoginAssets": "Распределение ОС для входа в активы",
|
||||
"OperationLogNum": "Журналы операций",
|
||||
"Operator": "Операционный пользователь",
|
||||
"OperatorGeographicDistribution": "Географическое распределение активных пользователей",
|
||||
"Operator": "Пользователь‑исполнитель",
|
||||
"OperatorGeographicDistribution": "Распределение по регионам пользователей‑исполнителей",
|
||||
"Options": "Параметры",
|
||||
"OracleDBNameHelpText": "Введите SID или имя службы (Service Name) базы данных Oracle",
|
||||
"OrgAdmin": "Администратор организации",
|
||||
@@ -976,13 +980,13 @@
|
||||
"Overview": "Обзор",
|
||||
"PageNext": "Дальше",
|
||||
"PagePrev": "Назад",
|
||||
"PamDashboard": "Панель привилегированных аккаунтов",
|
||||
"PamDashboard": "Панель PAM",
|
||||
"Params": "Параметры",
|
||||
"ParamsHelpText": "Настройки параметров пароля в настоящее время действуют только для активов типа хост.",
|
||||
"PassKey": "Ключ доступа",
|
||||
"Passkey": "Passkey",
|
||||
"PasskeyAddDisableInfo": "Ваш источник аутентификации - {source}, добавление Passkey не поддерживается",
|
||||
"PasskeySummary": "Безпарольная биометрическая аутентификация",
|
||||
"PasskeySummary": "Аутентификация с биометрией без пароля",
|
||||
"Passphrase": "Пароль ключа",
|
||||
"Password": "Пароль",
|
||||
"PasswordAndSSHKey": "Пароль и SSH-ключ",
|
||||
@@ -1024,7 +1028,8 @@
|
||||
"PleaseAgreeToTheTerms": "Пожалуйста, согласитесь с условиями",
|
||||
"PleaseEnterReason": "Введите причину",
|
||||
"PleaseSelect": "Пожалуйста, выберите ",
|
||||
"PleaseSelectTheDataYouWantToCheck": "Пожалуйста, выберите данные для отметки",
|
||||
"PleaseSelectAssetOrNode": "Пожалуйста, выберите актив или узел.",
|
||||
"PleaseSelectTheDataYouWantToCheck": "Пожалуйста, выберите данные, которые нужно отметить",
|
||||
"PolicyName": "Название политики",
|
||||
"Port": "Порт",
|
||||
"Ports": "Портов",
|
||||
@@ -1041,6 +1046,7 @@
|
||||
"PrivilegedOnly": "Только привилегированные",
|
||||
"PrivilegedTemplate": "Привилегированные",
|
||||
"Processing": "В процессе",
|
||||
"ProcessingMessage": "Задача выполняется, пожалуйста, подождите ⏳",
|
||||
"Product": "Продукт",
|
||||
"ProfileSetting": "Данные профиля",
|
||||
"Project": "Название проекта",
|
||||
@@ -1073,7 +1079,7 @@
|
||||
"QuickJob": "Быстрые задания",
|
||||
"QuickUpdate": "Быстрое обновление",
|
||||
"Radius": "Radius",
|
||||
"RankByNumberOfAssetAccounts": "Рейтинг количества аккаунтов активов",
|
||||
"RankByNumberOfAssetAccounts": "Рейтинг активов по количеству УЗ",
|
||||
"Ranking": "Рейтинг",
|
||||
"RazorNotSupport": "Сессия RDP-клиента, мониторинг пока не поддерживается",
|
||||
"ReLogin": "Войти снова",
|
||||
@@ -1112,8 +1118,8 @@
|
||||
"RelevantCommand": "Команда",
|
||||
"RelevantSystemUser": "Системный пользователь",
|
||||
"RemoteAddr": "Удалённый адрес",
|
||||
"RemoteAssetFoundAccountDeleteMsg": "Удалить аккаунты, обнаруженные с удалённых активов",
|
||||
"RemoteLoginProtocolUsageDistribution": "Распределение использования протоколов удаленного входа",
|
||||
"RemoteAssetFoundAccountDeleteMsg": "Удалить УЗ, обнаруженные на удалённых активах",
|
||||
"RemoteLoginProtocolUsageDistribution": "Распределение протоколов входа в активы",
|
||||
"Remove": "Удалить",
|
||||
"RemoveAssetFromNode": "Удалить активы из папки",
|
||||
"RemoveSelected": "Удалить выбранное",
|
||||
@@ -1132,7 +1138,7 @@
|
||||
"Reply": "Ответ",
|
||||
"Report": "Отчет",
|
||||
"ReportType": "Тип отчета",
|
||||
"RequestAssetPerm": "Запросить доступ к автивам",
|
||||
"RequestAssetPerm": "Запросить доступ к активам",
|
||||
"RequestPerm": "Запрос доступа",
|
||||
"RequestTickets": "Новая заявка",
|
||||
"RequiredAssetOrNode": "Пожалуйста, выберите хотя бы один актив или папку",
|
||||
@@ -1155,6 +1161,7 @@
|
||||
"ResetSSHKeySuccessMsg": "Задача отправки электронных писем принята, пользователи вскоре получат письма со ссылкой для сброса",
|
||||
"ResetSSHKeyWarningMsg": "Вы уверены, что хотите отправить письмо для сброса SSH-ключа пользователю?",
|
||||
"ResetSecret": "Секрет можно изменить",
|
||||
"ResetSuccessfully": "Сброс выполнен успешно",
|
||||
"ResolveSelected": "Решение установлено",
|
||||
"Resource": "Ресурсы",
|
||||
"ResourceType": "Тип ресурса",
|
||||
@@ -1213,7 +1220,7 @@
|
||||
"SSHKeyOfProfileSSHUpdatePage": "Вы можете нажать кнопку ниже для сброса и загрузки ключа, или скопировать свой SSH-ключ и отправить его.",
|
||||
"SSHPort": "SSH-порт",
|
||||
"SSHSecretKey": "SSH ключ",
|
||||
"SSO": "Единственный вход",
|
||||
"SSO": "Единый вход (SSO)",
|
||||
"SafeCommand": "Безопасная команда",
|
||||
"SameAccount": "Своя учетная запись",
|
||||
"SameAccountTip": "Учетная запись с тем же именем пользователя, что и у авторизованных пользователей",
|
||||
@@ -1235,7 +1242,7 @@
|
||||
"SecretKey": "Ключ",
|
||||
"SecretKeyStrategy": "Парольная политика",
|
||||
"SecretReset": "Сброс секрета",
|
||||
"SecretType": "Тип пароля",
|
||||
"SecretType": "Тип секрета",
|
||||
"Secure": "Безопасность",
|
||||
"Security": "Настройки безопасности",
|
||||
"Select": "Выберите",
|
||||
@@ -1402,7 +1409,7 @@
|
||||
"Task": "Задача",
|
||||
"TaskDetail": "Сведения о задаче",
|
||||
"TaskDone": "Задача завершена",
|
||||
"TaskExecutionTrends": "Тренд выполнения задач",
|
||||
"TaskExecutionTrends": "Тенденции выполнения задач",
|
||||
"TaskID": "ID задачи",
|
||||
"TaskList": "Задачи",
|
||||
"TaskMonitor": "Мониторинг",
|
||||
@@ -1416,6 +1423,7 @@
|
||||
"TemplateCreate": "Создать шаблон",
|
||||
"TemplateHelpText": "При выборе шаблона для добавления учетные записи, которые не существуют в активе, будут автоматически созданы и опубликованы",
|
||||
"TemplateManagement": "Шаблоны",
|
||||
"TemplateVariablesHelpText": "Вы можете выбрать шаблон и использовать {{ key }} в его содержимом для обращения ко встроенным переменным. Поддерживается только синтаксис {{ }}, другие виды синтаксиса не поддерживаются. Например, {% if title %} — не поддерживается",
|
||||
"Templates": "Шаблоны",
|
||||
"TencentCloud": "Tencent Cloud",
|
||||
"Terminal": "Компоненты",
|
||||
@@ -1436,7 +1444,7 @@
|
||||
"TestPortErrorMsg": "Ошибка порта, пожалуйста, введите заново",
|
||||
"TestSelected": "Проверить выбранное",
|
||||
"TestSuccessMsg": "Тест пройден успешно",
|
||||
"ThirdPartyMfaHelpText": "Способы входа через третьих лиц включают: OpenID, CAS, SAML2, OAuth2, Корпоративный WeChat, Feishu, Lark, Slack, DingTalk.",
|
||||
"ThirdPartyMfaHelpText": "Поддерживаются сторонние сервисы: OpenID, CAS, SAML2, OAuth2, WeCom, Feishu, Lark, Slack, DingTalk",
|
||||
"Thursday": "Четверг",
|
||||
"Ticket": "Система заявок",
|
||||
"TicketDetail": "Сведения о заяве",
|
||||
@@ -1450,7 +1458,7 @@
|
||||
"TimeDelta": "Продолжительность",
|
||||
"TimeExpression": "Выражение времени",
|
||||
"Timeout": "Таймаут",
|
||||
"Timeout(s)": "Превышение времени (сек)",
|
||||
"Timeout(s)": "Время ожидания (сек)",
|
||||
"TimeoutHelpText": "Если это значение равно -1, таймаут не указан.",
|
||||
"Timer": "Таймер",
|
||||
"TimerExecution": "Таймер выполнения",
|
||||
@@ -1459,7 +1467,7 @@
|
||||
"Today": "Сегодня",
|
||||
"TodayFailedConnections": "Сбой подключений сегодня",
|
||||
"Token": "Токен",
|
||||
"TopRankOfOperateUsers": "Рейтинг операционных пользователей",
|
||||
"TopRankOfOperateUsers": "Топ пользователей выполнявших смену пароля",
|
||||
"Total": "Всего",
|
||||
"TotalAccounts": "Всего учетных записей",
|
||||
"TotalJobFailed": "Неудачное выполнение",
|
||||
@@ -1505,7 +1513,7 @@
|
||||
"UpdateNodeAssetHardwareInfo": "Обновление информации об оборудовании активов в папке",
|
||||
"UpdatePlatformHelpText": "Актив будет обновлен только в том случае, если исходный тип платформы совпадает с выбранным типом платформы. Если типы платформы до и после обновления различаются, он не будет обновлен.",
|
||||
"UpdateSSHKey": "Изменить открытый ключ ssh",
|
||||
"UpdateSelected": "Редактировать выбранное",
|
||||
"UpdateSelected": "Обновить выбранные",
|
||||
"UpdateSuccessMsg": "Успешно обновлено!",
|
||||
"Updated": "Обновлено",
|
||||
"UpgradeEnterpriseEdition": "Обновить до корпоративной версии",
|
||||
@@ -1525,7 +1533,7 @@
|
||||
"User": "Пользователь",
|
||||
"UserACLss": "Правила входа пользователей",
|
||||
"UserAssetActivity": "Активность пользователя/актива",
|
||||
"UserChangePasswordReport": "Отчет о смене пароля пользователя",
|
||||
"UserChangePasswordReport": "Отчет о смене пароля пользователями",
|
||||
"UserCreate": "Создать пользователя",
|
||||
"UserData": "Данные пользователя",
|
||||
"UserDetail": "Информация о пользователе",
|
||||
@@ -1546,12 +1554,12 @@
|
||||
"UserLoginACLs": "Управление входом пользователей",
|
||||
"UserLoginLimit": "Ограничение для пользователей",
|
||||
"UserLoginReport": "Отчет о входах пользователей",
|
||||
"UserLoginTrend": "Тренд входа пользователей",
|
||||
"UserLoginTrends": "Тренды входа пользователей",
|
||||
"UserModificationTrends": "Тенденции изменений пользователями",
|
||||
"UserLoginTrend": "Тенденции входа по учётным записям",
|
||||
"UserLoginTrends": "Тенденции входа пользователей",
|
||||
"UserModificationTrends": "Тенденции смены пароля пользователями",
|
||||
"UserOverview": "Обзор пользователей",
|
||||
"UserPasswordChangeLog": "Журнал смены пароля пользователя",
|
||||
"UserReport": "Отчет о пользователях",
|
||||
"UserReport": "Отчет по пользователям",
|
||||
"UserSession": "Сессии к активам",
|
||||
"UserSwitchFrom": "Переключение su с",
|
||||
"UserUpdate": "Обновить пользователя",
|
||||
@@ -1583,7 +1591,7 @@
|
||||
"VirtualApp": "Виртуальное приложение",
|
||||
"VirtualAppDetail": "Сведения о виртуальном приложении",
|
||||
"VirtualApps": "Виртуальные приложения",
|
||||
"VisitTimeDistribution": "Посещение во временные интервалы",
|
||||
"VisitTimeDistribution": "Распределение доступа по периодам",
|
||||
"Visits": "Посещения",
|
||||
"Volcengine": "Volcengine",
|
||||
"Warning": "Предупреждение",
|
||||
@@ -1603,7 +1611,7 @@
|
||||
"Week": "Неделя",
|
||||
"WeekAdd": "Новое за неделю",
|
||||
"WeekOrTime": "День/Время",
|
||||
"WeeklyGrowthTrend": "Тренды нового на этой неделе",
|
||||
"WeeklyGrowthTrend": "Тенденция новых объектов за эту неделю",
|
||||
"WildcardsAllowed": "Допустимые подстановочные знаки",
|
||||
"WindowsPushHelpText": "Отправка ключа для активов Windows пока не поддерживается",
|
||||
"WordSep": " ",
|
||||
@@ -1621,14 +1629,18 @@
|
||||
"assetAddress": "Адрес актива",
|
||||
"assetId": "ID актива",
|
||||
"assetName": "Название актива",
|
||||
"clickToAdd": "Нажмите, чтобы добавить",
|
||||
"currentTime": "Текущее время",
|
||||
"description": "Нет данных",
|
||||
"disallowSelfUpdateFields": "Не разрешено самостоятельно изменять текущие поля.",
|
||||
"forceEnableMFAHelpText": "При принудительном включении пользователь не может отключить самостоятельно",
|
||||
"isConsoleCanUse": "Доступна ли Консоль",
|
||||
"name": "Имя пользователя",
|
||||
"overwriteProtocolsAndPortsMsg": "Это действие заменит все протоколы и порты. Продолжить?",
|
||||
"pleaseSelectAssets": "Пожалуйста, выберите актив",
|
||||
"removeWarningMsg": "Вы уверены, что хотите удалить",
|
||||
"selectedAssets": "Выбранные активы",
|
||||
"setVariable": "Задать переменную",
|
||||
"userId": "ID пользователя",
|
||||
"userName": "Имя пользовател"
|
||||
"userName": "Имя пользователя"
|
||||
}
|
||||
@@ -447,6 +447,10 @@
|
||||
"DangerCommand": "Lệnh nguy hiểm",
|
||||
"DangerousCommandNum": "Số lệnh nguy hiểm",
|
||||
"Dashboard": "Bảng điều khiển",
|
||||
"DataMasking": "Dữ liệu ẩn danh",
|
||||
"DataMaskingFieldsPatternHelpTip": "Hỗ trợ nhiều tên trường, phân tách bằng dấu phẩy, hỗ trợ ký tự đại diện *\nVí dụ:\nTên trường đơn: password biểu thị chỉ làm mờ trường password\nNhiều tên trường: password,secret biểu thị làm mờ cả trường password và secret\nKý tự đại diện *: password* biểu thị làm mờ các trường có tiền tố là password\nKý tự đại diện *: .*password biểu thị làm mờ các trường có hậu tố là password",
|
||||
"DataMaskingRuleHelpHelpMsg": "Khi kết nối với cơ sở dữ liệu tài sản, có thể áp dụng quy tắc này để làm mờ kết quả truy vấn.",
|
||||
"DataMaskingRuleHelpHelpText": "Khi kết nối tài sản cơ sở dữ liệu, có thể tiến hành làm mờ kết quả truy vấn theo quy tắc này.",
|
||||
"Database": "Cơ sở dữ liệu",
|
||||
"DatabaseCreate": "Tạo tài sản-Cơ sở dữ liệu",
|
||||
"DatabasePort": "cổng giao thức cơ sở dữ liệu",
|
||||
@@ -571,6 +575,7 @@
|
||||
"EsIndex": "Cung cấp chỉ mục mặc định: jumpserver. Nếu kích hoạt tạo chỉ mục theo ngày, thì giá trị nhập vào sẽ được sử dụng như tiền tố chỉ mục",
|
||||
"EsUrl": "Không được chứa ký tự đặc biệt `#`; ví dụ: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "Mỗi",
|
||||
"Example": "Giá trị mẫu",
|
||||
"Exclude": "Không chứa",
|
||||
"ExcludeAccount": "Loại trừ tài khoản",
|
||||
"ExcludeAsset": "Tài sản đã bỏ qua",
|
||||
@@ -866,6 +871,7 @@
|
||||
"MoveAssetToNode": "Di chuyển tài sản đến nút",
|
||||
"MoveToAsset": "Di chuyển đến tài sản",
|
||||
"MsgSubscribe": "Đăng ký tin nhắn",
|
||||
"MsgTemplate": "Mẫu tin nhắn",
|
||||
"MyAssets": "Tài sản của tôi",
|
||||
"MyTickets": "Những gì tôi đã khởi xướng",
|
||||
"NUMBER_REQUIRED": "Phải chứa chữ số",
|
||||
@@ -1022,6 +1028,7 @@
|
||||
"PleaseAgreeToTheTerms": "Vui lòng đồng ý với các điều khoản",
|
||||
"PleaseEnterReason": "Vui lòng nhập lý do",
|
||||
"PleaseSelect": "Vui lòng chọn",
|
||||
"PleaseSelectAssetOrNode": "Vui lòng chọn tài sản hoặc nút",
|
||||
"PleaseSelectTheDataYouWantToCheck": "Vui lòng chọn dữ liệu cần đánh dấu",
|
||||
"PolicyName": "Tên chính sách",
|
||||
"Port": "Cổng",
|
||||
@@ -1039,6 +1046,7 @@
|
||||
"PrivilegedOnly": "Chỉ tài khoản đặc quyền",
|
||||
"PrivilegedTemplate": "Đặc quyền",
|
||||
"Processing": "Đang xử lý",
|
||||
"ProcessingMessage": "Nhiệm vụ đang tiến hành, xin vui lòng chờ 🍃.",
|
||||
"Product": "Sản phẩm",
|
||||
"ProfileSetting": "Cài đặt thông tin cá nhân",
|
||||
"Project": "Tên dự án",
|
||||
@@ -1153,6 +1161,7 @@
|
||||
"ResetSSHKeySuccessMsg": "Gửi email nhiệm vụ đã được gửi, người dùng sẽ nhận được email reset khóa sau.",
|
||||
"ResetSSHKeyWarningMsg": "Bạn có chắc chắn muốn gửi email đặt lại SSH Key cho người dùng không?",
|
||||
"ResetSecret": "Có thể chỉnh sửa mật khẩu",
|
||||
"ResetSuccessfully": "Đặt lại thành công",
|
||||
"ResolveSelected": "Giải quyết đã chọn",
|
||||
"Resource": "Tài nguyên",
|
||||
"ResourceType": "Loại tài nguyên",
|
||||
@@ -1414,6 +1423,7 @@
|
||||
"TemplateCreate": "Tạo mẫu",
|
||||
"TemplateHelpText": "Khi chọn mẫu thêm, tài khoản không tồn tại dưới tài sản sẽ tự động được tạo và gửi thông báo",
|
||||
"TemplateManagement": "Quản lý mẫu",
|
||||
"TemplateVariablesHelpText": "Bạn có thể chọn một mẫu để sử dụng {{ key }} đọc các biến tích hợp sẵn, lưu ý: chỉ hỗ trợ cú pháp {{ }}, các cú pháp khác không được hỗ trợ. Ví dụ {% if title %}",
|
||||
"Templates": "Mẫu",
|
||||
"TencentCloud": "Tencent Cloud",
|
||||
"Terminal": "Cài đặt thành phần",
|
||||
@@ -1619,13 +1629,17 @@
|
||||
"assetAddress": "Địa chỉ tài sản",
|
||||
"assetId": "ID tài sản",
|
||||
"assetName": "Tên tài sản",
|
||||
"clickToAdd": "Nhấp để thêm",
|
||||
"currentTime": "Thời gian hiện tại",
|
||||
"description": "Chưa có dữ liệu.",
|
||||
"disallowSelfUpdateFields": "Không cho phép tự chỉnh sửa trường hiện tại",
|
||||
"forceEnableMFAHelpText": "Nếu buộc kích hoạt, người dùng sẽ không thể tự động vô hiệu hóa",
|
||||
"isConsoleCanUse": "Trang quản lý có khả dụng không",
|
||||
"name": "Tên người dùng",
|
||||
"overwriteProtocolsAndPortsMsg": "Hành động này sẽ ghi đè lên tất cả các giao thức và cổng, có tiếp tục không?",
|
||||
"pleaseSelectAssets": "Vui lòng chọn tài sản.",
|
||||
"removeWarningMsg": "Bạn có chắc chắn muốn xóa bỏ?",
|
||||
"selectedAssets": "Tài sản đã chọn",
|
||||
"setVariable": "Cài đặt tham số",
|
||||
"userId": "ID người dùng",
|
||||
"userName": "Tên người dùng"
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"AppletHelpText": "在上传过程中,如果应用不存在,则创建该应用;如果已存在,则进行应用更新。",
|
||||
"AppletHostCreate": "添加远程应用发布机",
|
||||
"AppletHostDetail": "远程应用发布机详情",
|
||||
"AppletHostSelectHelpMessage": "连接资产时,应用发布机选择是随机的(但优先选择上次使用的),如果想为某个资产固定发布机,可以指定标签 <发布机:发布机名称> 或 <AppletHost:发布机名称>; <br>连接该发布机选择账号时,以下情况会选择用户的 <b>同名账号 或 专有账号(js开头)</b>,否则使用公用账号(jms开头):<br> 1. 发布机和应用都支持并发; <br> 2. 发布机支持并发,应用不支持并发,当前应用没有使用专有账号; <br> 3. 发布机不支持并发,应用支持并发或不支持,没有任一应用使用专有账号; <br> 注意: 应用支不支持并发是开发者决定,主机支不支持是发布机配置中的 单用户单会话决定",
|
||||
"AppletHostSelectHelpMessage": "连接资产时,应用发布机选择是随机的(但优先选择上次使用的),如果想为某个资产固定发布机,可以指定标签 <发布机:发布机名称>、<AppletHost:发布机名称>、<仅发布机:发布机名称>、 <AppletHostOnly:发布机名称>; <br>连接该发布机选择账号时,以下情况会选择用户的 <b>同名账号 或 专有账号(js开头)</b>,否则使用公用账号(jms开头):<br> 1. 发布机和应用都支持并发; <br> 2. 发布机支持并发,应用不支持并发,当前应用没有使用专有账号; <br> 3. 发布机不支持并发,应用支持并发或不支持,没有任一应用使用专有账号; <br> 注意: 应用支不支持并发是开发者决定,主机支不支持是发布机配置中的 单用户单会话决定",
|
||||
"AppletHostUpdate": "更新远程应用发布机",
|
||||
"AppletHostZoneHelpText": "这里的网域属于 System 组织",
|
||||
"AppletHosts": "应用发布机",
|
||||
@@ -447,6 +447,10 @@
|
||||
"DangerCommand": "危险命令",
|
||||
"DangerousCommandNum": "危险命令数",
|
||||
"Dashboard": "仪表盘",
|
||||
"DataMasking": "数据脱敏",
|
||||
"DataMaskingFieldsPatternHelpTip": "支持多个字段名,逗号分隔,支持通配符*\n 例如: \n单字段名: password 表示只脱敏 password 字段\n多个字段名: password,secret 表示脱敏 password 和 secret 字段\n通配符*: password* 表示脱敏字段名中包含 password 前缀的字段\n通配符*: .*password 表示脱敏字段名中包含 password 后缀的字段\n",
|
||||
"DataMaskingRuleHelpHelpMsg": "连接数据库资产时,可以根据此规则对查询结果进行脱敏",
|
||||
"DataMaskingRuleHelpHelpText": "连接数据库资产时,可以根据此规则对查询结果进行脱敏",
|
||||
"Database": "数据库",
|
||||
"DatabaseCreate": "创建资产-数据库",
|
||||
"DatabasePort": "数据库协议端口",
|
||||
@@ -571,10 +575,11 @@
|
||||
"EsIndex": "es 提供默认 index:jumpserver。如果开启按日期建立索引,那么输入的值会作为索引前缀",
|
||||
"EsUrl": "不能包含特殊字符 `#`;eg: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "每",
|
||||
"Example": "示例值",
|
||||
"Exclude": "不包含",
|
||||
"ExcludeAccount": "排除账号",
|
||||
"ExcludeAsset": "跳过的资产",
|
||||
"ExcludeSymbol": "排除字符",
|
||||
"ExcludeAccount": "排除账号",
|
||||
"ExecCloudSyncErrorMsg": "云账号配置不完整,请更新后重试",
|
||||
"Execute": "执行",
|
||||
"ExecuteAfterSaving": "保存后执行",
|
||||
@@ -724,7 +729,7 @@
|
||||
"InstanceName": "实例名称",
|
||||
"InstanceNamePartIp": "实例名称和部分IP",
|
||||
"InstancePlatformName": "实例平台名称",
|
||||
"Integration": "应用集成",
|
||||
"Integration": "集成",
|
||||
"Interface": "网络接口",
|
||||
"InterfaceSettings": "界面设置",
|
||||
"Interval": "间隔",
|
||||
@@ -866,6 +871,7 @@
|
||||
"MoveAssetToNode": "移动资产到节点",
|
||||
"MoveToAsset": "移动到资产",
|
||||
"MsgSubscribe": "消息订阅",
|
||||
"MsgTemplate": "消息模板",
|
||||
"MyAssets": "我的资产",
|
||||
"MyTickets": "我发起的",
|
||||
"NUMBER_REQUIRED": "必须包含数字",
|
||||
@@ -1022,6 +1028,7 @@
|
||||
"PleaseAgreeToTheTerms": "请同意条款",
|
||||
"PleaseEnterReason": "请输入原因",
|
||||
"PleaseSelect": "请选择",
|
||||
"PleaseSelectAssetOrNode": "请选择资产或节点",
|
||||
"PleaseSelectTheDataYouWantToCheck": "请选择需要勾选的数据",
|
||||
"PolicyName": "策略名称",
|
||||
"Port": "端口",
|
||||
@@ -1039,6 +1046,7 @@
|
||||
"PrivilegedOnly": "仅特权账号",
|
||||
"PrivilegedTemplate": "特权的",
|
||||
"Processing": "处理中",
|
||||
"ProcessingMessage": "任务进行中,请稍候 ⏳",
|
||||
"Product": "产品",
|
||||
"ProfileSetting": "个人信息设置",
|
||||
"Project": "项目名",
|
||||
@@ -1153,6 +1161,7 @@
|
||||
"ResetSSHKeySuccessMsg": "发送邮件任务已提交, 用户稍后会收到重置密钥邮件",
|
||||
"ResetSSHKeyWarningMsg": "你确定要发送重置用户的SSH Key的邮件吗?",
|
||||
"ResetSecret": "可改密",
|
||||
"ResetSuccessfully": "重置成功",
|
||||
"ResolveSelected": "解决所选",
|
||||
"Resource": "资源",
|
||||
"ResourceType": "资源类型",
|
||||
@@ -1351,9 +1360,9 @@
|
||||
"SuperAdmin": "超级管理员",
|
||||
"SuperOrgAdmin": "超级管理员+组织管理员",
|
||||
"Support": "支持",
|
||||
"SupportedTypes": "支持的类型",
|
||||
"SupportedProtocol": "支持的协议",
|
||||
"SupportedProtocolHelpText": "设置资产支持的协议,点击设置按钮可以为协议修改自定义配置,如 SFTP 目录,RDP AD 域等",
|
||||
"SupportedTypes": "支持的类型",
|
||||
"Sync": "同步",
|
||||
"SyncAction": "同步动作",
|
||||
"SyncDelete": "同步删除",
|
||||
@@ -1414,6 +1423,7 @@
|
||||
"TemplateCreate": "创建模版",
|
||||
"TemplateHelpText": "选择模版添加时,会自动创建资产下不存在的账号并推送",
|
||||
"TemplateManagement": "模版管理",
|
||||
"TemplateVariablesHelpText": "您可以选择一个模板在模板内容中使用 {{ key }} 读取内置变量,注意:只支持 {{ }} 语法,其他语法不支持。例如 {% if title %}",
|
||||
"Templates": "模板",
|
||||
"TencentCloud": "腾讯云",
|
||||
"Terminal": "组件设置",
|
||||
@@ -1527,12 +1537,12 @@
|
||||
"UserCreate": "创建用户",
|
||||
"UserData": "用户数据",
|
||||
"UserDetail": "用户详情",
|
||||
"UserGroup": "用户组",
|
||||
"UserGroupCreate": "创建用户组",
|
||||
"UserGroupDetail": "用户组详情",
|
||||
"UserGroupList": "用户组",
|
||||
"UserGroupUpdate": "更新用户组",
|
||||
"UserGroups": "用户组",
|
||||
"UserGroup": "用户组",
|
||||
"UserList": "用户列表",
|
||||
"UserLogin": "用户登录",
|
||||
"UserLoginACL": "用户登录控制",
|
||||
@@ -1619,18 +1629,20 @@
|
||||
"assetAddress": "资产地址",
|
||||
"assetId": "资产 ID",
|
||||
"assetName": "资产名称",
|
||||
"clickToAdd": "点击添加",
|
||||
"currentTime": "当前时间",
|
||||
"description": "暂无数据",
|
||||
"disallowSelfUpdateFields": "不允许自己修改当前字段",
|
||||
"forceEnableMFAHelpText": "如果强制启用,用户无法自行禁用",
|
||||
"isConsoleCanUse": "管理页面是否可用",
|
||||
"name": "用户名称",
|
||||
"overwriteProtocolsAndPortsMsg": "此操作将覆盖所有协议和端口,是否继续?",
|
||||
"pleaseSelectAssets": "请选择资产",
|
||||
"removeWarningMsg": "你确定要移除",
|
||||
"selectedAssets": "已选资产",
|
||||
"setVariable": "设置参数",
|
||||
"userId": "用户ID",
|
||||
"userName": "用户名",
|
||||
"description": "暂无数据",
|
||||
"MsgTemplate": "消息模板",
|
||||
"Example": "示例值",
|
||||
"TemplateVariablesHelpText": "您可以选择一个模板在模板内容中使用 {{ key }} 读取内置变量,注意:只支持 {{ }} 语法,其他语法不支持。例如 {% if title %}",
|
||||
"ResetSuccessfully": "重置成功"
|
||||
}
|
||||
"Risk": "风险",
|
||||
"selectFiles": "已选择选择{number}文件"
|
||||
}
|
||||
@@ -452,6 +452,10 @@
|
||||
"DangerousCommandNum": "危險命令數",
|
||||
"Dashboard": "儀錶盤",
|
||||
"DataLastUsed": "最後使用日期",
|
||||
"DataMasking": "數據脫敏",
|
||||
"DataMaskingFieldsPatternHelpTip": "支持多個欄位名稱,以逗號分隔,並支持通配符*\n例如:\n單欄位名稱:password 表示僅脫敏 password 欄位\n多個欄位名稱:password,secret 表示脫敏 password 和 secret 欄位\n通配符*:password* 表示脫敏欄位名稱中包含 password 前綴的欄位\n通配符*:.*password 表示脫敏欄位名稱中包含 password 後綴的欄位",
|
||||
"DataMaskingRuleHelpHelpMsg": "連接資料庫資產時,可以根據此規則對查詢結果進行脫敏<–SEP–>任務進行中,請稍候 ⏳",
|
||||
"DataMaskingRuleHelpHelpText": "連接資料庫資產時,可以根據此規則對查詢結果進行脫敏。",
|
||||
"Database": "資料庫",
|
||||
"DatabaseCreate": "創建資產-資料庫",
|
||||
"DatabasePort": "資料庫協議埠",
|
||||
@@ -576,6 +580,7 @@
|
||||
"EsIndex": "es 提供預設 index:jumpserver。如果開啟按日期建立索引,那麼輸入的值會作為索引前綴",
|
||||
"EsUrl": "不能包含特殊字符 `#`;eg: http://es_user:es_password@es_host:es_port",
|
||||
"Every": "每",
|
||||
"Example": "示例值",
|
||||
"Exclude": "不包含",
|
||||
"ExcludeAccount": "排除帳號",
|
||||
"ExcludeAsset": "跳過的資產",
|
||||
@@ -871,6 +876,7 @@
|
||||
"MoveAssetToNode": "移動資產到節點",
|
||||
"MoveToAsset": "移動到資產",
|
||||
"MsgSubscribe": "消息訂閱",
|
||||
"MsgTemplate": "消息模板",
|
||||
"MyAssets": "我的資產",
|
||||
"MyTickets": "我發起的",
|
||||
"NUMBER_REQUIRED": "須包含數字",
|
||||
@@ -1027,6 +1033,7 @@
|
||||
"PleaseAgreeToTheTerms": "請同意條款",
|
||||
"PleaseEnterReason": "請輸入原因",
|
||||
"PleaseSelect": "請選擇",
|
||||
"PleaseSelectAssetOrNode": "請選擇資產或節點",
|
||||
"PleaseSelectTheDataYouWantToCheck": "請選擇需要勾選的數據",
|
||||
"PolicyName": "策略名稱",
|
||||
"Port": "端口",
|
||||
@@ -1044,6 +1051,7 @@
|
||||
"PrivilegedOnly": "僅特權帳號",
|
||||
"PrivilegedTemplate": "特權的",
|
||||
"Processing": "處理中",
|
||||
"ProcessingMessage": "任務進行中,請稍候 ⏳\n\n支持多個字段名,逗號分隔,支持通配符*\n例如: \n單字段名: password 表示僅脫敏 password 字段 \n多個字段名: password,secret 表示脫敏 password 與 secret 字段 \n通配符*: password* 表示脫敏字段名中包含 password 前綴的字段 \n通配符*: .*password 表示脫敏字段名中包含 password 後綴的字段",
|
||||
"Product": "產品",
|
||||
"ProfileSetting": "個人資訊設置",
|
||||
"Project": "項目名",
|
||||
@@ -1158,6 +1166,7 @@
|
||||
"ResetSSHKeySuccessMsg": "發送郵件任務已提交,用戶稍後會收到重置密鑰郵件",
|
||||
"ResetSSHKeyWarningMsg": "你確定要傳送重置用戶的SSH Key的郵件嗎?",
|
||||
"ResetSecret": "可更改密碼",
|
||||
"ResetSuccessfully": "重置成功",
|
||||
"ResolveSelected": "解決選定",
|
||||
"Resource": "資源",
|
||||
"ResourceType": "資源類型",
|
||||
@@ -1419,6 +1428,7 @@
|
||||
"TemplateCreate": "創建模板",
|
||||
"TemplateHelpText": "選擇模板添加時,會自動創建資產下不存在的帳號並推送",
|
||||
"TemplateManagement": "模版列表",
|
||||
"TemplateVariablesHelpText": "您可以選擇一個模板,在模板內容中使用 {{ key }} 來讀取內置變數,注意:只支持 {{ }} 語法,其他語法不支持。例如 {% if title %}",
|
||||
"Templates": "模板列表",
|
||||
"TencentCloud": "騰訊雲",
|
||||
"Terminal": "組件設置",
|
||||
@@ -1624,13 +1634,17 @@
|
||||
"assetAddress": "資產地址",
|
||||
"assetId": "資產 ID",
|
||||
"assetName": "資產名稱",
|
||||
"clickToAdd": "點擊添加",
|
||||
"currentTime": "當前時間",
|
||||
"description": "目前沒有數據。",
|
||||
"disallowSelfUpdateFields": "不允許自己修改當前欄位",
|
||||
"forceEnableMFAHelpText": "如果強制啟用,用戶無法自行禁用",
|
||||
"isConsoleCanUse": "管理頁面是否可用",
|
||||
"name": "用戶名稱",
|
||||
"overwriteProtocolsAndPortsMsg": "此操作將覆蓋所有協議和端口,是否繼續?",
|
||||
"pleaseSelectAssets": "請選擇資產",
|
||||
"removeWarningMsg": "你確定要移除",
|
||||
"selectedAssets": "已選資產",
|
||||
"setVariable": "設置參數",
|
||||
"userId": "用戶ID",
|
||||
"userName": "用戶名"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"ActionPerm": "Разрешения на действия",
|
||||
"Admin": "Администратор",
|
||||
"AutoFit": "Автоподбор",
|
||||
"AutoPasteOnClick": "Нажмите на поле ввода, чтобы автоматически вставить содержимое буфера обмена (необходимо включить HTTPS и разрешения браузера).",
|
||||
"AutoPasteOnClick": "Щёлкните по полю ввода для вставки из буфера обмена (требуется HTTPS и разрешение браузера)",
|
||||
"AvailableShortcutKey": "Доступные горячие клавиши",
|
||||
"Back": "Назад",
|
||||
"Cancel": "Отмена",
|
||||
@@ -11,21 +11,21 @@
|
||||
"Close": "Закрыть",
|
||||
"ConfirmBtn": "Подтвердить",
|
||||
"Connecting": "Подключение",
|
||||
"CopyLink": "Скопировать ссылку и код подтверждения",
|
||||
"CopyLink": "Копировать ссылку и код",
|
||||
"CopyShareURLSuccess": "URL общего доступа успешно скопирован",
|
||||
"CreateLink": "Создать ссылку",
|
||||
"CreateLink": "Создать ссылку для общего доступа",
|
||||
"CreateSuccess": "Успешно создано",
|
||||
"Display": "Показать",
|
||||
"Enable": "Активировать",
|
||||
"Enable": "Включить",
|
||||
"ErrTitle": "Ошибка подключения",
|
||||
"ExpiredTime": "Срок действия",
|
||||
"FileAddUploadingList": "Добавить файл в список загрузки",
|
||||
"FileAddUploadingList": "Файл добавлен в список загрузки",
|
||||
"FileDownloadDenied": "Нет прав для загрузки файлов",
|
||||
"FileManagement": "Управление файлами",
|
||||
"FileUploadStart": "Начало загрузки файла",
|
||||
"FileUploadingWarning": "Файлы загружаются, операции запрещены",
|
||||
"FileUploadStart": "Загрузка файла началась",
|
||||
"FileUploadingWarning": "Идет загрузка файла, операции недоступны",
|
||||
"Files": "Управление файлами",
|
||||
"General": "Универсальный",
|
||||
"General": "Основной",
|
||||
"GetShareUser": "Введите имя пользователя",
|
||||
"GuaErrClientUnauthorized": "Ошибка авторизации: неверное имя пользователя или пароль, вход не выполнен",
|
||||
"GuaErrSessionConflict": "Конфликт сессий: подключение закрыто удаленным сервером из-за конфликта с другим подключением. Попробуйте позже.",
|
||||
@@ -53,7 +53,7 @@
|
||||
"GuacamoleErrUnableToConnectToVNCServer": "Не удается подключиться к VNC серверу",
|
||||
"GuacamoleErrUnsupportedCredentialTypeRequested": "Запрашиваемый тип учетных данных не поддерживается",
|
||||
"GuacamoleErrUpstreamError": "Ошибка на стороне удаленного сервера",
|
||||
"InputVerifyCode": "Пожалуйста, введите код проверки.",
|
||||
"InputVerifyCode": "Введите код подтверждения",
|
||||
"JMSErrAPIFailed": "Ошибка API Core",
|
||||
"JMSErrAuthUser": "Пользователь не аутентифицирован",
|
||||
"JMSErrBadParams": "Ошибка параметров запроса",
|
||||
@@ -68,7 +68,7 @@
|
||||
"JMSErrRemoveShareUser": "Вы были удалены из общей сессии",
|
||||
"JMSErrTerminatedByAdmin": "Сессия прервана администратором",
|
||||
"JoinShare": "Присоединиться к общей сессии",
|
||||
"KeyboardLayout": "Макет",
|
||||
"KeyboardLayout": "Раскладка клавиатуры",
|
||||
"LeaveShare": "Покинуть общую сессию",
|
||||
"LinkAddr": "Адрес ссылки",
|
||||
"Minute": "Минуты",
|
||||
@@ -76,12 +76,12 @@
|
||||
"Name": "Имя",
|
||||
"NoData": "Нет данных",
|
||||
"OK": "ОК",
|
||||
"OnlineUser": "Онлайн пользователи",
|
||||
"Other": "Другие",
|
||||
"OnlineUser": "Пользователи онлайн",
|
||||
"Other": "Прочее",
|
||||
"Password": "Пароль",
|
||||
"PauseSession": "Приостановить сессию",
|
||||
"PleaseInput": "Пожалуйста, введите",
|
||||
"PrimaryUser": "Главный пользователь",
|
||||
"PrimaryUser": "Основной пользователь",
|
||||
"ReadOnly": "Только для чтения",
|
||||
"Refresh": "Обновить",
|
||||
"RemoveShareUserConfirm": "Вы действительно хотите удалить этого пользователя из сессии?",
|
||||
@@ -89,7 +89,7 @@
|
||||
"RequireParams": "Обязательные параметры",
|
||||
"ResumeSession": "Возобновить сессию",
|
||||
"SelectAction": "Выберите действие",
|
||||
"SessionShare": "Поделиться сеансом",
|
||||
"SessionShare": "Поделиться сессией",
|
||||
"Settings": "Настройки",
|
||||
"Share": "Поделиться",
|
||||
"ShareLink": "Поделиться ссылкой",
|
||||
@@ -97,7 +97,7 @@
|
||||
"ShareUserHelpText": "Пустое поле означает, что присоединиться может каждый желающий",
|
||||
"Shared Drive": "Общий диск",
|
||||
"Shortcuts": "Горячие клавиши",
|
||||
"ShowRemoteClip": "Показать удалённый буфер обмена",
|
||||
"ShowRemoteClip": "Показать удаленный буфер обмена",
|
||||
"Skip": "Пропустить",
|
||||
"Submit": "Отправить",
|
||||
"UploadFile": "Загрузить файл",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"AvailableHotkeys": "Available hotkeys",
|
||||
"Backspace as Ctrl+H": "Backspace as Ctrl+H",
|
||||
"Batch actions": "Batch actions",
|
||||
"Batch actions(select and right click)": "Batch actions(select and right click)",
|
||||
"Batch connect": "Batch connect",
|
||||
"BatchCommands": "Batch commands",
|
||||
"Belgian French keyboard layout": "Belgian French (Azerty)",
|
||||
@@ -87,6 +88,7 @@
|
||||
"Expand all asset": "Expand all assets under the current node",
|
||||
"Expire time": "Expire time",
|
||||
"ExpiredTime": "Expire time",
|
||||
"Face Verify": "Face verify",
|
||||
"Face online required": "This login requires face verification and monitoring. Do you want to continue?",
|
||||
"Face verify": "Face verify",
|
||||
"Face verify required": "This login requires face verification. Do you want to continue?",
|
||||
@@ -106,6 +108,7 @@
|
||||
"GUI": "GUI",
|
||||
"General": "General",
|
||||
"Go to Settings": "Go to Settings",
|
||||
"Go to profile": "Go to profile",
|
||||
"Help": "Help",
|
||||
"Help or download": "Help → Download",
|
||||
"Help text": "Help text",
|
||||
@@ -285,6 +288,5 @@
|
||||
"start time": "Start time",
|
||||
"success": "Success",
|
||||
"system user": "System user",
|
||||
"user": "User",
|
||||
"Batch actions(select and right click)": "Batch actions(select and right click)"
|
||||
"user": "User"
|
||||
}
|
||||
@@ -86,6 +86,7 @@
|
||||
"Expand all asset": "Expandir todos los activos bajo el nodo",
|
||||
"Expire time": "Tiempo de caducidad",
|
||||
"ExpiredTime": "Fecha de validez",
|
||||
"Face Verify": "Verificación facial",
|
||||
"Face online required": "Este inicio de sesión requiere verificación facial y monitoreo, ¿desea continuar?",
|
||||
"Face verify": "Verificación facial",
|
||||
"Face verify required": "Este inicio de sesión requiere verificación facial, ¿desea continuar?",
|
||||
@@ -106,6 +107,7 @@
|
||||
"GUI": "Gráfico",
|
||||
"General": "Configuración básica",
|
||||
"Go to Settings": "Ir a configuración",
|
||||
"Go to profile": "Ir a Información personal",
|
||||
"Help": "Ayuda",
|
||||
"Help or download": "Menú de ayuda → Descargar",
|
||||
"Help text": "Descripción",
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
"Expand all asset": "現在のノードの下にあるすべてのアセットを展開",
|
||||
"Expire time": "期限切れ",
|
||||
"ExpiredTime": "有効期限",
|
||||
"Face Verify": "顔認証",
|
||||
"Face online required": "今回のログインには顔認証と監視が必要です。続けますか?",
|
||||
"Face verify": "顔認証",
|
||||
"Face verify required": "今回のログインには顔認証が必要です。続けますか?",
|
||||
@@ -106,6 +107,7 @@
|
||||
"GUI": "グラフィカル",
|
||||
"General": "基本構成",
|
||||
"Go to Settings": "設定に行く",
|
||||
"Go to profile": "個人情報へ行く",
|
||||
"Help": "ヘルプ",
|
||||
"Help or download": "ヘルプ → ダウンロード",
|
||||
"Help text": "します",
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
"Expand all asset": "노드 아래의 모든 자산 확장",
|
||||
"Expire time": "만료 시간",
|
||||
"ExpiredTime": "유효 기간",
|
||||
"Face Verify": "얼굴 인증",
|
||||
"Face online required": "이번 로그인에서는 얼굴 인증 및 모니터링이 필요합니다. 계속하시겠습니까?",
|
||||
"Face verify": "얼굴 인증",
|
||||
"Face verify required": "이번 로그인에는 얼굴 인식이 필요합니다. 계속하시겠습니까?",
|
||||
@@ -106,6 +107,7 @@
|
||||
"GUI": "그래픽화",
|
||||
"General": "기본 설정",
|
||||
"Go to Settings": "설정으로 가기",
|
||||
"Go to profile": "개인 정보로 가기",
|
||||
"Help": "도움",
|
||||
"Help or download": "메뉴 도움 → 다운로드",
|
||||
"Help text": "설명",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user