Compare commits

..

131 Commits
v4.0.0 ... v3

Author SHA1 Message Date
fit2bot
a497b3cf94 fix: Add '/media/' to the list of whitelisted URLs for MFA login (#16412)
Co-authored-by: wangruidong <940853815@qq.com>
2025-12-10 14:19:39 +08:00
fit2bot
8548b73063 fix: Failed to switch languages (#16326)
Co-authored-by: wangruidong <940853815@qq.com>
2025-11-24 11:13:56 +08:00
fit2bot
182320f492 fix: SAML2 authentication failure with Okta integration (#16250)
Co-authored-by: wangruidong <940853815@qq.com>
2025-11-07 15:42:09 +08:00
fit2bot
40d326d6a6 fix: Any change to the LDAP server URI should require re-authentication and explicit re-entry of (#16195)
the bind password, not reuse stored credentials

Co-authored-by: wangruidong <940853815@qq.com>
2025-10-23 18:09:46 +08:00
ibuler
b87554f9db perf: conn token get 2025-10-21 11:05:05 +08:00
Bai
3c255f9fa6 fix: AK/SK remained valid after the user expired. 2025-09-16 13:31:53 +08:00
fit2bot
a6b5437f6a fix: Open redirect security vulnerability (#15938)
Co-authored-by: wangruidong <940853815@qq.com>
2025-08-27 11:03:30 +08:00
feng
71c690ef9e perf: Account remove 2025-08-26 17:28:28 +08:00
feng
bee07db900 perf: Windows change secret check_conn_after_change 2025-08-26 14:55:01 +08:00
Bryan
115eb7c15a Reformat import statements in utils.py 2025-08-25 18:20:18 +08:00
Bai
88810263cd perf: test rebase for commit id changed 2 2025-08-25 18:16:15 +08:00
Bai
3d95bc4656 perf: test rebase for commit id changed 2025-08-25 18:05:47 +08:00
feng
69de08bb5d fix: Ticket filtering current reviewer issue 2025-08-25 14:35:15 +08:00
Bai
5c234fdd0c perf: lock xmlsec==1.3.13 2025-08-25 11:19:24 +08:00
fit2bot
be5baa5a3f perf: Update IP group validation to include address validation (#15919)
Co-authored-by: wangruidong <940853815@qq.com>
2025-08-25 11:16:52 +08:00
feng
2f1a65f120 perf: migrate 2025-08-25 11:12:25 +08:00
fit2bot
e6d02eaf4c fix: Add third party login check is block (#15916)
Co-authored-by: wangruidong <940853815@qq.com>
2025-08-25 10:51:42 +08:00
fit2bot
6d6dec2752 fix: Prevent nested resource issues in type nodes tree API (#15915)
Co-authored-by: wangruidong <940853815@qq.com>
2025-08-25 10:40:10 +08:00
feng
c6c067c44b perf: Mongodb ping 2025-08-19 19:09:52 +08:00
feng
84ec1b047a perf: FormatAssetInfo posix_format cpu_model 2025-08-15 16:51:33 +08:00
feng
e6dca2ec14 fix: automation mysql priv and postgresql finally test the connectivity 2025-08-13 15:34:32 +08:00
wangruidong
8793003d18 perf: check_asset_permission_will_expired filter is_active=True 2025-08-07 10:18:24 +08:00
ibuler
29fd6e0ae4 perf: update pymyssql 2025-07-24 19:17:01 +08:00
Ewall555
90587a83cc feat: support rbac SSO token 2025-07-16 19:21:53 +08:00
ibuler
dfa0198742 perf: safe db connection on runner success 2025-07-11 11:00:40 +08:00
jiangweidong
9e857b54ed fix: According to the CMPP2.0 protocol standard, modify the attribute alignment. 2025-06-26 18:41:59 +08:00
Ewall555
c34358509b perf: Update metismenu plugin to version 3.0.7 2025-06-24 10:24:05 +08:00
feng
a7c46109d9 fix: Fix the problem of changing the password and retrying to obtain the password 2025-06-17 18:13:29 +08:00
feng
48fa6172bd perf: Suggestion api 2025-06-16 14:17:45 +08:00
wangruidong
89aa87fd6b fix: Failed to update database assets 2025-06-10 18:05:17 +08:00
ewall555
79d230755e perf: Update jsencrypt library version 2025-06-09 18:42:44 +08:00
feng
99082f261e perf: Optimize the results returned by the suggestion api for different organizations 2025-06-06 18:08:31 +08:00
wangruidong
7e2100b435 perf: set ansible_timeout for account connectivity tasks 2025-06-04 18:41:14 +08:00
wangruidong
185d4e9563 fix: Ensure platform_id is a digit before querying Platform 2025-05-29 16:18:47 +08:00
feng
ecaa84790c perf: SSO add mfa 2025-05-20 13:11:38 +08:00
Chenyang Shen
30210dc0b9 Merge pull request #15423 from jumpserver/pr@v3@feat_add_new_alg
feat: add a new piico gm alg
2025-05-15 17:44:34 +08:00
Aaron3S
ff699f4ee2 feat: add a new piico gm alg 2025-05-15 09:43:23 +00:00
ewall555
48239b0c63 feat: Set the default expiration days for adding user and asset permissions 2025-05-13 10:57:23 +08:00
ibuler
f4f74909a8 fix: update session error when db is pg 2025-04-21 13:30:09 +08:00
feng
cab1e0bf52 perf: Perm the template push account 2025-03-27 13:59:13 +08:00
feng
bf195c1599 fix: check_api 2025-03-27 13:00:14 +08:00
feng
7f5f7e81b8 perf: change secret change_secret_result 2025-03-26 23:28:22 +08:00
feng
99affad9b9 perf: Add check_conn_after_change 2025-03-26 18:01:26 +08:00
halo
34eea024f8 perf: Use a domain account to avoid automatically creating a local account 2025-03-25 14:18:04 +08:00
Bai
1d1e4b90ed perf: update pyproject.toml 2025-03-24 15:20:27 +08:00
feng
f5d40a787e fix: check_api 2025-03-24 14:34:44 +08:00
jiangweidong
77d8083c00 fix: Slove the problem that the third-party auth cannot update user name 2025-03-06 17:03:19 +08:00
fit2bot
180303ccb4 fix: Import failed but no prompt message (#14966)
* fix: Import failed but no prompt message

* fix: Prompt message

---------

Co-authored-by: halo <wuyihuangw@gmail.com>
2025-03-04 14:44:57 +08:00
jiangweidong
9cd1619990 fix: Solve the problem that some messages cannot be sent from unauthenticated email 2025-02-28 17:45:02 +08:00
wangruidong
7d0a901522 fix: When the organization does not exist, close ticket with an error. 2025-02-13 17:51:40 +08:00
wangruidong
5e9fabff1b fix: markdown render issue 2025-02-12 15:46:54 +08:00
wangruidong
1d36934111 fix: Cannot set original org when exception occurs 2025-02-08 11:17:24 +08:00
Bai
25603e4758 fix: setting field encrypt issue 2025-02-06 17:11:43 +08:00
Bai
3ae164d7e0 fix: circle imported for perms-api 2025-01-08 10:34:46 +08:00
wangruidong
3ad64e142e fix: circular import 2025-01-07 14:08:49 +08:00
wangruidong
0ff1413780 perf: ticket info add org name 2025-01-06 14:09:49 +08:00
jiangweidong
f5b64bed4e feat: VMware 自动同步文件夹到节点-翻译 2025-01-03 18:50:30 +08:00
Bai
a559415b65 fix: koko press r dont refresh user perm-nodes 2025-01-03 17:13:49 +08:00
Bai
2e7bd076f4 fix: limit connect method xpack 2024-12-25 16:27:51 +08:00
Bai
11f6fe0bf9 fix: system org 2024-12-25 15:34:19 +08:00
wangruidong
ae94648e80 fix: Add type check for secure command execution 2024-12-24 15:58:15 +08:00
jiangweidong
94e08f3d96 perf: The command amount does not record operation logs 2024-12-20 14:59:53 +08:00
Bai
8bedef92f0 fix: api prometheus count 2024-12-20 10:57:42 +08:00
jiangweidong
e5bb28231a perf: Oauth2.0 support two methods for passing authentication credentials. 2024-12-19 14:27:29 +08:00
jiangweidong
b5aeb24ae9 perf: create account add activity log 2024-12-18 15:53:08 +08:00
feng
674ea7142f perf: The entire organization can view activity log 2024-12-11 16:21:39 +08:00
fit2bot
5ab7b99b9d perf: add encrypted configuration API (#14633)
* perf: 添加加密配置API

* perf: modify url

---------

Co-authored-by: Eric <xplzv@126.com>
2024-12-11 11:42:34 +08:00
Bai
9cd163c99d fix: when oidc enabled and use_state user login raise 400 2024-12-06 16:26:59 +08:00
wangruidong
e72073f0cc perf: Add viewAssetOnlineSessionInfo conf 2024-11-25 15:26:14 +08:00
wangruidong
690f525afc perf: Add check for SECURITY_COMMAND_EXECUTION settings in ops tasks 2024-11-11 18:15:11 +08:00
wangruidong
6686afcec1 fix: Password reset is only required for AUTH_BACKEND_MODEL 2024-10-23 11:04:15 +08:00
wangruidong
0918f5c6f6 perf: Translate 2024-10-22 17:49:22 +08:00
wangruidong
891e3d5609 perf: Storage update comment failed 2024-10-22 17:28:47 +08:00
wangruidong
9fad591545 fix: Historical sessions download failed 2024-10-22 16:34:46 +08:00
fit2bot
1ed1c3a536 perf: optimize the connection of operation logs to ES to prevent ES downtime from causing the core component to become unhealthy. (#14283)
* perf: optimize the connection of operation logs to ES to prevent ES downtime from causing the core component to become unhealthy.

* perf: sync publish message

---------

Co-authored-by: jiangweidong <1053570670@qq.com>
2024-10-12 16:18:18 +08:00
wangruidong
63824d3491 fix: adhoc execute alert msg 2024-10-12 16:15:48 +08:00
wangruidong
96eadf060c perf: site msg content optimize 2024-10-11 11:30:59 +08:00
Bai
2c9128b0e7 perf: DEFAULT_PAGE_SIZE same as MAX_LIMIT_PER_PAGE 2024-10-10 18:00:26 +08:00
Bai
7d6fd0f881 fix: Fixed the issue that the workbench user login log only displays failed logs 2024-09-29 14:45:59 +08:00
jiangweidong
4e996afd5e perf: Cloud Sync IP Policy Updated to Preferred Option i18n 2024-09-27 14:29:08 +08:00
feng
1ed745d042 perf: Login encryption key cache added 2024-09-26 15:11:56 +08:00
feng
39ebbfcf10 perf: The locked ip shows the username 2024-09-06 11:00:39 +08:00
wangruidong
d0ec4f798b perf: Optimize asset connection speed with es command storage 2024-08-30 10:53:03 +08:00
feng
1712a9a104 perf: No permission to test asset connectivity 2024-08-21 11:33:03 +08:00
Bryan
951aafcabd fix: v3 apps/authentication migrations won't be applied 2024-08-20 19:05:43 +08:00
wangruidong
e46da9d741 perf: Translate 2024-08-20 16:41:50 +08:00
ibuler
06aaf9e3d0 revert: dockerfile change 2024-08-20 14:32:50 +08:00
ibuler
5ac9fb81dc perf: change docker file 2024-08-20 13:59:49 +08:00
ibuler
fb907e250c perf: change docker file 2024-08-20 13:51:00 +08:00
ibuler
34ea40a14d perf: change docker file 2024-08-19 16:45:57 +08:00
ibuler
8c560b0317 perf: add dockerfile 2024-08-19 16:13:06 +08:00
wangruidong
df9cc4700b perf: Improve performance by optimizing ES index creation 2024-08-16 18:18:33 +08:00
wangruidong
6aa1227e60 fix: call get_verify_state_failed_response NotImplementedError 2024-08-15 20:18:34 +08:00
Bai
296c788e28 fix: job periodic task double run 2024-08-15 20:17:48 +08:00
fit2bot
a1ae29d35e fix: Use only_sudo failed (#13966)
* fix: Use only_sudo failed

* fix: Use only_sudo failed

* fix: Use only_sudo failed

---------

Co-authored-by: feng <1304903146@qq.com>
2024-08-14 16:15:07 +08:00
fit2bot
139ffd0b47 perf: Automation remove account task fail (#13406)
Co-authored-by: feng <1304903146@qq.com>
2024-08-12 18:26:18 +08:00
feng
ff6c1aef7f perf: Activity log no display 2024-08-08 16:39:13 +08:00
Eric
9b6b48a7f1 perf: support only su or sudo 2024-08-07 14:23:26 +08:00
wangruidong
2b133a8085 perf: object storage builtin comment i18n 2024-08-06 10:45:32 +08:00
Eric
81b5f1ce93 perf: Check whether the applet is available. 2024-08-05 18:18:05 +08:00
feng
c646084c51 perf: Ticket set serial number add lock 2024-08-05 17:53:08 +08:00
wangruidong
5e69c03cb7 perf: Remove applets, no longer display remote application connection methods 2024-08-01 15:59:35 +08:00
wangruidong
e2df85bddd fix: stop job failed 2024-07-30 18:49:50 +08:00
feng
d710697fa9 perf: Saml2 callback url miss port 2024-07-26 18:14:49 +08:00
halo
a955fcd682 perf: Email service authentication username is optional 2024-07-26 14:04:47 +08:00
Bai
1816d52d21 perf: Modifying the label matching logic of an AppletHost (random) 2024-07-25 19:02:41 +08:00
feng
d7c26cab7d fix: Console dashboard user login count 2024-07-24 16:10:05 +08:00
wangruidong
dc894fdc2d perf: Modify error message for desktop client login 2024-07-23 14:03:39 +08:00
feng
742ef89bef perf: You can modify sudo permissions multiple times 2024-07-22 17:27:04 +08:00
feng
3d4fc56592 perf: Gpt3 to gpt-4o-mini 2024-07-19 11:56:41 +08:00
feng
45291aba0c perf: The gateway password contains ! Password parsing failed 2024-07-19 10:41:41 +08:00
feng
495ee99e29 perf: Create authorization to add template account Push account parameters 2024-07-18 19:15:59 +08:00
jiangweidong
223eb8ad38 fix: async sms task params can json 2024-07-12 18:38:02 +08:00
gerry-fit
370e959400 perf: Enterprise Edition Hide Footer Copyright Content 2024-07-11 11:47:22 +08:00
fit2bot
b82f007787 perf: Migrate (#13689)
Co-authored-by: feng <1304903146@qq.com>
2024-07-11 10:35:01 +08:00
Eric
1faeb54673 perf: update locale i18n files 2024-07-10 18:42:24 +08:00
wangruidong
04e102cb87 fix: 定时清理任务不生效问题 2024-07-10 15:39:26 +08:00
fit2bot
81027cd561 perf: save_passwd_change filter user source local and passwords not emtpy (#13680)
Co-authored-by: feng <1304903146@qq.com>
2024-07-10 14:25:43 +08:00
fit2bot
cf727d22c0 fix: Account tempale cannot push params (#13671)
Co-authored-by: feng <1304903146@qq.com>
2024-07-09 19:12:24 +08:00
feng
bb6d077645 perf: save_passwd_change filter user source local and passwords not emtpy 2024-07-09 19:07:49 +08:00
halo
a78ccc9667 perf: 优化创建子节点时锁置后 2024-07-09 15:15:36 +08:00
Eric
d70351e6b3 perf: add i18n .mo file 2024-07-09 15:11:21 +08:00
Eric
4e76207adb perf: add i18n 2024-07-09 14:44:59 +08:00
ibuler
7a12c3737f perf: xpack can disable force 2024-07-09 11:10:03 +08:00
吴小白
8450c49e25 Merge pull request #13639 from jumpserver/pr@v3@update_poetry_lock
perf: update poetry lock
2024-07-09 10:59:00 +08:00
吴小白
ab6d8df2f0 perf: update poetry lock 2024-07-09 10:48:04 +08:00
Bai
550115c39f perf: update poetry lock 2024-07-08 19:42:43 +08:00
Eric
9c23512d91 perf: add connection options for mongodb 2024-07-08 18:21:57 +08:00
ibuler
30054b286a perf: change ansible version 2024-07-08 14:29:25 +08:00
Eric
22d7385891 perf: clean mp4 replay file 2024-06-25 19:07:17 +08:00
Bai
1701bedb41 perf: Update poetry lock file 2024-06-24 14:42:05 +08:00
894 changed files with 38865 additions and 32456 deletions

View File

@@ -3,7 +3,6 @@ on:
push:
paths:
- 'Dockerfile'
- 'Dockerfile*'
- 'Dockerfile-*'
- 'pyproject.toml'
- 'poetry.lock'
@@ -11,53 +10,46 @@ on:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
component: [core]
version: [v4]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare Build
- name: Check Dockerfile
run: |
sed -i 's@^FROM registry.fit2cloud.com/jumpserver@FROM ghcr.io/jumpserver@g' Dockerfile-ee
test -f Dockerfile-ce || cp -f Dockerfile Dockerfile-ce
- name: Build CE Image
uses: docker/build-push-action@v5
with:
context: .
push: true
file: Dockerfile
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}-ce
push: false
file: Dockerfile-ce
tags: jumpserver/core-ce:test
platforms: linux/amd64
build-args: |
VERSION=${{ matrix.version }}
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
PIP_JMS_MIRROR=https://pypi.org/simple
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Prepare EE Image
run: |
sed -i 's@^FROM registry.fit2cloud.com@# FROM registry.fit2cloud.com@g' Dockerfile-ee
sed -i 's@^COPY --from=build-xpack@# COPY --from=build-xpack@g' Dockerfile-ee
- name: Build EE Image
uses: docker/build-push-action@v5
with:
context: .
push: false
file: Dockerfile-ee
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}
tags: jumpserver/core-ee:test
platforms: linux/amd64
build-args: |
VERSION=${{ matrix.version }}
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
PIP_JMS_MIRROR=https://pypi.org/simple
cache-from: type=gha
cache-to: type=gha,mode=max

2
.gitignore vendored
View File

@@ -44,5 +44,3 @@ data/*
test.py
.history/
.test/
*.mo

View File

@@ -1,10 +1,5 @@
# Contributing
As a contributor, you should agree that:
- The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
- Your contributed code may be used for commercial purposes, including but not limited to its cloud business operations.
## Create pull request
PR are always welcome, even if they only contain small fixes like typos or a few lines of code. If there will be a significant effort, please document it as an issue and get a discussion going before starting to work on it.

View File

@@ -1,49 +1,13 @@
FROM debian:bullseye-slim as stage-1
FROM python:3.11-slim-bullseye AS stage-1
ARG TARGETARCH
ARG DEPENDENCIES=" \
ca-certificates \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
--mount=type=cache,target=/var/lib/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 \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& echo "no" | dpkg-reconfigure dash
WORKDIR /opt
ARG CHECK_VERSION=v1.0.2
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 \
&& mv check /usr/local/bin/ \
&& chown root:root /usr/local/bin/check \
&& chmod 755 /usr/local/bin/check \
&& rm -f check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz
ARG RECEPTOR_VERSION=v1.4.5
RUN set -ex \
&& wget -O /opt/receptor.tar.gz https://github.com/ansible/receptor/releases/download/${RECEPTOR_VERSION}/receptor_${RECEPTOR_VERSION/v/}_linux_${TARGETARCH}.tar.gz \
&& tar -xf /opt/receptor.tar.gz -C /usr/local/bin/ \
&& chown root:root /usr/local/bin/receptor \
&& chmod 755 /usr/local/bin/receptor \
&& rm -f /opt/receptor.tar.gz
ARG VERSION
ENV VERSION=$VERSION
WORKDIR /opt/jumpserver
ADD . .
RUN echo > /opt/jumpserver/config.yml \
&& \
if [ -n "${VERSION}" ]; then \
sed -i "s@VERSION = .*@VERSION = '${VERSION}'@g" apps/jumpserver/const.py; \
fi
&& cd utils && bash -ixeu build.sh
FROM python:3.11-slim-bullseye as stage-2
ARG TARGETARCH
@@ -54,69 +18,90 @@ ARG BUILD_DEPENDENCIES=" \
pkg-config"
ARG DEPENDENCIES=" \
default-libmysqlclient-dev \
freetds-dev \
gettext \
libffi-dev \
libjpeg-dev \
libkrb5-dev \
libldap2-dev \
libsasl2-dev"
libpq-dev \
libsasl2-dev \
libssl-dev \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \
freerdp2-dev \
libaio-dev"
ARG TOOLS=" \
ca-certificates \
curl \
default-libmysqlclient-dev \
default-mysql-client \
git \
git-lfs \
unzip \
xz-utils \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core \
set -ex \
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& 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 \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& echo "no" | dpkg-reconfigure dash
WORKDIR /opt/jumpserver
ARG PIP_MIRROR=https://pypi.tuna.tsinghua.edu.cn/simple
RUN --mount=type=cache,target=/root/.cache,sharing=locked,id=core \
--mount=type=bind,source=poetry.lock,target=poetry.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
RUN --mount=type=cache,target=/root/.cache \
--mount=type=bind,source=poetry.lock,target=/opt/jumpserver/poetry.lock \
--mount=type=bind,source=pyproject.toml,target=/opt/jumpserver/pyproject.toml \
set -ex \
&& python3 -m venv /opt/py3 \
&& pip install poetry -i ${PIP_MIRROR} \
&& poetry config virtualenvs.create false \
&& . /opt/py3/bin/activate \
&& poetry install --only main
COPY --from=stage-1 /opt/jumpserver /opt/jumpserver
RUN set -ex \
&& export SECRET_KEY=$(head -c100 < /dev/urandom | base64 | tr -dc A-Za-z0-9 | head -c 48) \
&& . /opt/py3/bin/activate \
&& cd apps \
&& python manage.py compilemessages
&& poetry install
FROM python:3.11-slim-bullseye
ARG TARGETARCH
ENV LANG=en_US.UTF-8 \
ENV LANG=zh_CN.UTF-8 \
PATH=/opt/py3/bin:$PATH
ARG DEPENDENCIES=" \
libldap2-dev \
libx11-dev"
libjpeg-dev \
libpq-dev \
libx11-dev \
freerdp2-dev \
libxmlsec1-openssl"
ARG TOOLS=" \
ca-certificates \
curl \
default-libmysqlclient-dev \
default-mysql-client \
iputils-ping \
locales \
netcat-openbsd \
nmap \
openssh-client \
patch \
sshpass \
bubblewrap"
telnet \
vim \
bubblewrap \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core \
set -ex \
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& 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 \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
@@ -124,12 +109,21 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \
&& echo "no" | dpkg-reconfigure dash \
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
&& sed -i "s@# export @export @g" ~/.bashrc \
&& sed -i "s@# alias @alias @g" ~/.bashrc
COPY --from=stage-2 /opt /opt
COPY --from=stage-1 /usr/local/bin /usr/local/bin
COPY --from=stage-1 /opt/jumpserver/apps/libs/ansible/ansible.cfg /etc/ansible/
ARG RECEPTOR_VERSION=v1.4.5
RUN set -ex \
&& wget -O /opt/receptor.tar.gz https://github.com/ansible/receptor/releases/download/${RECEPTOR_VERSION}/receptor_${RECEPTOR_VERSION/v/}_linux_${TARGETARCH}.tar.gz \
&& tar -xf /opt/receptor.tar.gz -C /usr/local/bin/ \
&& chown root:root /usr/local/bin/receptor \
&& chmod 755 /usr/local/bin/receptor \
&& rm -f /opt/receptor.tar.gz
COPY --from=stage-2 /opt/py3 /opt/py3
COPY --from=stage-1 /opt/jumpserver/release/jumpserver /opt/jumpserver
COPY --from=stage-1 /opt/jumpserver/release/jumpserver/apps/libs/ansible/ansible.cfg /etc/ansible/
WORKDIR /opt/jumpserver
@@ -138,10 +132,6 @@ ENV VERSION=$VERSION
VOLUME /opt/jumpserver/data
ENTRYPOINT ["./entrypoint.sh"]
EXPOSE 8080
STOPSIGNAL SIGQUIT
CMD ["start", "all"]
ENTRYPOINT ["./entrypoint.sh"]

137
Dockerfile-ce Normal file
View File

@@ -0,0 +1,137 @@
FROM python:3.11-slim-bullseye AS stage-1
ARG TARGETARCH
ARG VERSION
ENV VERSION=$VERSION
WORKDIR /opt/jumpserver
ADD . .
RUN echo > /opt/jumpserver/config.yml \
&& cd utils && bash -ixeu build.sh
FROM python:3.11-slim-bullseye as stage-2
ARG TARGETARCH
ARG BUILD_DEPENDENCIES=" \
g++ \
make \
pkg-config"
ARG DEPENDENCIES=" \
freetds-dev \
libffi-dev \
libjpeg-dev \
libkrb5-dev \
libldap2-dev \
libpq-dev \
libsasl2-dev \
libssl-dev \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \
freerdp2-dev \
libaio-dev"
ARG TOOLS=" \
ca-certificates \
curl \
default-libmysqlclient-dev \
default-mysql-client \
git \
git-lfs \
unzip \
xz-utils \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& echo "no" | dpkg-reconfigure dash
WORKDIR /opt/jumpserver
ARG PIP_MIRROR=https://pypi.tuna.tsinghua.edu.cn/simple
RUN --mount=type=cache,target=/root/.cache \
--mount=type=bind,source=poetry.lock,target=/opt/jumpserver/poetry.lock \
--mount=type=bind,source=pyproject.toml,target=/opt/jumpserver/pyproject.toml \
set -ex \
&& python3 -m venv /opt/py3 \
&& pip install poetry -i ${PIP_MIRROR} \
&& poetry config virtualenvs.create false \
&& . /opt/py3/bin/activate \
&& poetry install
FROM python:3.11-slim-bullseye
ARG TARGETARCH
ENV LANG=zh_CN.UTF-8 \
PATH=/opt/py3/bin:$PATH
ARG DEPENDENCIES=" \
libjpeg-dev \
libpq-dev \
libx11-dev \
freerdp2-dev \
libxmlsec1-openssl"
ARG TOOLS=" \
ca-certificates \
curl \
default-libmysqlclient-dev \
default-mysql-client \
iputils-ping \
locales \
netcat-openbsd \
nmap \
openssh-client \
patch \
sshpass \
telnet \
vim \
bubblewrap \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \
&& echo "no" | dpkg-reconfigure dash \
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
&& sed -i "s@# export @export @g" ~/.bashrc \
&& sed -i "s@# alias @alias @g" ~/.bashrc
ARG RECEPTOR_VERSION=v1.4.5
RUN set -ex \
&& wget -O /opt/receptor.tar.gz https://github.com/ansible/receptor/releases/download/${RECEPTOR_VERSION}/receptor_${RECEPTOR_VERSION/v/}_linux_${TARGETARCH}.tar.gz \
&& tar -xf /opt/receptor.tar.gz -C /usr/local/bin/ \
&& chown root:root /usr/local/bin/receptor \
&& chmod 755 /usr/local/bin/receptor \
&& rm -f /opt/receptor.tar.gz
COPY --from=stage-2 /opt/py3 /opt/py3
COPY --from=stage-1 /opt/jumpserver/release/jumpserver /opt/jumpserver
COPY --from=stage-1 /opt/jumpserver/release/jumpserver/apps/libs/ansible/ansible.cfg /etc/ansible/
WORKDIR /opt/jumpserver
ARG VERSION
ENV VERSION=$VERSION
VOLUME /opt/jumpserver/data
EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]

View File

@@ -1,52 +1,5 @@
ARG VERSION
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} AS build-xpack
FROM registry.fit2cloud.com/jumpserver/core-ce:${VERSION}
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} as build-xpack
FROM python:3.11-slim-bullseye as build-core
ARG BUILD_DEPENDENCIES=" \
g++"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
--mount=type=cache,target=/var/lib/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 \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
&& echo "no" | dpkg-reconfigure dash
WORKDIR /opt/jumpserver
ARG PIP_MIRROR=https://pypi.tuna.tsinghua.edu.cn/simple
RUN --mount=type=cache,target=/root/.cache,sharing=locked,id=core \
--mount=type=bind,source=poetry.lock,target=/opt/jumpserver/poetry.lock \
--mount=type=bind,source=pyproject.toml,target=/opt/jumpserver/pyproject.toml \
set -ex \
&& python3 -m venv /opt/py3 \
&& pip install poetry -i ${PIP_MIRROR} \
&& poetry config virtualenvs.create false \
&& . /opt/py3/bin/activate \
&& poetry install --only xpack
FROM registry.fit2cloud.com/jumpserver/core:${VERSION}-ce
ARG TARGETARCH
ARG TOOLS=" \
curl \
iputils-ping \
netcat-openbsd \
nmap \
telnet \
vim \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core \
set -ex \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${TOOLS}
COPY --from=build-core /opt/py3 /opt/py3
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack

1
GITSHA
View File

@@ -1 +0,0 @@
02fc045370b39a2d14561ea978d465e162a53119

207
README.md
View File

@@ -1,112 +1,125 @@
<div align="center">
<a name="readme-top"></a>
<a href="https://jumpserver.org/index-en.html"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a>
## An open-source PAM tool (Bastion Host)
<p align="center">
<a href="https://jumpserver.org"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a>
</p>
<h3 align="center">广受欢迎的开源堡垒机</h3>
[![][license-shield]][license-link]
[![][discord-shield]][discord-link]
[![][docker-shield]][docker-link]
[![][github-release-shield]][github-release-link]
[![][github-stars-shield]][github-stars-link]
**English** · [简体中文](./README.zh-CN.md) · [Documents][docs-link] · [Report Bug][github-issues-link] · [Request Feature][github-issues-link]
</div>
<br/>
## What is JumpServer?
JumpServer is an open-source Privileged Access Management (PAM) tool that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and Remote App endpoints through a web browser.
![JumpServer Overview](https://github.com/jumpserver/jumpserver/assets/41712985/eb9e6f39-3911-4d4a-bca9-aa50cc3b761d)
## Screenshots
<table style="border-collapse: collapse; border: 1px solid black;">
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/99fabe5b-0475-4a53-9116-4c370a1426c4" alt="JumpServer Console" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/a424d731-1c70-4108-a7d8-5bbf387dda9a" alt="JumpServer Audits" /></td>
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/393d2c27-a2d0-4dea-882d-00ed509e00c9" alt="JumpServer Workbench" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/3a2611cd-8902-49b8-b82b-2a6dac851f3e" alt="JumpServer Settings" /></td>
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/1e236093-31f7-4563-8eb1-e36d865f1568" alt="JumpServer SSH" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/69373a82-f7ab-41e8-b763-bbad2ba52167" alt="JumpServer RDP" /></td>
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/5bed98c6-cbe8-4073-9597-d53c69dc3957" alt="JumpServer K8s" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/b80ad654-548f-42bc-ba3d-c1cfdf1b46d6" alt="JumpServer DB" /></td>
</tr>
</table>
## Quick Start
Prepare a clean Linux Server ( 64bit, >= 4c8g )
```
curl -sSL https://github.com/jumpserver/jumpserver/releases/latest/download/quick_start.sh | bash
```
Access JumpServer in your browser at `http://your-ip/`, `admin`/`admin`
## Components
JumpServer consists of multiple key components, which collectively form the functional framework of JumpServer, providing users with comprehensive capabilities for operations management and security control.
| Project | Status | Description |
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| [Lina](https://github.com/jumpserver/lina) | <a href="https://github.com/jumpserver/lina/releases"><img alt="Lina release" src="https://img.shields.io/github/release/jumpserver/lina.svg" /></a> | JumpServer Web UI |
| [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 |
| [Razor](https://github.com/jumpserver/razor) | <img alt="Chen" src="https://img.shields.io/badge/release-private-red" /> | JumpServer RDP Proxy Connector |
| [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 Remote Application Connector (Linux) |
| [Magnus](https://github.com/jumpserver/magnus) | <img alt="Magnus" src="https://img.shields.io/badge/release-private-red" /> | JumpServer Database Proxy Connector |
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/github/license/jumpserver/jumpserver" alt="License: GPLv3"></a>
<a href="https://hub.docker.com/u/jumpserver"><img src="https://img.shields.io/docker/pulls/jumpserver/jms_all.svg" alt="Docker pulls"></a>
<a href="https://github.com/jumpserver/jumpserver/releases/latest"><img src="https://img.shields.io/github/v/release/jumpserver/jumpserver" alt="Latest release"></a>
<a href="https://github.com/jumpserver/jumpserver"><img src="https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square" alt="Stars"></a>
</p>
<p align="center">
9 年时间,倾情投入,用心做好一款开源堡垒机。
</p>
## Contributing
------------------------------
JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。
Welcome to submit PR to contribute. Please refer to [CONTRIBUTING.md][contributing-link] for guidelines.
JumpServer 堡垒机帮助企业以更安全的方式管控和登录各种类型的资产,包括:
## Security
- **SSH**: Linux / Unix / 网络设备 等;
- **Windows**: Web 方式连接 / 原生 RDP 连接;
- **数据库**: MySQL / MariaDB / PostgreSQL / Oracle / SQLServer / ClickHouse 等;
- **NoSQL**: Redis / MongoDB 等;
- **GPT**: ChatGPT 等;
- **云服务**: Kubernetes / VMware vSphere 等;
- **Web 站点**: 各类系统的 Web 管理后台;
- **应用**: 通过 Remote App 连接各类应用。
JumpServer is a mission critical product. Please refer to the Basic Security Recommendations for installation and deployment. If you encounter any security-related issues, please contact us directly:
## 产品特色
- Email: support@fit2cloud.com
- **开源**: 零门槛,线上快速获取和安装;
- **无插件**: 仅需浏览器,极致的 Web Terminal 使用体验;
- **分布式**: 支持分布式部署和横向扩展,轻松支持大规模并发访问;
- **多云支持**: 一套系统,同时管理不同云上面的资产;
- **多租户**: 一套系统,多个子公司或部门同时使用;
- **云端存储**: 审计录像云端存储,永不丢失;
## License
## UI 展示
![UI展示](https://docs.jumpserver.org/zh/v3/img/dashboard.png)
## 在线体验
- 环境地址:<https://demo.jumpserver.org/>
| :warning: 注意 |
|:-----------------------------|
| 该环境仅作体验目的使用,我们会定时清理、重置数据! |
| 请勿修改体验环境用户的密码! |
| 请勿在环境中添加业务生产环境地址、用户名密码等敏感信息! |
## 快速开始
- [快速入门](https://docs.jumpserver.org/zh/v3/quick_start/)
- [产品文档](https://docs.jumpserver.org)
- [在线学习](https://edu.fit2cloud.com/page/2635362)
- [知识库](https://kb.fit2cloud.com/categories/jumpserver)
## 案例研究
- [腾讯音乐娱乐集团基于JumpServer的安全运维审计解决方案](https://blog.fit2cloud.com/?p=a04cdf0d-6704-4d18-9b40-9180baecd0e2)
- [腾讯海外游戏基于JumpServer构建游戏安全运营能力](https://blog.fit2cloud.com/?p=3704)
- [万华化学通过JumpServer管理全球化分布式IT资产并且实现与云管平台的联动](https://blog.fit2cloud.com/?p=3504)
- [雪花啤酒JumpServer堡垒机使用体会](https://blog.fit2cloud.com/?p=3412)
- [顺丰科技JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147)
- [沐瞳游戏通过JumpServer管控多项目分布式资产](https://blog.fit2cloud.com/?p=3213)
- [携程JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [大智慧JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [小红书JumpServer 堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [中手游JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
- [江苏农信JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)
## 社区交流
如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。
您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 当中进行交流沟通。
### 参与贡献
欢迎提交 PR 参与贡献。 参考 [CONTRIBUTING.md](https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md)
## 组件项目
| 项目 | 状态 | 描述 |
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| [Lina](https://github.com/jumpserver/lina) | <a href="https://github.com/jumpserver/lina/releases"><img alt="Lina release" src="https://img.shields.io/github/release/jumpserver/lina.svg" /></a> | JumpServer Web UI 项目 |
| [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 字符协议 Connector 项目 |
| [Lion](https://github.com/jumpserver/lion-release) | <a href="https://github.com/jumpserver/lion-release/releases"><img alt="Lion release" src="https://img.shields.io/github/release/jumpserver/lion-release.svg" /></a> | JumpServer 图形协议 Connector 项目,依赖 [Apache Guacamole](https://guacamole.apache.org/) |
| [Razor](https://github.com/jumpserver/razor) | <img alt="Chen" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer RDP 代理 Connector 项目 |
| [Tinker](https://github.com/jumpserver/tinker) | <img alt="Tinker" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer 远程应用 Connector 项目 (Windows) |
| [Panda](https://github.com/jumpserver/Panda) | <img alt="Panda" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer 远程应用 Connector 项目 (Linux) |
| [Magnus](https://github.com/jumpserver/magnus-release) | <a href="https://github.com/jumpserver/magnus-release/releases"><img alt="Magnus release" src="https://img.shields.io/github/release/jumpserver/magnus-release.svg" /> | JumpServer 数据库代理 Connector 项目 |
| [Chen](https://github.com/jumpserver/chen-release) | <a href="https://github.com/jumpserver/chen-release/releases"><img alt="Chen release" src="https://img.shields.io/github/release/jumpserver/chen-release.svg" /> | JumpServer Web DB 项目,替代原来的 OmniDB |
| [Kael](https://github.com/jumpserver/kael) | <a href="https://github.com/jumpserver/kael/releases"><img alt="Kael release" src="https://img.shields.io/github/release/jumpserver/kael.svg" /> | JumpServer 连接 GPT 资产的组件项目 |
| [Wisp](https://github.com/jumpserver/wisp) | <a href="https://github.com/jumpserver/wisp/releases"><img alt="Magnus release" src="https://img.shields.io/github/release/jumpserver/wisp.svg" /> | JumpServer 各系统终端组件和 Core API 通信的组件项目 |
| [Clients](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 客户端 项目 |
| [Installer](https://github.com/jumpserver/installer) | <a href="https://github.com/jumpserver/installer/releases"><img alt="Installer release" src="https://img.shields.io/github/release/jumpserver/installer.svg" /> | JumpServer 安装包 项目 |
## 安全说明
JumpServer是一款安全产品请参考 [基本安全建议](https://docs.jumpserver.org/zh/master/install/install_security/)
进行安装部署。如果您发现安全相关问题,请直接联系我们:
- 邮箱support@fit2cloud.com
- 电话400-052-0755
## License & Copyright
Copyright (c) 2014-2024 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/gpl-3.0.html
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an " AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
<!-- JumpServer official link -->
[docs-link]: https://en-docs.jumpserver.org/
[discord-link]: https://discord.com/invite/jcM5tKWJ
[contributing-link]: https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md
<!-- JumpServer Other link-->
[license-link]: https://www.gnu.org/licenses/gpl-3.0.html
[docker-link]: https://hub.docker.com/u/jumpserver
[github-release-link]: https://github.com/jumpserver/jumpserver/releases/latest
[github-stars-link]: https://github.com/jumpserver/jumpserver
[github-issues-link]: https://github.com/jumpserver/jumpserver/issues
<!-- Shield link-->
[github-release-shield]: https://img.shields.io/github/v/release/jumpserver/jumpserver
[github-stars-shield]: https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square
[docker-shield]: https://img.shields.io/docker/pulls/jumpserver/jms_all.svg
[license-shield]: https://img.shields.io/github/license/jumpserver/jumpserver
[discord-shield]: https://img.shields.io/discord/1194233267294052363?style=flat&logo=discord&logoColor=%23f5f5f5&labelColor=%235462eb&color=%235462eb
<!-- Image link -->
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "
AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.

View File

@@ -1,125 +0,0 @@
<p align="center">
<a href="https://jumpserver.org"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a>
</p>
<h3 align="center">广受欢迎的开源堡垒机</h3>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/github/license/jumpserver/jumpserver" alt="License: GPLv3"></a>
<a href="https://hub.docker.com/u/jumpserver"><img src="https://img.shields.io/docker/pulls/jumpserver/jms_all.svg" alt="Docker pulls"></a>
<a href="https://github.com/jumpserver/jumpserver/releases/latest"><img src="https://img.shields.io/github/v/release/jumpserver/jumpserver" alt="Latest release"></a>
<a href="https://github.com/jumpserver/jumpserver"><img src="https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square" alt="Stars"></a>
</p>
<p align="center">
9 年时间,倾情投入,用心做好一款开源堡垒机。
</p>
------------------------------
JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。
JumpServer 堡垒机帮助企业以更安全的方式管控和登录各种类型的资产,包括:
- **SSH**: Linux / Unix / 网络设备 等;
- **Windows**: Web 方式连接 / 原生 RDP 连接;
- **数据库**: MySQL / MariaDB / PostgreSQL / Oracle / SQLServer / ClickHouse 等;
- **NoSQL**: Redis / MongoDB 等;
- **GPT**: ChatGPT 等;
- **云服务**: Kubernetes / VMware vSphere 等;
- **Web 站点**: 各类系统的 Web 管理后台;
- **应用**: 通过 Remote App 连接各类应用。
## 产品特色
- **开源**: 零门槛,线上快速获取和安装;
- **无插件**: 仅需浏览器,极致的 Web Terminal 使用体验;
- **分布式**: 支持分布式部署和横向扩展,轻松支持大规模并发访问;
- **多云支持**: 一套系统,同时管理不同云上面的资产;
- **多租户**: 一套系统,多个子公司或部门同时使用;
- **云端存储**: 审计录像云端存储,永不丢失;
## UI 展示
![UI展示](https://docs.jumpserver.org/zh/v3/img/dashboard.png)
## 在线体验
- 环境地址:<https://demo.jumpserver.org/>
| :warning: 注意 |
|:-----------------------------|
| 该环境仅作体验目的使用,我们会定时清理、重置数据! |
| 请勿修改体验环境用户的密码! |
| 请勿在环境中添加业务生产环境地址、用户名密码等敏感信息! |
## 快速开始
- [快速入门](https://docs.jumpserver.org/zh/v3/quick_start/)
- [产品文档](https://docs.jumpserver.org)
- [在线学习](https://edu.fit2cloud.com/page/2635362)
- [知识库](https://kb.fit2cloud.com/categories/jumpserver)
## 案例研究
- [腾讯音乐娱乐集团基于JumpServer的安全运维审计解决方案](https://blog.fit2cloud.com/?p=a04cdf0d-6704-4d18-9b40-9180baecd0e2)
- [腾讯海外游戏基于JumpServer构建游戏安全运营能力](https://blog.fit2cloud.com/?p=3704)
- [万华化学通过JumpServer管理全球化分布式IT资产并且实现与云管平台的联动](https://blog.fit2cloud.com/?p=3504)
- [雪花啤酒JumpServer堡垒机使用体会](https://blog.fit2cloud.com/?p=3412)
- [顺丰科技JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147)
- [沐瞳游戏通过JumpServer管控多项目分布式资产](https://blog.fit2cloud.com/?p=3213)
- [携程JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [大智慧JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [小红书JumpServer 堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [中手游JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
- [江苏农信JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)
## 社区交流
如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。
您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 当中进行交流沟通。
### 参与贡献
欢迎提交 PR 参与贡献。 参考 [CONTRIBUTING.md](https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md)
## 组件项目
| 项目 | 状态 | 描述 |
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| [Lina](https://github.com/jumpserver/lina) | <a href="https://github.com/jumpserver/lina/releases"><img alt="Lina release" src="https://img.shields.io/github/release/jumpserver/lina.svg" /></a> | JumpServer Web UI 项目 |
| [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 字符协议 Connector 项目 |
| [Lion](https://github.com/jumpserver/lion-release) | <a href="https://github.com/jumpserver/lion-release/releases"><img alt="Lion release" src="https://img.shields.io/github/release/jumpserver/lion-release.svg" /></a> | JumpServer 图形协议 Connector 项目,依赖 [Apache Guacamole](https://guacamole.apache.org/) |
| [Razor](https://github.com/jumpserver/razor) | <img alt="Chen" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer RDP 代理 Connector 项目 |
| [Tinker](https://github.com/jumpserver/tinker) | <img alt="Tinker" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer 远程应用 Connector 项目 (Windows) |
| [Panda](https://github.com/jumpserver/Panda) | <img alt="Panda" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer 远程应用 Connector 项目 (Linux) |
| [Magnus](https://github.com/jumpserver/magnus-release) | <a href="https://github.com/jumpserver/magnus-release/releases"><img alt="Magnus release" src="https://img.shields.io/github/release/jumpserver/magnus-release.svg" /> | JumpServer 数据库代理 Connector 项目 |
| [Chen](https://github.com/jumpserver/chen-release) | <a href="https://github.com/jumpserver/chen-release/releases"><img alt="Chen release" src="https://img.shields.io/github/release/jumpserver/chen-release.svg" /> | JumpServer Web DB 项目,替代原来的 OmniDB |
| [Kael](https://github.com/jumpserver/kael) | <a href="https://github.com/jumpserver/kael/releases"><img alt="Kael release" src="https://img.shields.io/github/release/jumpserver/kael.svg" /> | JumpServer 连接 GPT 资产的组件项目 |
| [Wisp](https://github.com/jumpserver/wisp) | <a href="https://github.com/jumpserver/wisp/releases"><img alt="Magnus release" src="https://img.shields.io/github/release/jumpserver/wisp.svg" /> | JumpServer 各系统终端组件和 Core API 通信的组件项目 |
| [Clients](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 客户端 项目 |
| [Installer](https://github.com/jumpserver/installer) | <a href="https://github.com/jumpserver/installer/releases"><img alt="Installer release" src="https://img.shields.io/github/release/jumpserver/installer.svg" /> | JumpServer 安装包 项目 |
## 安全说明
JumpServer是一款安全产品请参考 [基本安全建议](https://docs.jumpserver.org/zh/master/install/install_security/)
进行安装部署。如果您发现安全相关问题,请直接联系我们:
- 邮箱support@fit2cloud.com
- 电话400-052-0755
## License & Copyright
Copyright (c) 2014-2024 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/gpl-3.0.html
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "
AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.

94
README_EN.md Normal file
View File

@@ -0,0 +1,94 @@
<p align="center"><a href="https://jumpserver.org"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a></p>
<h3 align="center">Open Source Bastion Host</h3>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/github/license/jumpserver/jumpserver" alt="License: GPLv3"></a>
<a href="https://shields.io/github/downloads/jumpserver/jumpserver/total"><img src="https://shields.io/github/downloads/jumpserver/jumpserver/total" alt=" release"></a>
<a href="https://hub.docker.com/u/jumpserver"><img src="https://img.shields.io/docker/pulls/jumpserver/jms_all.svg" alt="Codacy"></a>
<a href="https://github.com/jumpserver/jumpserver"><img src="https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square" alt="Stars"></a>
</p>
JumpServer is the world's first open-source Bastion Host and is licensed under the GPLv3. It is a 4A-compliant professional operation and maintenance security audit system.
JumpServer uses Python / Django for development, follows Web 2.0 specifications, and is equipped with an industry-leading Web Terminal solution that provides a beautiful user interface and great user experience
JumpServer adopts a distributed architecture to support multi-branch deployment across multiple cross-regional areas. The central node provides APIs, and login nodes are deployed in each branch. It can be scaled horizontally without concurrency restrictions.
Change the world by taking every little step
----
### Advantages
- Open Source: huge transparency and free to access with quick installation process.
- Distributed: support large-scale concurrent access with ease.
- No Plugin required: all you need is a browser, the ultimate Web Terminal experience.
- Multi-Cloud supported: a unified system to manage assets on different clouds at the same time
- Cloud storage: audit records are stored in the cloud. Data lost no more!
- Multi-Tenant system: multiple subsidiary companies or departments access the same system simultaneously.
- Many applications supported: link to databases, windows remote applications, and Kubernetes cluster, etc.
### JumpServer Component Projects
- [Lina](https://github.com/jumpserver/lina) JumpServer Web UI
- [Luna](https://github.com/jumpserver/luna) JumpServer Web Terminal
- [KoKo](https://github.com/jumpserver/koko) JumpServer Character protocaol Connector, replace original Python Version [Coco](https://github.com/jumpserver/coco)
- [Lion](https://github.com/jumpserver/lion-release) JumpServer Graphics protocol Connectorrely on [Apache Guacamole](https://guacamole.apache.org/)
### Contribution
If you have any good ideas or helping us to fix bugs, please submit a Pull Request and accept our thanks :)
Thanks to the following contributors for making JumpServer better everyday!
<a href="https://github.com/jumpserver/jumpserver/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/jumpserver" />
</a>
<a href="https://github.com/jumpserver/koko/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/koko" />
</a>
<a href="https://github.com/jumpserver/lina/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/lina" />
</a>
<a href="https://github.com/jumpserver/luna/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/luna" />
</a>
### Thanks to
- [Apache Guacamole](https://guacamole.apache.org/) Web page connection RDP, SSH, VNC protocol equipment. JumpServer graphical connection dependent.
- [OmniDB](https://omnidb.org/) Web page connection to databases. JumpServer Web database dependent.
### JumpServer Enterprise Version
- [Apply for it](https://jinshuju.net/f/kyOYpi)
### Case Study
- [JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147)
- [JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [携程 JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [小红书的JumpServer堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
- [江苏农信JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)。
### For safety instructions
JumpServer is a security product. Please refer to [Basic Security Recommendations](https://docs.jumpserver.org/zh/master/install/install_security/) for deployment and installation.
If you find a security problem, please contact us directly
- ibuler@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
### License & Copyright
Copyright (c) 2014-2024 FIT2CLOUD Tech, Inc., All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/gpl-3.0.htmll
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

View File

@@ -4,7 +4,6 @@ from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'
verbose_name = 'App Accounts'
def ready(self):
from . import signal_handlers # noqa

View File

@@ -3,7 +3,6 @@ import time
from collections import defaultdict, OrderedDict
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from xlsxwriter import Workbook
@@ -18,7 +17,7 @@ from terminal.models.component.storage import ReplayStorage
from users.models import User
PATH = os.path.join(os.path.dirname(settings.BASE_DIR), 'tmp')
split_help_text = _('The account key will be split into two parts and sent')
class RecipientsNotFound(Exception):
pass
@@ -116,8 +115,8 @@ class AssetAccountHandler(BaseAccountHandler):
data = AccountSecretSerializer(_accounts, many=True).data
cls.handler_secret(data, section)
data_map.update(cls.add_rows(data, header_fields, sheet_name))
number_of_backup_accounts = _('Number of backup accounts')
print('\n\033[33m- {}: {}\033[0m'.format(number_of_backup_accounts, accounts.count()))
print('\n\033[33m- 共备份 {} 条账号\033[0m'.format(accounts.count()))
return data_map
@@ -128,10 +127,9 @@ class AccountBackupHandler:
self.is_frozen = False # 任务状态冻结标志
def create_excel(self, section='complete'):
hint = _('Generating asset or application related backup information files')
print(
'\n'
f'\033[32m>>> {hint}\033[0m'
'\033[32m>>> 正在生成资产或应用相关备份信息文件\033[0m'
''
)
# Print task start date
@@ -153,9 +151,7 @@ class AccountBackupHandler:
wb.close()
files.append(filename)
timedelta = round((time.time() - time_start), 2)
time_cost = _('Time cost')
file_created = _('Backup file creation completed')
print('{}: {} {}s'.format(file_created, time_cost, timedelta))
print('创建备份文件完成: 用时 {}s'.format(timedelta))
return files
def send_backup_mail(self, files, recipients):
@@ -164,7 +160,7 @@ class AccountBackupHandler:
recipients = User.objects.filter(id__in=list(recipients))
print(
'\n'
f'\033[32m>>> {_("Start sending backup emails")}\033[0m'
'\033[32m>>> 开始发送备份邮件\033[0m'
''
)
plan_name = self.plan_name
@@ -176,8 +172,7 @@ class AccountBackupHandler:
encrypt_and_compress_zip_file(attachment, user.secret_key, files)
attachment_list = [attachment, ]
AccountBackupExecutionTaskMsg(plan_name, user).publish(attachment_list)
email_sent_to = _('Email sent to')
print('{} {}({})'.format(email_sent_to, user, user.email))
print('邮件已发送至{}({})'.format(user, user.email))
for file in files:
os.remove(file)
@@ -187,22 +182,20 @@ class AccountBackupHandler:
recipients = ReplayStorage.objects.filter(id__in=list(recipients))
print(
'\n'
'\033[32m>>> 📃 ---> sftp \033[0m'
'\033[32m>>> 开始发送备份文件到sftp服务器\033[0m'
''
)
plan_name = self.plan_name
encrypt_file = _('Encrypting files using encryption password')
for rec in recipients:
attachment = os.path.join(PATH, f'{plan_name}-{local_now_filename()}-{time.time()}.zip')
if password:
print(f'\033[32m>>> {encrypt_file}\033[0m')
print('\033[32m>>> 使用加密密码对文件进行加密中\033[0m')
encrypt_and_compress_zip_file(attachment, password, files)
else:
zip_files(attachment, files)
attachment_list = attachment
AccountBackupByObjStorageExecutionTaskMsg(plan_name, rec).publish(attachment_list)
file_sent_to = _('The backup file will be sent to')
print('{}: {}({})'.format(file_sent_to, rec.name, rec.id))
print('备份文件将发送至{}({})'.format(rec.name, rec.id))
for file in files:
os.remove(file)
@@ -210,15 +203,14 @@ class AccountBackupHandler:
self.execution.reason = reason[:1024]
self.execution.is_success = is_success
self.execution.save()
finish = _('Finish')
print(f'\n{finish}\n')
print('\n已完成对任务状态的更新\n')
@staticmethod
def step_finished(is_success):
if is_success:
print(_('Success'))
print('任务执行成功')
else:
print(_('Failed'))
print('任务执行失败')
def _run(self):
is_success = False
@@ -231,6 +223,8 @@ class AccountBackupHandler:
self.backup_by_obj_storage()
except Exception as e:
self.is_frozen = True
print('任务执行被异常中断')
print('下面打印发生异常的 Traceback 信息 : ')
print(e)
error = str(e)
else:
@@ -245,16 +239,15 @@ class AccountBackupHandler:
zip_encrypt_password = AccountBackupAutomation.objects.get(id=object_id).zip_encrypt_password
obj_recipients_part_one = self.execution.snapshot.get('obj_recipients_part_one', [])
obj_recipients_part_two = self.execution.snapshot.get('obj_recipients_part_two', [])
no_assigned_sftp_server = _('The backup task has no assigned sftp server')
if not obj_recipients_part_one and not obj_recipients_part_two:
print(
'\n'
f'\033[31m>>> {no_assigned_sftp_server}\033[0m'
'\033[31m>>> 该备份任务未分配sftp服务器\033[0m'
''
)
raise RecipientsNotFound('Not Found Recipients')
if obj_recipients_part_one and obj_recipients_part_two:
print(f'\033[32m>>> {split_help_text}\033[0m')
print('\033[32m>>> 账号的密钥将被拆分成前后两部分发送\033[0m')
files = self.create_excel(section='front')
self.send_backup_obj_storage(files, obj_recipients_part_one, zip_encrypt_password)
@@ -266,19 +259,17 @@ class AccountBackupHandler:
self.send_backup_obj_storage(files, recipients, zip_encrypt_password)
def backup_by_email(self):
warn_text = _('The backup task has no assigned recipient')
recipients_part_one = self.execution.snapshot.get('recipients_part_one', [])
recipients_part_two = self.execution.snapshot.get('recipients_part_two', [])
if not recipients_part_one and not recipients_part_two:
print(
'\n'
f'\033[31m>>> {warn_text}\033[0m'
'\033[31m>>> 该备份任务未分配收件人\033[0m'
''
)
raise RecipientsNotFound('Not Found Recipients')
if recipients_part_one and recipients_part_two:
print(f'\033[32m>>> {split_help_text}\033[0m')
print('\033[32m>>> 账号的密钥将被拆分成前后两部分发送\033[0m')
files = self.create_excel(section='front')
self.send_backup_mail(files, recipients_part_one)
@@ -290,18 +281,15 @@ class AccountBackupHandler:
self.send_backup_mail(files, recipients)
def run(self):
plan_start = _('Plan start')
plan_end = _('Plan end')
time_cost = _('Time cost')
error = _('An exception occurred during task execution')
print('{}: {}'.format(plan_start, local_now_display()))
print('任务开始: {}'.format(local_now_display()))
time_start = time.time()
try:
self._run()
except Exception as e:
print(error)
print('任务运行出现异常')
print('下面显示异常 Traceback 信息: ')
print(e)
finally:
print('\n{}: {}'.format(plan_end, local_now_display()))
print('\n任务结束: {}'.format(local_now_display()))
timedelta = round((time.time() - time_start), 2)
print('{}: {}s'.format(time_cost, timedelta))
print('用时: {}s'.format(timedelta))

View File

@@ -3,7 +3,6 @@
import time
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from common.utils.timezone import local_now_display
from .handlers import AccountBackupHandler
@@ -20,8 +19,7 @@ class AccountBackupManager:
def do_run(self):
execution = self.execution
account_backup_execution_being_executed = _('The account backup plan is being executed')
print(f'\n\033[33m# {account_backup_execution_being_executed}\033[0m')
print('\n\033[33m# 账号备份计划正在执行\033[0m')
handler = AccountBackupHandler(execution)
handler.run()
@@ -34,11 +32,9 @@ class AccountBackupManager:
self.date_end = timezone.now()
print('\n\n' + '-' * 80)
plan_execution_end = _('Plan execution end')
print('{} {}\n'.format(plan_execution_end, local_now_display()))
print('计划执行结束 {}\n'.format(local_now_display()))
self.timedelta = self.time_end - self.time_start
time_cost = _('Time cost')
print('{}: {}s'.format(time_cost, self.timedelta))
print('用时: {}s'.format(self.timedelta))
self.execution.timedelta = self.timedelta
self.execution.save()

View File

@@ -53,3 +53,5 @@
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
register: result
failed_when: not result.is_available

View File

@@ -36,7 +36,8 @@
name: "{{ account.username }}"
password: "{{ account.secret }}"
host: "%"
priv: "{{ account.username + '.*:USAGE' if db_name == '' else db_name + '.*:ALL' }}"
priv: "{{ omit if db_name == '' else db_name + '.*:ALL' }}"
append_privs: "{{ db_name != '' | bool }}"
ignore_errors: true
when: db_info is succeeded

View File

@@ -39,3 +39,5 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
db: "{{ jms_asset.spec_info.db_name }}"
register: result
failed_when: not result.is_available

View File

@@ -35,12 +35,24 @@
- user_info.failed
- params.groups
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('des') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: remove jumpserver ssh key
@@ -57,19 +69,9 @@
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- params.sudo
- name: Refresh connection
ansible.builtin.meta: reset_connection
@@ -86,7 +88,9 @@
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@@ -97,5 +101,7 @@
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@@ -5,6 +5,12 @@ type:
- AIX
method: change_secret
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@@ -34,6 +40,11 @@ i18n:
ja: 'Ansible user モジュールを使用してアカウントのパスワード変更 (DES)'
en: 'Using Ansible module user to change account secret (DES)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@@ -49,6 +60,11 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'

View File

@@ -35,12 +35,24 @@
- user_info.failed
- params.groups
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: remove jumpserver ssh key
@@ -57,19 +69,9 @@
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- params.sudo
- name: Refresh connection
ansible.builtin.meta: reset_connection
@@ -86,7 +88,9 @@
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@@ -97,5 +101,7 @@
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@@ -6,6 +6,12 @@ type:
- linux
method: change_secret
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@@ -36,6 +42,11 @@ i18n:
ja: 'Ansible user モジュールを使用して アカウントのパスワード変更 (SHA512)'
en: 'Using Ansible module user to change account secret (SHA512)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@@ -51,6 +62,11 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'

View File

@@ -28,4 +28,6 @@
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change

View File

@@ -31,5 +31,7 @@
login_password: "{{ account.secret }}"
login_secret_type: "{{ account.secret_type }}"
login_private_key_path: "{{ account.private_key_path }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change
delegate_to: localhost

View File

@@ -97,9 +97,10 @@ class ChangeSecretManager(AccountBasePlaybookManager):
return host
accounts = self.get_accounts(account)
error_msg = _("No pending accounts found")
if not accounts:
print(f'{asset}: {error_msg}')
print('没有发现待处理的账号: %s 用户ID: %s 类型: %s' % (
asset.name, self.account_ids, self.secret_type
))
return []
records = []
@@ -133,6 +134,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
record_id = self.record_map[asset_account_id]
try:
recorder = ChangeSecretRecord.objects.get(id=record_id)
new_secret = recorder.new_secret
except ChangeSecretRecord.DoesNotExist:
print(f"Record {record_id} not found")
continue

View File

@@ -53,3 +53,5 @@
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
register: result
failed_when: not result.is_available

View File

@@ -36,7 +36,8 @@
name: "{{ account.username }}"
password: "{{ account.secret }}"
host: "%"
priv: "{{ account.username + '.*:USAGE' if db_name == '' else db_name + '.*:ALL' }}"
priv: "{{ omit if db_name == '' else db_name + '.*:ALL' }}"
append_privs: "{{ db_name != '' | bool }}"
ignore_errors: true
when: db_info is succeeded

View File

@@ -31,7 +31,6 @@
role_attr_flags: LOGIN
ignore_errors: true
when: result is succeeded
register: change_info
- name: Verify password
community.postgresql.postgresql_ping:
@@ -40,8 +39,5 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
db: "{{ jms_asset.spec_info.db_name }}"
when:
- result is succeeded
- change_info is succeeded
register: result
failed_when: not result.is_available

View File

@@ -35,12 +35,24 @@
- user_info.failed
- params.groups
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('des') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: remove jumpserver ssh key
@@ -57,19 +69,9 @@
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- params.sudo
- name: Refresh connection
ansible.builtin.meta: reset_connection
@@ -86,7 +88,9 @@
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@@ -97,6 +101,8 @@
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@@ -5,6 +5,12 @@ type:
- AIX
method: push_account
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@@ -34,6 +40,11 @@ i18n:
ja: 'Ansible user モジュールを使用して Aix アカウントをプッシュする (DES)'
en: 'Using Ansible module user to push account (DES)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@@ -49,6 +60,11 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'

View File

@@ -35,12 +35,24 @@
- user_info.failed
- params.groups
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: remove jumpserver ssh key
@@ -57,19 +69,9 @@
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- params.sudo
- name: Refresh connection
ansible.builtin.meta: reset_connection
@@ -86,7 +88,9 @@
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@@ -97,6 +101,8 @@
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@@ -6,6 +6,12 @@ type:
- linux
method: push_account
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@@ -36,6 +42,11 @@ i18n:
ja: 'Ansible user モジュールを使用してアカウントをプッシュする (sha512)'
en: 'Using Ansible module user to push account (sha512)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@@ -51,6 +62,11 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'

View File

@@ -28,4 +28,6 @@
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change

View File

@@ -31,5 +31,7 @@
login_password: "{{ account.secret }}"
login_secret_type: "{{ account.secret_type }}"
login_private_key_path: "{{ account.private_key_path }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change
delegate_to: localhost

View File

@@ -2,7 +2,6 @@
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}"
tasks:
- name: "Remove account"

View File

@@ -16,3 +16,5 @@
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert }}"
register: result
failed_when: not result.is_available

View File

@@ -8,6 +8,7 @@
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
ansible_ssh_private_key_file: "{{ account.private_key_path }}"
ansible_timeout: 30
when: not account.become.ansible_become
- name: Verify account connectivity(Switch)
@@ -20,4 +21,5 @@
ansible_become_method: "{{ account.become.ansible_become_method }}"
ansible_become_user: "{{ account.become.ansible_become_user }}"
ansible_become_password: "{{ account.become.ansible_become_password }}"
ansible_timeout: 30
when: account.become.ansible_become

View File

@@ -9,3 +9,4 @@
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
ansible_timeout: 30

View File

@@ -1,8 +1,10 @@
# Generated by Django 4.1.13 on 2024-05-09 03:16
# Generated by Django 3.2.14 on 2022-12-28 07:29
import uuid
import django.db.models.deletion
import simple_history.models
from django.conf import settings
from django.db import migrations, models
import common.db.encoder
@@ -10,10 +12,11 @@ import common.db.fields
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('assets', '0098_auto_20220430_2126'),
]
operations = [
@@ -26,144 +29,55 @@ class Migration(migrations.Migration):
('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')),
('connectivity', models.CharField(choices=[('-', 'Unknown'), ('ok', 'OK'), ('err', 'Error')], default='-', max_length=16, verbose_name='Connectivity')),
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('connectivity',
models.CharField(choices=[('-', 'Unknown'), ('ok', 'Ok'), ('err', 'Error')], default='-',
max_length=16, verbose_name='Connectivity')),
('date_verified', models.DateTimeField(null=True, verbose_name='Date verified')),
('_secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')),
('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16, verbose_name='Secret type')),
('secret_type', models.CharField(
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16,
verbose_name='Secret type')),
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('privileged', models.BooleanField(default=False, verbose_name='Privileged')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('version', models.IntegerField(default=0, verbose_name='Version')),
('source', models.CharField(default='local', max_length=30, verbose_name='Source')),
('source_id', models.CharField(blank=True, max_length=128, null=True, verbose_name='Source ID')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts',
to='assets.asset', verbose_name='Asset')),
('su_from',
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to',
to='accounts.account', verbose_name='Su from')),
],
options={
'verbose_name': 'Account',
'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret'), ('verify_account', 'Can verify account'), ('push_account', 'Can push account'), ('remove_account', 'Can remove account')],
},
),
migrations.CreateModel(
name='AccountBackupAutomation',
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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')),
('types', models.JSONField(default=list)),
('backup_type', models.CharField(choices=[('email', 'Email'), ('object_storage', 'SFTP')], default='email', max_length=128, verbose_name='Backup type')),
('is_password_divided_by_email', models.BooleanField(default=True, verbose_name='Password divided')),
('is_password_divided_by_obj_storage', models.BooleanField(default=True, verbose_name='Password divided')),
('zip_encrypt_password', common.db.fields.EncryptCharField(blank=True, max_length=4096, null=True, verbose_name='Zip encrypt password')),
],
options={
'verbose_name': 'Account backup plan',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='AccountBackupExecution',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('date_start', models.DateTimeField(auto_now_add=True, verbose_name='Date start')),
('timedelta', models.FloatField(default=0.0, null=True, verbose_name='Time')),
('snapshot', models.JSONField(blank=True, default=dict, encoder=common.db.encoder.ModelJSONFieldEncoder, null=True, verbose_name='Account backup snapshot')),
('trigger', models.CharField(choices=[('manual', 'Manual trigger'), ('timing', 'Timing trigger')], default='manual', max_length=128, verbose_name='Trigger mode')),
('reason', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Reason')),
('is_success', models.BooleanField(default=False, verbose_name='Is success')),
],
options={
'verbose_name': 'Account backup execution',
'ordering': ('-date_start',),
},
),
migrations.CreateModel(
name='AccountTemplate',
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')),
('_secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('secret_strategy', models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy')),
('password_rules', models.JSONField(default=dict, verbose_name='Password rules')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')),
('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16, verbose_name='Secret type')),
('privileged', models.BooleanField(default=False, verbose_name='Privileged')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('auto_push', models.BooleanField(default=False, verbose_name='Auto push')),
('push_params', models.JSONField(default=dict, verbose_name='Push params')),
],
options={
'verbose_name': 'Account template',
'permissions': [('view_accounttemplatesecret', 'Can view asset account template secret')],
},
),
migrations.CreateModel(
name='ChangeSecretRecord',
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)),
('old_secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Old secret')),
('new_secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='New secret')),
('date_started', models.DateTimeField(blank=True, null=True, verbose_name='Date started')),
('date_finished', models.DateTimeField(blank=True, null=True, verbose_name='Date finished')),
('status', models.CharField(default='pending', max_length=16, verbose_name='Status')),
('error', models.TextField(blank=True, null=True, verbose_name='Error')),
],
options={
'verbose_name': 'Change secret record',
'ordering': ('-date_created',),
},
),
migrations.CreateModel(
name='GatheredAccount',
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')),
('present', models.BooleanField(default=True, verbose_name='Present')),
('date_last_login', models.DateTimeField(null=True, verbose_name='Date login')),
('username', models.CharField(blank=True, db_index=True, max_length=32, verbose_name='Username')),
('address_last_login', models.CharField(default='', max_length=39, verbose_name='Address login')),
],
options={
'verbose_name': 'Gather asset accounts',
'ordering': ['asset'],
'permissions': [('view_accountsecret', 'Can view asset account secret'),
('view_historyaccount', 'Can view asset history account'),
('view_historyaccountsecret', 'Can view asset history account secret')],
'unique_together': {('username', 'asset', 'secret_type'), ('name', 'asset')},
},
),
migrations.CreateModel(
name='HistoricalAccount',
fields=[
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
('_secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16, verbose_name='Secret type')),
('secret_type', models.CharField(
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16,
verbose_name='Secret type')),
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('version', models.IntegerField(default=0, verbose_name='Version')),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField(db_index=True)),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_type',
models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user',
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+',
to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'historical Account',
@@ -174,16 +88,31 @@ class Migration(migrations.Migration):
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name='VirtualAccount',
name='AccountTemplate',
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')),
('alias', models.CharField(choices=[('@INPUT', 'Manual input'), ('@USER', 'Dynamic user'), ('@ANON', 'Anonymous account'), ('@SPEC', 'Specified account')], max_length=128, verbose_name='Alias')),
('secret_from_login', models.BooleanField(default=None, null=True, verbose_name='Secret from login')),
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')),
('secret_type', models.CharField(
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16,
verbose_name='Secret type')),
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('privileged', models.BooleanField(default=False, verbose_name='Privileged')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
],
options={
'verbose_name': 'Account template',
'permissions': [('view_accounttemplatesecret', 'Can view asset account template secret'),
('change_accounttemplatesecret', 'Can change asset account template secret')],
'unique_together': {('name', 'org_id')},
},
),
]

View File

@@ -1,103 +1,44 @@
# Generated by Django 4.1.13 on 2024-05-09 03:16
import django.db.models.deletion
from django.db import migrations, models
# Generated by Django 3.2.14 on 2022-12-28 10:39
import common.db.encoder
import common.db.fields
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('assets', '0001_initial'),
('assets', '0106_auto_20221228_1838'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='AccountBaseAutomation',
name='AccountBackupAutomation',
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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Cycle perform')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Regularly perform')),
('types', models.JSONField(default=list)),
('recipients', models.ManyToManyField(blank=True, related_name='recipient_escape_route_plans',
to=settings.AUTH_USER_MODEL, verbose_name='Recipient')),
],
options={
'verbose_name': 'Account automation task',
'proxy': True,
'indexes': [],
'constraints': [],
'verbose_name': 'Account backup plan',
'ordering': ['name'],
'unique_together': {('name', 'org_id')},
},
bases=('assets.baseautomation',),
),
migrations.CreateModel(
name='AutomationExecution',
fields=[
],
options={
'verbose_name': 'Automation execution',
'verbose_name_plural': 'Automation executions',
'permissions': [('view_changesecretexecution', 'Can view change secret execution'), ('add_changesecretexecution', 'Can add change secret execution'), ('view_gatheraccountsexecution', 'Can view gather accounts execution'), ('add_gatheraccountsexecution', 'Can add gather accounts execution'), ('view_pushaccountexecution', 'Can view push account execution'), ('add_pushaccountexecution', 'Can add push account execution')],
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('assets.automationexecution',),
),
migrations.CreateModel(
name='ChangeSecretAutomation',
fields=[
('baseautomation_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.baseautomation')),
('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16, verbose_name='Secret type')),
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('secret_strategy', models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy')),
('password_rules', models.JSONField(default=dict, verbose_name='Password rules')),
('ssh_key_change_strategy', models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy')),
],
options={
'verbose_name': 'Change secret automation',
},
bases=('accounts.accountbaseautomation', models.Model),
),
migrations.CreateModel(
name='GatherAccountsAutomation',
fields=[
('baseautomation_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.baseautomation')),
('is_sync_account', models.BooleanField(blank=True, default=False, verbose_name='Is sync account')),
],
options={
'verbose_name': 'Gather account automation',
},
bases=('accounts.accountbaseautomation',),
),
migrations.CreateModel(
name='PushAccountAutomation',
fields=[
('baseautomation_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.baseautomation')),
('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16, verbose_name='Secret type')),
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('secret_strategy', models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy')),
('password_rules', models.JSONField(default=dict, verbose_name='Password rules')),
('ssh_key_change_strategy', models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy')),
('triggers', models.JSONField(default=list, max_length=16, verbose_name='Triggers')),
('username', models.CharField(max_length=128, verbose_name='Username')),
('action', models.CharField(max_length=16, verbose_name='Action')),
],
options={
'verbose_name': 'Push asset account',
},
bases=('accounts.accountbaseautomation', models.Model),
),
migrations.CreateModel(
name='VerifyAccountAutomation',
fields=[
('baseautomation_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.baseautomation')),
],
options={
'verbose_name': 'Verify asset account',
},
bases=('accounts.accountbaseautomation',),
),
migrations.AlterUniqueTogether(
name='virtualaccount',
unique_together={('alias', 'org_id')},
),
)
]

View File

@@ -1,116 +1,198 @@
# Generated by Django 4.1.13 on 2024-05-09 03:16
# Generated by Django 3.2.16 on 2022-12-30 08:08
import uuid
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import common.db.encoder
import common.db.fields
class Migration(migrations.Migration):
initial = True
dependencies = [
('assets', '0001_initial'),
('terminal', '0001_initial'),
('accounts', '0002_auto_20220616_0021'),
('assets', '0107_automation'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0002_auto_20220616_0021'),
]
operations = [
migrations.AddField(
model_name='historicalaccount',
name='history_user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL),
migrations.CreateModel(
name='AccountBaseAutomation',
fields=[
],
options={
'verbose_name': 'Account automation task',
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('assets.baseautomation',),
),
migrations.AddField(
model_name='gatheredaccount',
name='asset',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.asset', verbose_name='Asset'),
migrations.CreateModel(
name='AutomationExecution',
fields=[
],
options={
'verbose_name': 'Automation execution',
'verbose_name_plural': 'Automation executions',
'permissions': [('view_changesecretexecution', 'Can view change secret execution'),
('add_changesecretexecution', 'Can add change secret execution'),
('view_gatheraccountsexecution', 'Can view gather accounts execution'),
('add_gatheraccountsexecution', 'Can add gather accounts execution')],
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('assets.automationexecution',),
),
migrations.AddField(
model_name='changesecretrecord',
name='account',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.account'),
migrations.CreateModel(
name='PushAccountAutomation',
fields=[
('baseautomation_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
primary_key=True, serialize=False, to='assets.baseautomation')),
('secret_type', models.CharField(
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16,
verbose_name='Secret type')),
('secret_strategy', models.CharField(choices=[('specific', 'Specific password'),
('random_one', 'All assets use the same random password'),
('random_all',
'All assets use different random password')],
default='specific', max_length=16,
verbose_name='Secret strategy')),
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('password_rules', models.JSONField(default=dict, verbose_name='Password rules')),
('ssh_key_change_strategy', models.CharField(
choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'),
('set_jms', 'Replace (The key generated by JumpServer) ')], default='add', max_length=16,
verbose_name='SSH key change strategy')),
('triggers', models.JSONField(default=list, max_length=16, verbose_name='Triggers')),
('username', models.CharField(max_length=128, verbose_name='Username')),
('action', models.CharField(max_length=16, verbose_name='Action')),
],
options={
'verbose_name': 'Push asset account',
},
bases=('accounts.accountbaseautomation', models.Model),
),
migrations.AddField(
model_name='changesecretrecord',
name='asset',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.asset'),
migrations.CreateModel(
name='GatherAccountsAutomation',
fields=[
('baseautomation_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
primary_key=True, serialize=False, to='assets.baseautomation')),
],
options={
'verbose_name': 'Gather asset accounts',
},
bases=('accounts.accountbaseautomation',),
),
migrations.AddField(
model_name='changesecretrecord',
name='execution',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.automationexecution'),
migrations.CreateModel(
name='VerifyAccountAutomation',
fields=[
('baseautomation_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
primary_key=True, serialize=False, to='assets.baseautomation')),
],
options={
'verbose_name': 'Verify asset account',
},
bases=('accounts.accountbaseautomation',),
),
migrations.AddField(
model_name='accounttemplate',
name='platforms',
field=models.ManyToManyField(blank=True, related_name='account_templates', to='assets.platform', verbose_name='Platforms'),
migrations.CreateModel(
name='ChangeSecretRecord',
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)),
('old_secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Old secret')),
('new_secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='New secret')),
('date_started', models.DateTimeField(blank=True, null=True, verbose_name='Date started')),
('date_finished', models.DateTimeField(blank=True, null=True, verbose_name='Date finished')),
('status', models.CharField(default='pending', max_length=16, verbose_name='Status')),
('error', models.TextField(blank=True, null=True, verbose_name='Error')),
('account',
models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.account')),
('asset', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.asset')),
('execution',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.automationexecution')),
],
options={
'verbose_name': 'Change secret record',
},
),
migrations.AddField(
model_name='accounttemplate',
name='su_from',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='accounts.accounttemplate', verbose_name='Su from'),
migrations.CreateModel(
name='AccountBackupExecution',
fields=[
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('date_start', models.DateTimeField(auto_now_add=True, verbose_name='Date start')),
('timedelta', models.FloatField(default=0.0, null=True, verbose_name='Time')),
('plan_snapshot',
models.JSONField(blank=True, default=dict, encoder=common.db.encoder.ModelJSONFieldEncoder, null=True,
verbose_name='Account backup snapshot')),
('trigger', models.CharField(choices=[('manual', 'Manual trigger'), ('timing', 'Timing trigger')],
default='manual', max_length=128, verbose_name='Trigger mode')),
('reason', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Reason')),
('is_success', models.BooleanField(default=False, verbose_name='Is success')),
('plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='execution',
to='accounts.accountbackupautomation', verbose_name='Account backup plan')),
],
options={
'verbose_name': 'Account backup execution',
'ordering': ('-date_start',),
},
),
migrations.AddField(
model_name='accountbackupexecution',
name='plan',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='execution', to='accounts.accountbackupautomation', verbose_name='Account backup plan'),
migrations.CreateModel(
name='ChangeSecretAutomation',
fields=[
('baseautomation_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
primary_key=True, serialize=False, to='assets.baseautomation')),
('secret_type', models.CharField(
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
('token', 'Token'), ('api_key', 'API key')], default='password', max_length=16,
verbose_name='Secret type')),
('secret_strategy', models.CharField(choices=[('specific', 'Specific password'),
('random_one', 'All assets use the same random password'),
('random_all',
'All assets use different random password')],
default='specific', max_length=16,
verbose_name='Secret strategy')),
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('password_rules', models.JSONField(default=dict, verbose_name='Password rules')),
('ssh_key_change_strategy', models.CharField(
choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'),
('set_jms', 'Replace (The key generated by JumpServer) ')], default='add', max_length=16,
verbose_name='SSH key change strategy')),
('recipients',
models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Recipient')),
],
options={
'verbose_name': 'Change secret automation',
},
bases=('accounts.accountbaseautomation', models.Model),
),
migrations.AddField(
model_name='accountbackupautomation',
name='obj_recipients_part_one',
field=models.ManyToManyField(blank=True, related_name='obj_recipient_part_one_plans', to='terminal.replaystorage', verbose_name='Object storage recipient part one'),
migrations.AlterModelOptions(
name='automationexecution',
options={'permissions': [('view_changesecretexecution', 'Can view change secret execution'),
('add_changesecretexecution', 'Can add change secret execution'),
('view_gatheraccountsexecution', 'Can view gather accounts execution'),
('add_gatheraccountsexecution', 'Can add gather accounts execution'),
('view_pushaccountexecution', 'Can view push account execution'),
('add_pushaccountexecution', 'Can add push account execution')],
'verbose_name': 'Automation execution', 'verbose_name_plural': 'Automation executions'},
),
migrations.AddField(
model_name='accountbackupautomation',
name='obj_recipients_part_two',
field=models.ManyToManyField(blank=True, related_name='obj_recipient_part_two_plans', to='terminal.replaystorage', verbose_name='Object storage recipient part two'),
),
migrations.AddField(
model_name='accountbackupautomation',
name='recipients_part_one',
field=models.ManyToManyField(blank=True, related_name='recipient_part_one_plans', to=settings.AUTH_USER_MODEL, verbose_name='Recipient part one'),
),
migrations.AddField(
model_name='accountbackupautomation',
name='recipients_part_two',
field=models.ManyToManyField(blank=True, related_name='recipient_part_two_plans', to=settings.AUTH_USER_MODEL, verbose_name='Recipient part two'),
),
migrations.AddField(
model_name='account',
name='asset',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='assets.asset', verbose_name='Asset'),
),
migrations.AddField(
model_name='account',
name='su_from',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='accounts.account', verbose_name='Su from'),
),
migrations.AlterUniqueTogether(
name='gatheredaccount',
unique_together={('username', 'asset')},
),
migrations.AddField(
model_name='gatheraccountsautomation',
name='recipients',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Recipient'),
),
migrations.AddField(
model_name='changesecretautomation',
name='recipients',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Recipient'),
),
migrations.AlterUniqueTogether(
name='accounttemplate',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='accountbackupautomation',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='account',
unique_together={('name', 'asset'), ('username', 'asset', 'secret_type')},
migrations.AlterModelOptions(
name='changesecretrecord',
options={'ordering': ('-date_started',), 'verbose_name': 'Change secret record'},
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2.16 on 2023-01-06 07:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0003_automation'),
]
operations = [
migrations.AlterField(
model_name='changesecretautomation',
name='secret_strategy',
field=models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy'),
),
migrations.AlterField(
model_name='pushaccountautomation',
name='secret_strategy',
field=models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy'),
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 3.2.16 on 2023-01-10 06:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0004_auto_20230106_1507'),
]
operations = [
migrations.AlterModelOptions(
name='changesecretrecord',
options={'ordering': ('-date_created',), 'verbose_name': 'Change secret record'},
),
]

View File

@@ -0,0 +1,38 @@
# Generated by Django 3.2.16 on 2023-02-07 04:41
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0108_alter_platform_charset'),
('accounts', '0005_alter_changesecretrecord_options'),
]
operations = [
migrations.CreateModel(
name='GatheredAccount',
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')),
('present', models.BooleanField(default=True, verbose_name='Present')),
('date_last_login', models.DateTimeField(null=True, verbose_name='Date last login')),
('username', models.CharField(blank=True, db_index=True, max_length=32, verbose_name='Username')),
('address_last_login', models.CharField(default='', max_length=39, verbose_name='Address last login')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.asset', verbose_name='Asset')),
],
options={
'verbose_name': 'Gather account',
'ordering': ['asset'],
'unique_together': {('username', 'asset')},
},
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2.16 on 2023-02-16 11:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0006_gatheredaccount'),
]
operations = [
migrations.AlterModelOptions(
name='account',
options={'permissions': [
('view_accountsecret', 'Can view asset account secret'),
('view_historyaccount', 'Can view asset history account'),
('view_historyaccountsecret', 'Can view asset history account secret'),
('verify_account', 'Can verify account'),
('push_account', 'Can push account'),
('remove_account', 'Can remove account'),
], 'verbose_name': 'Account'},
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 3.2.16 on 2023-02-23 09:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0007_alter_account_options'),
]
operations = [
migrations.AlterModelOptions(
name='gatheredaccount',
options={'ordering': ['asset'], 'verbose_name': 'Gather account automation'},
),
]

View File

@@ -0,0 +1,69 @@
# Generated by Django 3.2.16 on 2023-03-07 07:36
from django.db import migrations
from django.db.models import Q
def get_nodes_all_assets(apps, *nodes):
node_model = apps.get_model('assets', 'Node')
asset_model = apps.get_model('assets', 'Asset')
node_ids = set()
descendant_node_query = Q()
for n in nodes:
node_ids.add(n.id)
descendant_node_query |= Q(key__istartswith=f'{n.key}:')
if descendant_node_query:
_ids = node_model.objects.order_by().filter(descendant_node_query).values_list('id', flat=True)
node_ids.update(_ids)
return asset_model.objects.order_by().filter(nodes__id__in=node_ids).distinct()
def get_all_assets(apps, snapshot):
node_model = apps.get_model('assets', 'Node')
asset_model = apps.get_model('assets', 'Asset')
asset_ids = snapshot.get('assets', [])
node_ids = snapshot.get('nodes', [])
nodes = node_model.objects.filter(id__in=node_ids)
node_asset_ids = get_nodes_all_assets(apps, *nodes).values_list('id', flat=True)
asset_ids = set(list(asset_ids) + list(node_asset_ids))
return asset_model.objects.filter(id__in=asset_ids)
def migrate_account_usernames_to_ids(apps, schema_editor):
db_alias = schema_editor.connection.alias
execution_model = apps.get_model('accounts', 'AutomationExecution')
account_model = apps.get_model('accounts', 'Account')
executions = execution_model.objects.using(db_alias).all()
executions_update = []
for execution in executions:
snapshot = execution.snapshot
accounts = account_model.objects.none()
account_usernames = snapshot.get('accounts', [])
for asset in get_all_assets(apps, snapshot):
accounts = accounts | asset.accounts.all()
secret_type = snapshot.get('secret_type')
if secret_type:
ids = accounts.filter(
username__in=account_usernames,
secret_type=secret_type
).values_list('id', flat=True)
else:
ids = accounts.filter(
username__in=account_usernames
).values_list('id', flat=True)
snapshot['accounts'] = [str(_id) for _id in ids]
execution.snapshot = snapshot
executions_update.append(execution)
execution_model.objects.bulk_update(executions_update, ['snapshot'])
class Migration(migrations.Migration):
dependencies = [
('accounts', '0008_alter_gatheredaccount_options'),
]
operations = [
migrations.RunPython(migrate_account_usernames_to_ids),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 3.2.16 on 2023-03-23 08:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0009_account_usernames_to_ids'),
]
operations = [
migrations.AddField(
model_name='gatheraccountsautomation',
name='is_sync_account',
field=models.BooleanField(blank=True, default=False, verbose_name='Is sync account'),
),
migrations.AddField(
model_name='account',
name='source_id',
field=models.CharField(max_length=128, null=True, blank=True, verbose_name='Source ID'),
),
]

View File

@@ -0,0 +1,29 @@
# Generated by Django 3.2.17 on 2023-05-06 06:43
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('accounts', '0010_gatheraccountsautomation_is_sync_account'),
]
operations = [
migrations.AddField(
model_name='accounttemplate',
name='su_from',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='accounts.accounttemplate', verbose_name='Su from'),
),
migrations.AlterField(
model_name='changesecretautomation',
name='ssh_key_change_strategy',
field=models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'),
),
migrations.AlterField(
model_name='pushaccountautomation',
name='ssh_key_change_strategy',
field=models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 3.2.19 on 2023-06-21 06:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0011_auto_20230506_1443'),
]
operations = [
migrations.RenameField(
model_name='account',
old_name='secret',
new_name='_secret',
),
migrations.RenameField(
model_name='accounttemplate',
old_name='secret',
new_name='_secret',
),
migrations.RenameField(
model_name='historicalaccount',
old_name='secret',
new_name='_secret',
),
]

View File

@@ -0,0 +1,77 @@
# Generated by Django 4.1.10 on 2023-08-03 08:28
from django.conf import settings
from django.db import migrations, models
import common.db.encoder
def migrate_recipients(apps, schema_editor):
account_backup_model = apps.get_model('accounts', 'AccountBackupAutomation')
execution_model = apps.get_model('accounts', 'AccountBackupExecution')
for account_backup in account_backup_model.objects.all():
recipients = list(account_backup.recipients.all())
if not recipients:
continue
account_backup.recipients_part_one.set(recipients)
objs = []
for execution in execution_model.objects.all():
snapshot = execution.snapshot
recipients = snapshot.pop('recipients', {})
snapshot.update({'recipients_part_one': recipients, 'recipients_part_two': {}})
objs.append(execution)
execution_model.objects.bulk_update(objs, ['snapshot'])
def migrate_snapshot(apps, schema_editor):
model = apps.get_model('accounts', 'AccountBackupExecution')
objs = []
for execution in model.objects.all():
execution.snapshot = execution.plan_snapshot
objs.append(execution)
model.objects.bulk_update(objs, ['snapshot'])
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0012_auto_20230621_1456'),
]
operations = [
migrations.AddField(
model_name='accountbackupautomation',
name='recipients_part_one',
field=models.ManyToManyField(
blank=True, related_name='recipient_part_one_plans',
to=settings.AUTH_USER_MODEL, verbose_name='Recipient part one'
),
),
migrations.AddField(
model_name='accountbackupautomation',
name='recipients_part_two',
field=models.ManyToManyField(
blank=True, related_name='recipient_part_two_plans',
to=settings.AUTH_USER_MODEL, verbose_name='Recipient part two'
),
),
migrations.AddField(
model_name='accountbackupexecution',
name='snapshot',
field=models.JSONField(
default=dict, encoder=common.db.encoder.ModelJSONFieldEncoder,
null=True, blank=True, verbose_name='Account backup snapshot'
),
),
migrations.RunPython(migrate_snapshot),
migrations.RunPython(migrate_recipients),
migrations.RemoveField(
model_name='accountbackupexecution',
name='plan_snapshot',
),
migrations.RemoveField(
model_name='accountbackupautomation',
name='recipients',
),
]

View File

@@ -0,0 +1,31 @@
# Generated by Django 4.1.10 on 2023-08-01 09:12
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0013_account_backup_recipients'),
]
operations = [
migrations.CreateModel(
name='VirtualAccount',
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')),
('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')),
('alias', models.CharField(choices=[('@INPUT', 'Manual input'), ('@USER', 'Dynamic user'), ('@ANON', 'Anonymous account'), ('@SPEC', 'Specified account')], max_length=128, verbose_name='Alias')),
('secret_from_login', models.BooleanField(default=None, null=True, verbose_name='Secret from login')),
],
options={
'unique_together': {('alias', 'org_id')},
},
),
]

View File

@@ -0,0 +1,34 @@
# Generated by Django 4.1.10 on 2023-08-25 03:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0122_auto_20230803_1553'),
('accounts', '0014_virtualaccount'),
]
operations = [
migrations.AddField(
model_name='accounttemplate',
name='auto_push',
field=models.BooleanField(default=False, verbose_name='Auto push'),
),
migrations.AddField(
model_name='accounttemplate',
name='platforms',
field=models.ManyToManyField(related_name='account_templates', to='assets.platform', verbose_name='Platforms', blank=True),
),
migrations.AddField(
model_name='accounttemplate',
name='push_params',
field=models.JSONField(default=dict, verbose_name='Push params'),
),
migrations.AddField(
model_name='accounttemplate',
name='secret_strategy',
field=models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1.10 on 2023-09-18 08:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0015_auto_20230825_1120'),
]
operations = [
migrations.AddField(
model_name='accounttemplate',
name='password_rules',
field=models.JSONField(default=dict, verbose_name='Password rules'),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 4.1.10 on 2023-10-24 05:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0016_accounttemplate_password_rules'),
]
operations = [
migrations.AlterModelOptions(
name='automationexecution',
options={
'permissions': [
('view_changesecretexecution', 'Can view change secret execution'),
('add_changesecretexecution', 'Can add change secret execution'),
('view_gatheraccountsexecution', 'Can view gather accounts execution'),
('add_gatheraccountsexecution', 'Can add gather accounts execution'),
('view_pushaccountexecution', 'Can view push account execution'),
('add_pushaccountexecution', 'Can add push account execution')
],
'verbose_name': 'Automation execution', 'verbose_name_plural': 'Automation executions'},
),
]

View File

@@ -0,0 +1,45 @@
# Generated by Django 4.1.10 on 2023-11-03 07:10
import common.db.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('terminal', '0067_alter_replaystorage_type'),
('accounts', '0017_alter_automationexecution_options'),
]
operations = [
migrations.AddField(
model_name='accountbackupautomation',
name='backup_type',
field=models.CharField(choices=[('email', 'Email'), ('object_storage', 'Object Storage')], default='email', max_length=128),
),
migrations.AddField(
model_name='accountbackupautomation',
name='is_password_divided_by_email',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='accountbackupautomation',
name='is_password_divided_by_obj_storage',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='accountbackupautomation',
name='obj_recipients_part_one',
field=models.ManyToManyField(blank=True, related_name='obj_recipient_part_one_plans', to='terminal.replaystorage', verbose_name='Object Storage Recipient part one'),
),
migrations.AddField(
model_name='accountbackupautomation',
name='obj_recipients_part_two',
field=models.ManyToManyField(blank=True, related_name='obj_recipient_part_two_plans', to='terminal.replaystorage', verbose_name='Object Storage Recipient part two'),
),
migrations.AddField(
model_name='accountbackupautomation',
name='zip_encrypt_password',
field=common.db.fields.EncryptCharField(blank=True, max_length=4096, null=True, verbose_name='Zip Encrypt Password'),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 4.1.10 on 2023-10-31 06:12
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0018_accountbackupautomation_backup_type_and_more'),
]
operations = [
migrations.AddField(
model_name='gatheraccountsautomation',
name='recipients',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Recipient'),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 4.1.10 on 2023-11-16 02:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0019_gatheraccountsautomation_recipients'),
]
operations = [
migrations.AlterField(
model_name='accountbackupautomation',
name='backup_type',
field=models.CharField(choices=[('email', 'Email'), ('object_storage', 'SFTP')], default='email', max_length=128, verbose_name='Backup Type'),
),
migrations.AlterField(
model_name='accountbackupautomation',
name='is_password_divided_by_email',
field=models.BooleanField(default=True, verbose_name='Is Password Divided'),
),
migrations.AlterField(
model_name='accountbackupautomation',
name='is_password_divided_by_obj_storage',
field=models.BooleanField(default=True, verbose_name='Is Password Divided'),
),
]

View File

@@ -53,7 +53,8 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount):
on_delete=models.SET_NULL, verbose_name=_("Su from")
)
version = models.IntegerField(default=0, verbose_name=_('Version'))
history = AccountHistoricalRecords(included_fields=['id', '_secret', 'secret_type', 'version'])
history = AccountHistoricalRecords(included_fields=['id', '_secret', 'secret_type', 'version'],
verbose_name=_("historical Account"))
source = models.CharField(max_length=30, default=Source.LOCAL, verbose_name=_('Source'))
source_id = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Source ID'))
@@ -119,7 +120,8 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount):
return auth
auth.update(self.make_account_ansible_vars(su_from))
become_method = platform.su_method if platform.su_method else 'sudo'
become_method = platform.ansible_become_method
password = su_from.secret if become_method == 'sudo' else self.secret
auth['ansible_become'] = True
auth['ansible_become_method'] = become_method

View File

@@ -24,9 +24,9 @@ logger = get_logger(__file__)
class AccountBackupAutomation(PeriodTaskModelMixin, JMSOrgBaseModel):
types = models.JSONField(default=list)
backup_type = models.CharField(max_length=128, choices=AccountBackupType.choices,
default=AccountBackupType.email.value, verbose_name=_('Backup type'))
is_password_divided_by_email = models.BooleanField(default=True, verbose_name=_('Password divided'))
is_password_divided_by_obj_storage = models.BooleanField(default=True, verbose_name=_('Password divided'))
default=AccountBackupType.email.value, verbose_name=_('Backup Type'))
is_password_divided_by_email = models.BooleanField(default=True, verbose_name=_('Is Password Divided'))
is_password_divided_by_obj_storage = models.BooleanField(default=True, verbose_name=_('Is Password Divided'))
recipients_part_one = models.ManyToManyField(
'users.User', related_name='recipient_part_one_plans', blank=True,
verbose_name=_("Recipient part one")
@@ -37,15 +37,14 @@ class AccountBackupAutomation(PeriodTaskModelMixin, JMSOrgBaseModel):
)
obj_recipients_part_one = models.ManyToManyField(
'terminal.ReplayStorage', related_name='obj_recipient_part_one_plans', blank=True,
verbose_name=_("Object storage recipient part one")
verbose_name=_("Object Storage Recipient part one")
)
obj_recipients_part_two = models.ManyToManyField(
'terminal.ReplayStorage', related_name='obj_recipient_part_two_plans', blank=True,
verbose_name=_("Object storage recipient part two")
)
zip_encrypt_password = fields.EncryptCharField(
max_length=4096, blank=True, null=True, verbose_name=_('Zip encrypt password')
verbose_name=_("Object Storage Recipient part two")
)
zip_encrypt_password = fields.EncryptCharField(max_length=4096, blank=True, null=True,
verbose_name=_('Zip Encrypt Password'))
def __str__(self):
return f'{self.name}({self.org_id})'

View File

@@ -12,10 +12,10 @@ __all__ = ['GatherAccountsAutomation', 'GatheredAccount']
class GatheredAccount(JMSOrgBaseModel):
present = models.BooleanField(default=True, verbose_name=_("Present"))
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date login"))
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date last login"))
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset"))
username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username'))
address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address login"))
address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address last login"))
@property
def address(self):
@@ -41,7 +41,7 @@ class GatheredAccount(JMSOrgBaseModel):
Account.objects.bulk_create(account_objs)
class Meta:
verbose_name = _("Gather asset accounts")
verbose_name = _('Gather account automation')
unique_together = [
('username', 'asset'),
]
@@ -72,4 +72,4 @@ class GatherAccountsAutomation(AccountBaseAutomation):
super().save(*args, **kwargs)
class Meta:
verbose_name = _('Gather account automation')
verbose_name = _("Gather asset accounts")

View File

@@ -29,6 +29,7 @@ class AccountTemplate(LabeledMixin, BaseAccount, SecretWithRandomMixin):
)
permissions = [
('view_accounttemplatesecret', _('Can view asset account template secret')),
('change_accounttemplatesecret', _('Can change asset account template secret')),
]
def __str__(self):

View File

@@ -15,7 +15,6 @@ class VirtualAccount(JMSOrgBaseModel):
class Meta:
unique_together = [('alias', 'org_id')]
verbose_name = _('Virtual account')
@property
def name(self):

View File

@@ -31,9 +31,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer):
default=False, label=_("Push now"), write_only=True
)
params = serializers.JSONField(
decoder=None, encoder=None, required=False,
style={'base_template': 'textarea.html'},
label=_('Params'),
decoder=None, encoder=None, required=False, style={'base_template': 'textarea.html'}
)
on_invalid = LabeledChoiceField(
choices=AccountInvalidPolicy.choices, default=AccountInvalidPolicy.ERROR,
@@ -81,21 +79,28 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer):
@staticmethod
def get_template_attr_for_account(template):
# Set initial data from template
field_names = [
'name', 'username', 'secret', 'push_params',
'secret_type', 'privileged', 'is_active'
'name', 'username',
'secret_type', 'secret',
'privileged', 'is_active'
]
field_map = {
'push_params': 'params',
'auto_push': 'push_now'
}
field_names.extend(field_map.keys())
attrs = {}
for name in field_names:
value = getattr(template, name, None)
if value is None:
continue
if name == 'push_params':
attrs['params'] = value
else:
attrs[name] = value
attr_name = field_map.get(name, name)
attrs[attr_name] = value
attrs['secret'] = template.get_secret()
return attrs
@@ -178,7 +183,8 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer):
params = validated_data.pop('params', None)
self.clean_auth_fields(validated_data)
instance, stat = self.do_create(validated_data)
self.push_account_if_need(instance, push_now, params, stat)
if instance.source == Source.LOCAL:
self.push_account_if_need(instance, push_now, params, stat)
return instance
def update(self, instance, validated_data):
@@ -280,8 +286,8 @@ class AssetAccountBulkSerializer(
fields = [
'name', 'username', 'secret', 'secret_type', 'passphrase',
'privileged', 'is_active', 'comment', 'template',
'on_invalid', 'push_now', 'assets', 'su_from_username',
'source', 'source_id',
'on_invalid', 'push_now', 'params', 'assets',
'su_from_username', 'source', 'source_id',
]
extra_kwargs = {
'name': {'required': False},
@@ -419,16 +425,23 @@ class AssetAccountBulkSerializer(
return results
@staticmethod
def push_accounts_if_need(results, push_now):
def push_accounts_if_need(results, push_now, params):
if not push_now:
return
accounts = [str(v['instance']) for v in results if v.get('instance')]
push_accounts_to_assets_task.delay(accounts)
account_ids = [v['instance'] for v in results if v.get('instance')]
accounts = Account.objects.filter(id__in=account_ids, source=Source.LOCAL)
if not accounts.exists():
return
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):
params = validated_data.pop('params', None)
push_now = validated_data.pop('push_now', False)
results = self.perform_bulk_create(validated_data)
self.push_accounts_if_need(results, push_now)
self.push_accounts_if_need(results, push_now, params)
for res in results:
res['asset'] = str(res['asset'])
return results

View File

@@ -35,7 +35,8 @@ class AccountBackupSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSer
]
extra_kwargs = {
'name': {'required': True},
'executed_amount': {'label': _('Executions')},
'periodic_display': {'label': _('Periodic perform')},
'executed_amount': {'label': _('Executed amount')},
'recipients': {
'label': _('Recipient'),
'help_text': _('Currently only mail sending is supported')

View File

@@ -9,34 +9,26 @@ from common.serializers import ResourceLabelsMixin
from common.serializers.fields import EncryptedField, LabeledChoiceField
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
__all__ = ["AuthValidateMixin", "BaseAccountSerializer"]
__all__ = ['AuthValidateMixin', 'BaseAccountSerializer']
class AuthValidateMixin(serializers.Serializer):
secret_type = LabeledChoiceField(
choices=SecretType.choices, label=_("Secret type"), default="password"
choices=SecretType.choices, label=_('Secret type'), default='password'
)
secret = EncryptedField(
label=_("Secret"),
required=False,
max_length=40960,
allow_blank=True,
allow_null=True,
write_only=True,
label=_('Secret'), required=False, max_length=40960, allow_blank=True,
allow_null=True, write_only=True,
)
passphrase = serializers.CharField(
allow_blank=True,
allow_null=True,
required=False,
max_length=512,
write_only=True,
label=_("Passphrase"),
allow_blank=True, allow_null=True, required=False, max_length=512,
write_only=True, label=_('Key password')
)
@staticmethod
def handle_secret(secret, secret_type, passphrase=None):
if not secret:
return ""
return ''
if secret_type == SecretType.PASSWORD:
validate_password_for_ansible(secret)
return secret
@@ -48,15 +40,17 @@ class AuthValidateMixin(serializers.Serializer):
return secret
def clean_auth_fields(self, validated_data):
secret_type = validated_data.get("secret_type")
passphrase = validated_data.get("passphrase")
secret = validated_data.pop("secret", None)
validated_data["secret"] = self.handle_secret(secret, secret_type, passphrase)
for field in ("secret",):
secret_type = validated_data.get('secret_type')
passphrase = validated_data.get('passphrase')
secret = validated_data.pop('secret', None)
validated_data['secret'] = self.handle_secret(
secret, secret_type, passphrase
)
for field in ('secret',):
value = validated_data.get(field)
if not value:
validated_data.pop(field, None)
validated_data.pop("passphrase", None)
validated_data.pop('passphrase', None)
def create(self, validated_data):
self.clean_auth_fields(validated_data)
@@ -67,34 +61,22 @@ class AuthValidateMixin(serializers.Serializer):
return super().update(instance, validated_data)
class BaseAccountSerializer(
AuthValidateMixin, ResourceLabelsMixin, BulkOrgResourceModelSerializer
):
class BaseAccountSerializer(AuthValidateMixin, ResourceLabelsMixin, BulkOrgResourceModelSerializer):
class Meta:
model = BaseAccount
fields_mini = ["id", "name", "username"]
fields_mini = ['id', 'name', 'username']
fields_small = fields_mini + [
"secret_type",
"secret",
"passphrase",
"privileged",
"is_active",
"spec_info",
'secret_type', 'secret', 'passphrase',
'privileged', 'is_active',
]
fields_other = ["created_by", "date_created", "date_updated", "comment"]
fields = fields_small + fields_other + ["labels"]
fields_other = ['created_by', 'date_created', 'date_updated', 'comment']
fields = fields_small + fields_other + ['labels']
read_only_fields = [
"spec_info",
"date_verified",
"created_by",
"date_created",
'date_verified', 'created_by', 'date_created',
]
extra_kwargs = {
"spec_info": {"label": _("Spec info")},
"username": {
"help_text": _(
"* If no username is required for authentication, enter null. "
"For AD accounts, use the format username@domain."
)
},
'username': {'help_text': _(
"Tip: If no username is required for authentication, fill in `null`, "
"If AD account, like `username@domain`"
)},
}

View File

@@ -25,8 +25,7 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe
class Meta:
read_only_fields = [
'date_created', 'date_updated', 'created_by',
'periodic_display', 'executed_amount'
'date_created', 'date_updated', 'created_by', 'periodic_display', 'executed_amount'
]
fields = read_only_fields + [
'id', 'name', 'is_periodic', 'interval', 'crontab', 'comment',
@@ -35,7 +34,8 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe
extra_kwargs = {
'name': {'required': True},
'type': {'read_only': True},
'executed_amount': {'label': _('Executions')},
'periodic_display': {'label': _('Periodic perform')},
'executed_amount': {'label': _('Executed amount')},
}
def validate_name(self, name):

View File

@@ -54,13 +54,10 @@ class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializ
'ssh_key_change_strategy', 'passphrase', 'recipients', 'params'
]
extra_kwargs = {**BaseAutomationSerializer.Meta.extra_kwargs, **{
'accounts': {'required': True, 'help_text': _('Please enter your account username')},
'accounts': {'required': True},
'recipients': {'label': _('Recipient'), 'help_text': _(
"Currently only mail sending is supported"
)},
'params': {'help_text': _(
"Secret parameter settings, currently only effective for assets of the host type."
)},
}}
@property

View File

@@ -7,6 +7,7 @@ from .change_secret import (
class PushAccountAutomationSerializer(ChangeSecretAutomationSerializer):
class Meta(ChangeSecretAutomationSerializer.Meta):
model = PushAccountAutomation
fields = [

View File

@@ -6,6 +6,7 @@ from django.dispatch import receiver
from django.utils.translation import gettext_noop
from accounts.backends import vault_client
from accounts.const import Source
from audits.const import ActivityChoices
from audits.signal_handlers import create_activities
from common.decorators import merge_delay_run
@@ -32,7 +33,7 @@ def push_accounts_if_need(accounts=()):
template_accounts = defaultdict(list)
for ac in accounts:
# 再强调一次吧
if ac.source != 'template':
if ac.source != Source.TEMPLATE:
continue
template_accounts[ac.source_id].append(ac)
@@ -61,7 +62,7 @@ def create_accounts_activities(account, action='create'):
@receiver(post_save, sender=Account)
def on_account_create_by_template(sender, instance, created=False, **kwargs):
if not created or instance.source != 'template':
if not created:
return
push_accounts_if_need.delay(accounts=(instance,))
create_accounts_activities(instance, action='create')

View File

@@ -2,10 +2,11 @@ import copy
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from accounts.const import SecretType, DEFAULT_PASSWORD_RULES
from common.utils import ssh_key_gen, random_string
from common.utils import validate_ssh_private_key, parse_ssh_private_key_str
from common.utils import (
validate_ssh_private_key, parse_ssh_private_key_str, ssh_key_gen,
random_string
)
class SecretGenerator:

View File

@@ -4,4 +4,4 @@ from django.utils.translation import gettext_lazy as _
class AclsConfig(AppConfig):
name = 'acls'
verbose_name = _('App Acls')
verbose_name = _('Acls')

View File

@@ -6,5 +6,5 @@ class ActionChoices(models.TextChoices):
reject = 'reject', _('Reject')
accept = 'accept', _('Accept')
review = 'review', _('Review')
warning = 'warning', _('Warn')
notice = 'notice', _('Notify')
warning = 'warning', _('Warning')
notice = 'notice', _('Notifications')

View File

@@ -1,129 +1,75 @@
# Generated by Django 4.1.13 on 2024-05-09 03:16
# Generated by Django 3.1 on 2021-03-11 09:53
import common.db.fields
import django.core.validators
from django.db import migrations, models
import uuid
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='CommandFilterACL',
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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('assets', common.db.fields.JSONManyToManyField(default=dict, to='assets.Asset', verbose_name='Assets')),
('accounts', models.JSONField(default=list, verbose_name='Accounts')),
],
options={
'verbose_name': 'Command acl',
'ordering': ('priority', '-is_active', 'name'),
'abstract': False,
},
),
migrations.CreateModel(
name='CommandGroup',
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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('type', models.CharField(choices=[('command', 'Command'), ('regex', 'Regex')], default='command', max_length=16, verbose_name='Type')),
('content', models.TextField(help_text='One command per line', verbose_name='Content')),
('ignore_case', models.BooleanField(default=True, verbose_name='Ignore case')),
],
options={
'verbose_name': 'Command group',
},
),
migrations.CreateModel(
name='ConnectMethodACL',
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)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('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')),
('connect_methods', models.JSONField(default=list, verbose_name='Connect methods')),
],
options={
'verbose_name': 'Connect method acl',
'ordering': ('priority', '-is_active', 'name'),
'abstract': False,
},
),
migrations.CreateModel(
name='LoginACL',
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')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created 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)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('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')),
('is_active', models.BooleanField(default=True, verbose_name='Active')),
('users', common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users')),
('rules', models.JSONField(default=dict, verbose_name='Rule')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('ip_group', models.JSONField(default=list, verbose_name='Login IP')),
('action',
models.CharField(choices=[('reject', 'Reject'), ('allow', 'Allow')], default='reject', max_length=64,
verbose_name='Action')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='login_acls',
to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'verbose_name': 'Login acl',
'ordering': ('priority', '-is_active', 'name'),
'abstract': False,
},
),
migrations.CreateModel(
name='LoginAssetACL',
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')),
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created 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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('assets', common.db.fields.JSONManyToManyField(default=dict, to='assets.Asset', verbose_name='Assets')),
('accounts', models.JSONField(default=list, verbose_name='Accounts')),
('rules', models.JSONField(default=dict, verbose_name='Rule')),
('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')),
('is_active', models.BooleanField(default=True, verbose_name='Active')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('users', models.JSONField(verbose_name='User')),
('system_users', models.JSONField(verbose_name='System User')),
('assets', models.JSONField(verbose_name='Asset')),
('action',
models.CharField(choices=[('login_confirm', 'Login confirm')], default='login_confirm', max_length=64,
verbose_name='Action')),
('reviewers',
models.ManyToManyField(blank=True, related_name='review_login_asset_acls', to=settings.AUTH_USER_MODEL,
verbose_name='Reviewers')),
],
options={
'verbose_name': 'Login asset acl',
'ordering': ('priority', '-is_active', 'name'),
'abstract': False,
'unique_together': {('name', 'org_id')},
},
),
]

View File

@@ -1,54 +1,98 @@
# Generated by Django 4.1.13 on 2024-05-09 03:16
# Generated by Django 3.1.12 on 2021-09-26 02:47
import django
from django.conf import settings
from django.db import migrations, models
from django.db import migrations, models, transaction
LOGIN_CONFIRM_ZH = '登录复核'
LOGIN_CONFIRM_EN = 'Login confirm'
DEFAULT_TIME_PERIODS = [{'id': i, 'value': '00:00~00:00'} for i in range(7)]
def has_zh(name: str) -> bool:
for i in name:
if u'\u4e00' <= i <= u'\u9fff':
return True
return False
def migrate_login_confirm(apps, schema_editor):
login_acl_model = apps.get_model("acls", "LoginACL")
login_confirm_model = apps.get_model("authentication", "LoginConfirmSetting")
with transaction.atomic():
for instance in login_confirm_model.objects.filter(is_active=True):
user = instance.user
reviewers = instance.reviewers.all()
login_confirm = LOGIN_CONFIRM_ZH if has_zh(user.name) else LOGIN_CONFIRM_EN
date_created = instance.date_created.strftime('%Y-%m-%d %H:%M:%S')
if reviewers.count() == 0:
continue
data = {
'user': user,
'name': f'{user.name}-{login_confirm} ({date_created})',
'created_by': instance.created_by,
'action': 'confirm',
'rules': {'ip_group': ['*'], 'time_period': DEFAULT_TIME_PERIODS}
}
instance = login_acl_model.objects.create(**data)
instance.reviewers.set(reviewers)
def migrate_ip_group(apps, schema_editor):
login_acl_model = apps.get_model("acls", "LoginACL")
updates = list()
with transaction.atomic():
for instance in login_acl_model.objects.exclude(action='confirm'):
instance.rules = {'ip_group': instance.ip_group, 'time_period': DEFAULT_TIME_PERIODS}
updates.append(instance)
login_acl_model.objects.bulk_update(updates, ['rules', ])
class Migration(migrations.Migration):
initial = True
dependencies = [
('acls', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('acls', '0001_initial'),
('authentication', '0004_ssotoken'),
]
operations = [
migrations.AddField(
model_name='loginassetacl',
name='reviewers',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
migrations.AlterField(
model_name='loginacl',
name='action',
field=models.CharField(choices=[('reject', 'Reject'), ('allow', 'Allow'), ('confirm', 'Login confirm')],
default='reject', max_length=64, verbose_name='Action'),
),
migrations.AddField(
model_name='loginacl',
name='reviewers',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
field=models.ManyToManyField(blank=True, related_name='login_confirm_acls',
to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
),
migrations.AlterField(
model_name='loginacl',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
related_name='login_acls', to=settings.AUTH_USER_MODEL, verbose_name='User'),
),
migrations.AddField(
model_name='connectmethodacl',
name='reviewers',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
model_name='loginacl',
name='rules',
field=models.JSONField(default=dict, verbose_name='Rule'),
),
migrations.AlterUniqueTogether(
name='commandgroup',
unique_together={('org_id', 'name')},
migrations.RunPython(migrate_login_confirm),
migrations.RunPython(migrate_ip_group),
migrations.RemoveField(
model_name='loginacl',
name='ip_group',
),
migrations.AddField(
model_name='commandfilteracl',
name='command_groups',
field=models.ManyToManyField(related_name='command_filters', to='acls.commandgroup', verbose_name='Command group'),
migrations.AlterModelOptions(
name='loginacl',
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login acl'},
),
migrations.AddField(
model_name='commandfilteracl',
name='reviewers',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
),
migrations.AlterUniqueTogether(
migrations.AlterModelOptions(
name='loginassetacl',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='commandfilteracl',
unique_together={('name', 'org_id')},
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login asset acl'},
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 3.1.13 on 2021-11-30 02:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('acls', '0002_auto_20210926_1047'),
]
operations = [
migrations.AlterModelOptions(
name='loginacl',
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login acl'},
),
migrations.AlterModelOptions(
name='loginassetacl',
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login asset acl'},
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 3.2.13 on 2022-08-31 08:58
from django.db import migrations, models
def migrate_system_users_to_accounts(apps, schema_editor):
login_asset_acl_model = apps.get_model('acls', 'LoginAssetACL')
qs = login_asset_acl_model.objects.all()
login_asset_acls = []
for instance in qs:
instance.accounts = instance.system_users
login_asset_acls.append(instance)
login_asset_acl_model.objects.bulk_update(login_asset_acls, ['accounts'])
class Migration(migrations.Migration):
dependencies = [
('acls', '0003_auto_20211130_1037'),
]
operations = [
migrations.AddField(
model_name='loginassetacl',
name='accounts',
field=models.JSONField(verbose_name='Account'),
),
migrations.RunPython(migrate_system_users_to_accounts),
migrations.RemoveField(
model_name='loginassetacl',
name='system_users',
),
]

View File

@@ -0,0 +1,34 @@
# Generated by Django 3.2.14 on 2022-12-01 10:46
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('acls', '0004_auto_20220831_1658'),
]
operations = [
migrations.AlterField(
model_name='loginacl',
name='action',
field=models.CharField(default='reject', max_length=64, verbose_name='Action'),
),
migrations.AlterField(
model_name='loginacl',
name='reviewers',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
),
migrations.AlterField(
model_name='loginassetacl',
name='action',
field=models.CharField(default='reject', max_length=64, verbose_name='Action'),
),
migrations.AlterField(
model_name='loginassetacl',
name='reviewers',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
),
]

View File

@@ -0,0 +1,70 @@
# Generated by Django 3.2.14 on 2022-12-01 11:39
import uuid
import django.core.validators
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('acls', '0005_auto_20221201_1846'),
]
operations = [
migrations.CreateModel(
name='CommandGroup',
fields=[
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('updated_by', models.CharField(blank=True, max_length=32, 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')),
('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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('type', models.CharField(choices=[('command', 'Command'), ('regex', 'Regex')], default='command',
max_length=16, verbose_name='Type')),
('content', models.TextField(help_text='One line one command', verbose_name='Content')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('ignore_case', models.BooleanField(default=True, verbose_name='Ignore case')),
],
options={
'verbose_name': 'Command filter rule',
'unique_together': {('org_id', 'name')},
},
),
migrations.CreateModel(
name='CommandFilterACL',
fields=[
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created 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')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('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')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('users', models.JSONField(verbose_name='User')),
('accounts', models.JSONField(verbose_name='Account')),
('assets', models.JSONField(verbose_name='Asset')),
('commands', models.ManyToManyField(to='acls.CommandGroup', verbose_name='Commands')),
(
'reviewers',
models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')),
],
options={
'verbose_name': 'Command acl',
'ordering': ('priority', '-is_active', 'name'),
'unique_together': {('name', 'org_id')},
},
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 3.2.14 on 2022-12-02 02:48
from django.db import migrations
def migrate_login_type(apps, schema_editor):
login_asset_model = apps.get_model('acls', 'LoginAssetACL')
login_asset_model.objects.filter(action='login_confirm').update(action='review')
login_system_model = apps.get_model('acls', 'LoginACL')
login_system_model.objects.filter(action='confirm').update(action='review')
class Migration(migrations.Migration):
dependencies = [
('acls', '0006_commandfilteracl_commandgroup'),
]
operations = [
migrations.RunPython(migrate_login_type),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 3.2.14 on 2022-12-02 04:25
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('acls', '0007_auto_20221202_1048'),
]
operations = [
migrations.AlterModelOptions(
name='commandgroup',
options={'verbose_name': 'Command group'},
),
migrations.RenameField(
model_name='commandfilteracl',
old_name='commands',
new_name='command_groups',
),
migrations.AlterModelOptions(
name='commandfilteracl',
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Command acl'},
),
migrations.AlterModelOptions(
name='loginacl',
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login acl'},
),
migrations.AlterModelOptions(
name='loginassetacl',
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login asset acl'},
),
]

View File

@@ -0,0 +1,53 @@
# Generated by Django 3.2.14 on 2022-12-20 11:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('acls', '0008_commandgroup_comment'),
]
operations = [
migrations.AddField(
model_name='commandfilteracl',
name='updated_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
),
migrations.AddField(
model_name='loginacl',
name='updated_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
),
migrations.AddField(
model_name='loginassetacl',
name='updated_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
),
migrations.AlterField(
model_name='commandfilteracl',
name='created_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='commandgroup',
name='created_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='commandgroup',
name='updated_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
),
migrations.AlterField(
model_name='loginacl',
name='created_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='loginassetacl',
name='created_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-01-10 06:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('acls', '0009_auto_20221220_1956'),
]
operations = [
migrations.AlterField(
model_name='commandfilteracl',
name='command_groups',
field=models.ManyToManyField(to='acls.CommandGroup', verbose_name='Command group'),
),
]

View File

@@ -0,0 +1,44 @@
# Generated by Django 3.2.17 on 2023-04-25 09:04
from django.db import migrations, models
import common.db.fields
class Migration(migrations.Migration):
dependencies = [
('acls', '0010_alter_commandfilteracl_command_groups'),
]
operations = [
migrations.AddField(
model_name='commandfilteracl',
name='new_accounts',
field=models.JSONField(default=list, verbose_name='Accounts'),
),
migrations.AddField(
model_name='commandfilteracl',
name='new_assets',
field=common.db.fields.JSONManyToManyField(default=dict, to='assets.Asset', verbose_name='Assets'),
),
migrations.AddField(
model_name='commandfilteracl',
name='new_users',
field=common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users'),
),
migrations.AddField(
model_name='loginassetacl',
name='new_accounts',
field=models.JSONField(default=list, verbose_name='Accounts')
),
migrations.AddField(
model_name='loginassetacl',
name='new_assets',
field=common.db.fields.JSONManyToManyField(default=dict, to='assets.Asset', verbose_name='Assets'),
),
migrations.AddField(
model_name='loginassetacl',
name='new_users',
field=common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users'),
),
]

View File

@@ -0,0 +1,41 @@
# Generated by Django 3.2.17 on 2023-04-26 03:11
from django.db import migrations
def migrate_base_acl_users_assets_accounts(apps, *args):
cmd_acl_model = apps.get_model('acls', 'CommandFilterACL')
login_asset_acl_model = apps.get_model('acls', 'LoginAssetACL')
for model in [cmd_acl_model, login_asset_acl_model]:
for obj in model.objects.all():
user_names = (obj.users or {}).get('username_group', [])
obj.new_users = {
"type": "attrs",
"attrs": [{"name": "username", "value": user_names, "match": "in"}]
}
asset_names = (obj.assets or {}).get('name_group', [])
asset_attrs = []
if asset_names:
asset_attrs.append({"name": "name", "value": asset_names, "match": "in"})
asset_address = (obj.assets or {}).get('address_group', [])
if asset_address:
asset_attrs.append({"name": "address", "value": asset_address, "match": "ip_in"})
obj.new_assets = {"type": "attrs", "attrs": asset_attrs}
account_usernames = (obj.accounts or {}).get('username_group', [])
if '*' in account_usernames:
account_usernames = ['@ALL']
obj.new_accounts = account_usernames
obj.save()
class Migration(migrations.Migration):
dependencies = [
('acls', '0011_auto_20230425_1704'),
]
operations = [
migrations.RunPython(migrate_base_acl_users_assets_accounts)
]

View File

@@ -0,0 +1,66 @@
# Generated by Django 3.2.17 on 2023-04-26 09:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('acls', '0012_auto_20230426_1111'),
]
operations = [
migrations.RemoveField(
model_name='commandfilteracl',
name='accounts',
),
migrations.RemoveField(
model_name='commandfilteracl',
name='assets',
),
migrations.RemoveField(
model_name='commandfilteracl',
name='users',
),
migrations.RemoveField(
model_name='loginassetacl',
name='accounts',
),
migrations.RemoveField(
model_name='loginassetacl',
name='assets',
),
migrations.RemoveField(
model_name='loginassetacl',
name='users',
),
migrations.RenameField(
model_name='commandfilteracl',
old_name='new_accounts',
new_name='accounts',
),
migrations.RenameField(
model_name='commandfilteracl',
old_name='new_assets',
new_name='assets',
),
migrations.RenameField(
model_name='commandfilteracl',
old_name='new_users',
new_name='users',
),
migrations.RenameField(
model_name='loginassetacl',
old_name='new_accounts',
new_name='accounts',
),
migrations.RenameField(
model_name='loginassetacl',
old_name='new_assets',
new_name='assets',
),
migrations.RenameField(
model_name='loginassetacl',
old_name='new_users',
new_name='users',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2.17 on 2023-05-26 09:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('acls', '0013_auto_20230426_1759'),
]
operations = [
migrations.AddField(
model_name='loginassetacl',
name='rules',
field=models.JSONField(default=dict, verbose_name='Rule'),
),
]

View File

@@ -0,0 +1,46 @@
# Generated by Django 3.2.17 on 2023-06-06 06:23
import uuid
import django.core.validators
from django.conf import settings
from django.db import migrations, models
import common.db.fields
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('acls', '0014_loginassetacl_rules'),
]
operations = [
migrations.CreateModel(
name='ConnectMethodACL',
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)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('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')),
('connect_methods', models.JSONField(default=list, verbose_name='Connect methods')),
(
'reviewers',
models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')),
],
options={
'ordering': ('priority', '-is_active', 'name'),
'abstract': False,
},
),
]

View File

@@ -0,0 +1,47 @@
# Generated by Django 3.2.17 on 2023-06-06 10:57
from django.db import migrations, models
import common.db.fields
def migrate_users_login_acls(apps, schema_editor):
login_acl_model = apps.get_model('acls', 'LoginACL')
name_used = []
login_acls = []
for login_acl in login_acl_model.objects.all().select_related('user'):
name = '{}_{}'.format(login_acl.name, login_acl.user.username)
if name.lower() in name_used:
name += '_{}'.format(str(login_acl.user_id)[:4])
name_used.append(name.lower())
login_acl.name = name
login_acl.users = {
"type": "ids", "ids": [str(login_acl.user_id)]
}
login_acls.append(login_acl)
login_acl_model.objects.bulk_update(login_acls, ['name', 'users'])
class Migration(migrations.Migration):
dependencies = [
('acls', '0015_connectmethodacl'),
]
operations = [
migrations.AddField(
model_name='loginacl',
name='users',
field=common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users'),
),
migrations.RunPython(migrate_users_login_acls),
migrations.RemoveField(
model_name='loginacl',
name='user',
),
migrations.AlterField(
model_name='loginacl',
name='name',
field=models.CharField(max_length=128, unique=True, verbose_name='Name'),
),
]

View File

@@ -0,0 +1,16 @@
# Generated by Django 3.2.19 on 2023-06-13 07:49
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('acls', '0016_auto_20230606_1857'),
]
operations = [
migrations.AlterModelOptions(
name='connectmethodacl',
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Connect method acl'},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1.10 on 2023-10-18 10:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('acls', '0017_alter_connectmethodacl_options'),
]
operations = [
migrations.AlterField(
model_name='commandfilteracl',
name='command_groups',
field=models.ManyToManyField(related_name='command_filters', to='acls.commandgroup', verbose_name='Command group'),
),
]

View File

@@ -23,7 +23,7 @@ class CommandGroup(JMSOrgBaseModel):
max_length=16, default=TypeChoices.command, choices=TypeChoices.choices,
verbose_name=_("Type")
)
content = models.TextField(verbose_name=_("Content"), help_text=_("One command per line"))
content = models.TextField(verbose_name=_("Content"), help_text=_("One line one command"))
ignore_case = models.BooleanField(default=True, verbose_name=_('Ignore case'))
TypeChoices = TypeChoices

View File

@@ -39,7 +39,7 @@ class UserLoginReminderMsg(UserMessage):
class AssetLoginReminderMsg(UserMessage):
subject = _('User login alert for asset')
subject = _('Asset login reminder')
def __init__(self, user, asset: Asset, login_user: User, account: Account, input_username):
self.asset = asset

View File

@@ -1,5 +1,7 @@
# coding: utf-8
#
from urllib.parse import urlparse
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
@@ -8,7 +10,7 @@ from common.utils.ip import is_ip_address, is_ip_network, is_ip_segment
logger = get_logger(__file__)
__all__ = ['RuleSerializer', 'ip_group_child_validator', 'ip_group_help_text']
__all__ = ['RuleSerializer', 'ip_group_child_validator', 'ip_group_help_text', 'address_validator']
def ip_group_child_validator(ip_group_child):
@@ -21,6 +23,19 @@ def ip_group_child_validator(ip_group_child):
raise serializers.ValidationError(error)
def address_validator(value):
parsed = urlparse(value)
is_basic_url = parsed.scheme in ('http', 'https') and parsed.netloc
is_valid = value == '*' \
or is_ip_address(value) \
or is_ip_network(value) \
or is_ip_segment(value) \
or is_basic_url
if not is_valid:
error = _('address invalid: `{}`').format(value)
raise serializers.ValidationError(error)
ip_group_help_text = _(
'With * indicating a match all. '
'Such as: '

View File

@@ -1,16 +1,13 @@
{% load i18n %}
<h3>{% trans 'Dear' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
<h3>{% trans 'Respectful' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
<hr>
<p>{% trans 'We would like to inform you that a user has recently logged into the following asset:' %}<p>
<p><strong>{% trans 'Asset details' %}:</strong></p>
<ul>
<li><strong>{% trans 'User' %}:</strong> [{{ name }}({{ username }})]</li>
<li><strong>{% trans 'Assets' %}:</strong> [{{ asset }}]</li>
<li><strong>{% trans 'Account' %}:</strong> [{{ account_name }}({{ account }})]</li>
</ul>
<p><strong>{% trans 'User' %}:</strong> [{{ name }}({{ username }})]</p>
<p><strong>{% trans 'Assets' %}:</strong> [{{ asset }}]</p>
<p><strong>{% trans 'Account' %}:</strong> [{{ account_name }}({{ account }})]</p>
<hr>
<p>{% trans 'Please review the login activity to ensure the security and proper usage of the asset. If you did not authorize this login or if you notice any suspicious activity, please take the necessary actions immediately.' %}</p>
<p>{% trans 'The user has just logged in to the asset. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}</p>
<p>{% trans 'Thank you' %}</p>
<p>{% trans 'Thank you for your attention to this matter' %}</p>

View File

@@ -1,16 +1,14 @@
{% load i18n %}
<h3>{% trans 'Dear' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
<h3>{% trans 'Respectful' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
<hr>
<p>{% trans 'We would like to inform you that a user has recently logged:' %}<p>
<p><strong>{% trans 'User details' %}:</strong></p>
<ul>
<li><strong>{% trans 'User' %}:</strong> [{{ username }}]</li>
<li><strong>IP:</strong> [{{ ip }}]</li>
<li><strong>{% trans 'Login city' %}:</strong> [{{ city }}]</li>
<li><strong>{% trans 'User agent' %}:</strong> [{{ user_agent }}]</li>
</ul>
<p><strong>{% trans 'User' %}:</strong> [{{ username }}]</p>
<p><strong>IP:</strong> [{{ ip }}]</p>
<p><strong>{% trans 'Login city' %}:</strong> [{{ city }}]</p>
<p><strong>{% trans 'User agent' %}:</strong> [{{ user_agent }}]</p>
<hr>
<p>{% trans 'Please review the login activity to ensure the security and proper usage of the asset. If you did not authorize this login or if you notice any suspicious activity, please take the necessary actions immediately.' %}</p>
<p>{% trans 'Thank you for your attention to this matter' %}</p>
<p>{% trans 'The user has just successfully logged into the system. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}</p>
<p>{% trans 'Thank you' %}</p>

View File

12
apps/applications/apps.py Normal file
View File

@@ -0,0 +1,12 @@
from __future__ import unicode_literals
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ApplicationsConfig(AppConfig):
name = 'applications'
verbose_name = _('Applications')
def ready(self):
super().ready()

View File

@@ -0,0 +1,42 @@
# Generated by Django 2.1.7 on 2019-05-20 11:04
import common.db.fields
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('assets', '0026_auto_20190325_2035'),
]
operations = [
migrations.CreateModel(
name='RemoteApp',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('type', models.CharField(choices=[('Browser', (('chrome', 'Chrome'),)), ('Database tools', (('mysql_workbench', 'MySQL Workbench'),)), ('Virtualization tools', (('vmware_client', 'vSphere Client'),)), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type')),
('path', models.CharField(max_length=128, verbose_name='App path')),
('params', common.db.fields.EncryptJsonDictTextField(blank=True, default={}, max_length=4096, null=True, verbose_name='Parameters')),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset', verbose_name='Asset')),
('system_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.SystemUser', verbose_name='System user')),
],
options={
'verbose_name': 'RemoteApp',
'ordering': ('name',),
},
),
migrations.AlterUniqueTogether(
name='remoteapp',
unique_together={('org_id', 'name')},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-09-09 09:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('applications', '0001_initial'),
('perms', '0009_remoteapppermission_system_users'),
]
operations = [
migrations.RemoveField(
model_name='remoteapp',
name='system_user',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.1.11 on 2019-12-10 08:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('applications', '0002_remove_remoteapp_system_user'),
]
operations = [
migrations.AlterField(
model_name='remoteapp',
name='type',
field=models.CharField(choices=[('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type'),
),
]

Some files were not shown because too many files have changed in this diff Show More