mirror of https://github.com/jumpserver/jumpserver
Merge v4 to dev
commit
dfde50c768
|
@ -3,6 +3,7 @@ on:
|
|||
push:
|
||||
paths:
|
||||
- 'Dockerfile'
|
||||
- 'Dockerfile*'
|
||||
- 'Dockerfile-*'
|
||||
- 'pyproject.toml'
|
||||
- 'poetry.lock'
|
||||
|
@ -10,46 +11,53 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
component: [core]
|
||||
version: [v4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Check Dockerfile
|
||||
- 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
|
||||
run: |
|
||||
test -f Dockerfile-ce || cp -f Dockerfile Dockerfile-ce
|
||||
sed -i 's@^FROM registry.fit2cloud.com/jumpserver@FROM ghcr.io/jumpserver@g' Dockerfile-ee
|
||||
|
||||
- name: Build CE Image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
file: Dockerfile-ce
|
||||
tags: jumpserver/core-ce:test
|
||||
push: true
|
||||
file: Dockerfile
|
||||
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}-ce
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
VERSION=${{ matrix.version }}
|
||||
APT_MIRROR=http://deb.debian.org
|
||||
PIP_MIRROR=https://pypi.org/simple
|
||||
PIP_JMS_MIRROR=https://pypi.org/simple
|
||||
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
|
||||
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: jumpserver/core-ee:test
|
||||
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
VERSION=${{ matrix.version }}
|
||||
APT_MIRROR=http://deb.debian.org
|
||||
PIP_MIRROR=https://pypi.org/simple
|
||||
PIP_JMS_MIRROR=https://pypi.org/simple
|
||||
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
|
@ -44,3 +44,4 @@ data/*
|
|||
test.py
|
||||
.history/
|
||||
.test/
|
||||
*.mo
|
||||
|
|
|
@ -1,13 +1,49 @@
|
|||
FROM python:3.11-slim-bullseye as stage-1
|
||||
FROM debian:bullseye-slim 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 \
|
||||
&& cd utils && bash -ixeu build.sh
|
||||
&& \
|
||||
if [ -n "${VERSION}" ]; then \
|
||||
sed -i "s@VERSION = .*@VERSION = '${VERSION}'@g" apps/jumpserver/const.py; \
|
||||
fi
|
||||
|
||||
FROM python:3.11-slim-bullseye as stage-2
|
||||
ARG TARGETARCH
|
||||
|
@ -18,90 +54,68 @@ ARG BUILD_DEPENDENCIES=" \
|
|||
pkg-config"
|
||||
|
||||
ARG DEPENDENCIES=" \
|
||||
default-libmysqlclient-dev \
|
||||
freetds-dev \
|
||||
libffi-dev \
|
||||
libjpeg-dev \
|
||||
gettext \
|
||||
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"
|
||||
libsasl2-dev"
|
||||
|
||||
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 \
|
||||
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 \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& 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} \
|
||||
&& 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 \
|
||||
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 \
|
||||
set -ex \
|
||||
&& python3 -m venv /opt/py3 \
|
||||
&& pip install poetry -i ${PIP_MIRROR} \
|
||||
&& poetry config virtualenvs.create false \
|
||||
&& . /opt/py3/bin/activate \
|
||||
&& poetry install
|
||||
&& 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
|
||||
|
||||
FROM python:3.11-slim-bullseye
|
||||
ARG TARGETARCH
|
||||
ENV LANG=zh_CN.UTF-8 \
|
||||
ENV LANG=en_US.UTF-8 \
|
||||
PATH=/opt/py3/bin:$PATH
|
||||
|
||||
ARG DEPENDENCIES=" \
|
||||
libjpeg-dev \
|
||||
libpq-dev \
|
||||
libx11-dev \
|
||||
freerdp2-dev \
|
||||
libxmlsec1-openssl"
|
||||
libldap2-dev \
|
||||
libx11-dev"
|
||||
|
||||
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"
|
||||
sshpass"
|
||||
|
||||
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 \
|
||||
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 \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& apt-get update \
|
||||
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
|
||||
|
@ -109,21 +123,12 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \
|
|||
&& 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/
|
||||
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/
|
||||
|
||||
WORKDIR /opt/jumpserver
|
||||
|
||||
|
@ -132,6 +137,10 @@ ENV VERSION=$VERSION
|
|||
|
||||
VOLUME /opt/jumpserver/data
|
||||
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
STOPSIGNAL SIGQUIT
|
||||
|
||||
CMD ["start", "all"]
|
|
@ -1,5 +1,52 @@
|
|||
ARG VERSION
|
||||
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} as build-xpack
|
||||
FROM registry.fit2cloud.com/jumpserver/core-ce:${VERSION}
|
||||
|
||||
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack
|
||||
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
|
||||
|
|
282
README.md
282
README.md
|
@ -1,125 +1,227 @@
|
|||
<p align="center">
|
||||
<div align="center"><a name="readme-top"></a>
|
||||
<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>
|
||||
|
||||
### Popular Open-Source Bastion Host
|
||||
|
||||
<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>
|
||||
<!-- SHIELD GROUP-->
|
||||
[![][license-shield]][license-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]
|
||||
|
||||
For 9 years, pouring heart and soul into creating a high-quality open-source bastion host. <br/>
|
||||
|
||||
<!-- [![][github-trending-shield]][github-trending-link] -->
|
||||
|
||||
</div>
|
||||
|
||||
#
|
||||
![][image-dashboard]
|
||||
_[To-do]: Need to design the graphics._
|
||||
|
||||
|
||||
<p align="center">
|
||||
9 年时间,倾情投入,用心做好一款开源堡垒机。
|
||||
</p>
|
||||
## Table of contents
|
||||
|
||||
------------------------------
|
||||
JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。
|
||||
- [Getting Started](#getting-started)
|
||||
- [Introduction](#introduction)
|
||||
- [Why JumpServer](#why-jumpserver)
|
||||
- [Installation](#installation)
|
||||
- [Product Architecture & Components](#product-architecture--components)
|
||||
- [Features](#features)
|
||||
- [Acknowledgements](#acknowledgements)
|
||||
- [Contributing](#contributing)
|
||||
- [Security](#security)
|
||||
- [License](#license)
|
||||
|
||||
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 连接各类应用。
|
||||
<br/>
|
||||
|
||||
## 产品特色
|
||||
</details>
|
||||
|
||||
- **开源**: 零门槛,线上快速获取和安装;
|
||||
- **无插件**: 仅需浏览器,极致的 Web Terminal 使用体验;
|
||||
- **分布式**: 支持分布式部署和横向扩展,轻松支持大规模并发访问;
|
||||
- **多云支持**: 一套系统,同时管理不同云上面的资产;
|
||||
- **多租户**: 一套系统,多个子公司或部门同时使用;
|
||||
- **云端存储**: 审计录像云端存储,永不丢失;
|
||||
## Getting Started
|
||||
|
||||
## UI 展示
|
||||
Step right into our online demonstration environment, where you can effortlessly experience our product without the need for time-consuming software installations. With just a few clicks, you'll quickly grasp the functionality and features of our product. In the demonstration environment, you can explore the various features of our product to your heart's content and experience our innovative design and exceptional performance.
|
||||
|
||||

|
||||
Whether you're new to the experience or a seasoned expert, we invite you to join our Discord community right away! Here, our developers and enthusiastic users come together to offer support and assistance. No matter what challenges you encounter during your usage, we are committed to answering your questions and providing guidance.
|
||||
|
||||
## 在线体验
|
||||
| [![][demo-shield-badge]][demo-link] | No installation or registration necessary! Visit our website to experience it firsthand. |
|
||||
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
|
||||
| [![][discord-shield-badge]][discord-link] | Join our Discord community! This is where you can connect with developers and other enthusiastic users of JumpServer. |
|
||||
|
||||
- 环境地址:<https://demo.jumpserver.org/>
|
||||
> \[!IMPORTANT]
|
||||
>
|
||||
> **Star Us**, You will receive all release notifications from GitHub without any delay \~ ⭐️
|
||||
|
||||
| :warning: 注意 |
|
||||
|:-----------------------------|
|
||||
| 该环境仅作体验目的使用,我们会定时清理、重置数据! |
|
||||
| 请勿修改体验环境用户的密码! |
|
||||
| 请勿在环境中添加业务生产环境地址、用户名密码等敏感信息! |
|
||||
![][image-star]
|
||||
|
||||
## 快速开始
|
||||
<details>
|
||||
<summary><kbd>Star History</kbd></summary>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=jumpserver%2Fjumpserver&theme=dark&type=Date">
|
||||
<img width="100%" src="https://api.star-history.com/svg?repos=jumpserver%2Fjumpserver&type=Date">
|
||||
</picture>
|
||||
</details>
|
||||
|
||||
- [快速入门](https://docs.jumpserver.org/zh/v3/quick_start/)
|
||||
- [产品文档](https://docs.jumpserver.org)
|
||||
- [在线学习](https://edu.fit2cloud.com/page/2635362)
|
||||
- [知识库](https://kb.fit2cloud.com/categories/jumpserver)
|
||||
> \[!TIP]
|
||||
>
|
||||
> This is a demonstration video that can quickly help you understand the page design and product features of JumpServer.
|
||||
|
||||
## 案例研究
|
||||
<video controls src="https://github.com/jumpserver/jumpserver/assets/32935519/6f984266-24a1-4d1f-9745-4a8e0122f49c" muted="false"></video>
|
||||
_[To-do]: Need to design the video._
|
||||
|
||||
- [腾讯音乐娱乐集团:基于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)
|
||||
## Introduction
|
||||
|
||||
## 社区交流
|
||||
JumpServer is a widely acclaimed open-source bastion host, serving as a professional operational security auditing system compliant with the 4A standards. It helps businesses securely manage and access all types of assets in a more secure manner, enabling pre-authorization, real-time monitoring, and post-audit capabilities.
|
||||
|
||||
如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。
|
||||
JumpServer aims to become the industry's preferred platform, assisting businesses in securely and efficiently managing and accessing all types of assets. By offering a professional operational security auditing system compliant with 4A standards, JumpServer is committed to delivering advanced asset management and access solutions, meeting enterprises' needs for security, reliability, and efficiency.
|
||||
|
||||
您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 当中进行交流沟通。
|
||||
JumpServer's vision is to become a leader in the enterprise-level asset management and access control field, providing comprehensive solutions for users to securely and efficiently manage and utilize their assets. Through continuous innovation and enhancement of product features, JumpServer is committed to driving the development of the entire industry and becoming a key supporter and promoter of enterprise digital transformation.
|
||||
|
||||
### 参与贡献
|
||||
![][image-supported-asset-type]
|
||||
_[To-do]: Need to design the graphics._
|
||||
|
||||
欢迎提交 PR 参与贡献。 参考 [CONTRIBUTING.md](https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md)
|
||||
## Why JumpServer
|
||||
1. **Open Source**: JumpServer is an open-source software, meaning users can freely access, use, and modify its source code to meet individual needs, while also benefiting from community support and collaboration.
|
||||
2. **Plugin-Free**: JumpServer provides comprehensive functionality without the need for additional plugins or extensions. This simplifies deployment and management processes, reducing potential compatibility and security risks.
|
||||
3. **Distributed**: JumpServer supports a distributed architecture, allowing easy scaling across multiple nodes for high availability and fault tolerance. This makes it suitable for large-scale deployments and complex network environments.
|
||||
4. **Multi-Cloud**: JumpServer offers support for various cloud platforms, including AWS, Azure, Google Cloud, etc., enabling users to manage and access assets seamlessly across different cloud environments.
|
||||
5. **Cloud Storage**: JumpServer supports storing critical data such as audit logs and configuration files in the cloud, ensuring data security and reliability, as well as facilitating cross-region and cross-device access.
|
||||
6. **Organizational**: JumpServer provides a flexible organizational structure, supporting multi-level organizational hierarchies and permission management. This allows administrators to finely control user access permissions, ensuring asset security and compliance.
|
||||
|
||||
## 组件项目
|
||||
## Installation
|
||||
|
||||
| 项目 | 状态 | 描述 |
|
||||
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
|
||||
| [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 supports multiple installation methods to cater to diverse user scenarios and preferences:
|
||||
|
||||
## 安全说明
|
||||
See Docs: https://docs.jumpserver.org/zh/v3/
|
||||
|
||||
JumpServer是一款安全产品,请参考 [基本安全建议](https://docs.jumpserver.org/zh/master/install/install_security/)
|
||||
进行安装部署。如果您发现安全相关问题,请直接联系我们:
|
||||
#### 1. Online
|
||||
Ideal for users with internet access, this method involves downloading installation scripts or packages directly from the internet. It ensures easy access to the latest updates and dependencies during installation.
|
||||
|
||||
- 邮箱:support@fit2cloud.com
|
||||
- 电话:400-052-0755
|
||||
Quick installation of JumpServer in just two steps:
|
||||
|
||||
## License & Copyright
|
||||
1. Prepare a 64-bit Linux host with at least 4 cores and 8 GB of RAM, which has internet access.
|
||||
2. Execute the following command as the root user for one-click installation of JumpServer.
|
||||
|
||||
```sh
|
||||
curl -sSL https://github.com/jumpserver/jumpserver/releases/latest/download/quick_start.sh | bash
|
||||
```
|
||||
|
||||
#### 2. Offline
|
||||
Suited for environments without internet connectivity, this method allows users to download all necessary installation files and dependencies beforehand. It ensures seamless installation even in isolated or restricted network environments.
|
||||
|
||||
Download offline package: https://community.fit2cloud.com/#/products/jumpserver/downloads
|
||||
|
||||
#### 3. Kubernetes (K8s)
|
||||
JumpServer supports installation on Kubernetes clusters. You can deploy JumpServer as containerized applications on Kubernetes, leveraging the scalability and management features of Kubernetes for running JumpServer.
|
||||
|
||||
#### 4. All-in-One
|
||||
This method provides a simplified installation process where all components of JumpServer are installed on a single server or machine. It's suitable for small-scale deployments or testing purposes where separate component deployment is not required.
|
||||
|
||||
#### 5. Enterprise Edition Trial
|
||||
JumpServer offers a trial version of its enterprise edition, allowing users to test out the enterprise features and functionalities before committing to a full deployment. This trial version typically comes with limited duration or features to provide a glimpse of the capabilities of the enterprise edition.
|
||||
|
||||
Each installation method caters to different use cases and deployment scenarios, offering flexibility and options for users based on their requirements and infrastructure setup.
|
||||
|
||||
Applying for the Enterprise Edition: https://jumpserver.org/enterprise.html
|
||||
|
||||
## Product Architecture & Components
|
||||
#### 1. Architecture Diagram
|
||||
Below is the schematic diagram of the JumpServer system architecture, providing a more comprehensive understanding of the product features of JumpServer.
|
||||
![][image-system-architecture]
|
||||
_[To-do]: Need to design the graphics._
|
||||
|
||||
#### 2. Supporting Components
|
||||
| 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-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 Graphical Protocol Connector, dependent on [Apache Guacamole](https://guacamole.apache.org/) |
|
||||
| [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-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 Database Proxy 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 |
|
||||
| [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 Assets Connector |
|
||||
| [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 Inter-Project Communication Component with 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 Client |
|
||||
| [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 Installation Tool |
|
||||
|
||||
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.
|
||||
|
||||
## Features
|
||||
Below are the features supported by JumpServer, covering various aspects of operations management and security control, providing you with a comprehensive solution.
|
||||
|
||||
1. User Authentication Supporting Integration with Multiple Single Sign-On Systems (SSO)
|
||||
2. User Management Based on Role-based Access Control (RBAC)
|
||||
3. Asset Management of Everything is an Asset
|
||||
4. Asset Account Management
|
||||
5. Asset Authorization Management
|
||||
6. Asset Permission Management Based Access Control Logic (ACL)
|
||||
7. Remote Application Management for Everything
|
||||
8. Support for Multiple Asset Connection Methods
|
||||
9. Comprehensive and Detailed User Behavior Audit System
|
||||
10. Organization Management with Resource Isolation [![][version-ee-shield-badge]][official-website-en-link]
|
||||
11. Ticket Management [![][version-ee-shield-badge]][official-website-en-link]
|
||||
|
||||
For details, [See Docs][docs-link].
|
||||
|
||||
## Acknowledgements
|
||||
- [Icons8](https://icons8.com/) is an excellent platform that offers free icon resources, providing a diverse selection of icons for projects. We are grateful to Icons8 for providing us with such high-quality resources!
|
||||
|
||||
## Contributing
|
||||
Welcome to submit PR to contribute. Please refer to [CONTRIBUTING.md][contributing-link] for guidelines.
|
||||
|
||||
## Security
|
||||
JumpServer is a secure 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
|
||||
- Phone: 400-052-0755
|
||||
|
||||
## License
|
||||
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.
|
||||
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 -->
|
||||
[official-website-en-link]: https://jumpserver.org/
|
||||
[docs-link]: https://docs.jumpserver.org/
|
||||
[community-link]: https://community.fit2cloud.com/#/products/jumpserver/downloads
|
||||
[demo-link]: https://demo.jumpserver.org/
|
||||
[discord-link]: https://discord.gg/DVz6Hckx
|
||||
[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-shield]: https://img.shields.io/github/v/release/jumpserver/jumpserver
|
||||
[github-release-link]: https://github.com/jumpserver/jumpserver/releases/latest
|
||||
[github-stars-shield]: https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square
|
||||
[github-stars-link]: https://github.com/jumpserver/jumpserver
|
||||
[github-issues-link]: https://github.com/jumpserver/jumpserver/issues
|
||||
[github-trending-link]: https://trendshift.io/repositories/5071
|
||||
|
||||
<!-- Shield link-->
|
||||
[docker-shield]: https://img.shields.io/docker/pulls/jumpserver/jms_all.svg
|
||||
[license-shield]: https://img.shields.io/github/license/jumpserver/jumpserver
|
||||
[demo-shield-badge]: https://img.shields.io/badge/ONLINE-online?style=plastic&logo=jameson&logoColor=white&label=TRY%20JUMPSERVER&labelColor=black&color=%23148f76
|
||||
[discord-shield-badge]: https://img.shields.io/badge/JOIN_US_NOW-ONLINE?style=plastic&logo=discord&logoColor=white&label=DISCORD&labelColor=black&color=%23404eed
|
||||
[version-ee-shield-badge]: https://img.shields.io/badge/Enterprise-black?style=flat-square&logo=vagrant
|
||||
[github-trending-shield]: https://trendshift.io/api/badge/repositories/5071
|
||||
|
||||
<!-- Image link -->
|
||||
[image-jumpserver]: https://download.jumpserver.org/images/jumpserver-logo.svg
|
||||
[image-dashboard]: https://github.com/jumpserver/jumpserver/assets/32935519/014c2230-82d3-4b53-b907-8149ce44bbd0
|
||||
[image-star]: https://github.com/jumpserver/jumpserver/assets/32935519/76158e65-783d-4f11-81cd-45556a388e63
|
||||
[image-supported-asset-type]: https://github.com/jumpserver/jumpserver/assets/32935519/8e769007-5449-4e86-b34b-d04e8e484257
|
||||
[image-system-architecture]: https://github.com/jumpserver/jumpserver/assets/32935519/8a720b4e-19ed-4e3c-a8aa-325d7581005a
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ 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
|
||||
|
|
|
@ -98,9 +98,11 @@ class ChangeSecretManager(AccountBasePlaybookManager):
|
|||
|
||||
accounts = self.get_accounts(account)
|
||||
if not accounts:
|
||||
print('没有发现待处理的账号: %s 用户ID: %s 类型: %s' % (
|
||||
asset.name, self.account_ids, self.secret_type
|
||||
))
|
||||
print(
|
||||
_("No pending accounts found: {name} User ID: {account_ids} Type: {secret_type}").format(
|
||||
name=asset.name,
|
||||
account_ids=self.account_ids,
|
||||
secret_type=self.secret_type))
|
||||
return []
|
||||
|
||||
records = []
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
path: "{{ user_home_dir.stdout }}"
|
||||
register: home_dir
|
||||
when: user_home_dir.stdout != ""
|
||||
ignore_errors: yes
|
||||
|
||||
- name: "Rename user home directory if it exists"
|
||||
ansible.builtin.command:
|
||||
cmd: "mv {{ user_home_dir.stdout }} {{ user_home_dir.stdout }}.bak"
|
||||
when: home_dir.stat | default(false) and user_home_dir.stdout != ""
|
||||
ignore_errors: yes
|
||||
|
||||
- name: "Remove account"
|
||||
ansible.builtin.user:
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
# Generated by Django 3.2.14 on 2022-12-28 07:29
|
||||
# Generated by Django 4.1.13 on 2024-05-09 03:16
|
||||
|
||||
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
|
||||
|
@ -12,11 +10,10 @@ import common.db.fields
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('assets', '0098_auto_20220430_2126'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -29,63 +26,65 @@ 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', 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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('source_id', models.CharField(blank=True, max_length=128, null=True, verbose_name='Source ID')),
|
||||
],
|
||||
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')],
|
||||
'unique_together': {('username', 'asset', 'secret_type'), ('name', 'asset')},
|
||||
'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='HistoricalAccount',
|
||||
name='AccountBackupAutomation',
|
||||
fields=[
|
||||
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
|
||||
('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_user',
|
||||
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+',
|
||||
to=settings.AUTH_USER_MODEL)),
|
||||
('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': 'historical Account',
|
||||
'verbose_name_plural': 'historical Accounts',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
'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',),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AccountTemplate',
|
||||
|
@ -96,23 +95,95 @@ 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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('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'),
|
||||
('change_accounttemplatesecret', 'Can change asset account template secret')],
|
||||
'unique_together': {('name', 'org_id')},
|
||||
'permissions': [('view_accounttemplatesecret', 'Can view asset account template secret'), ('change_accounttemplatesecret', 'Can change 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'],
|
||||
},
|
||||
),
|
||||
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')),
|
||||
('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)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Account',
|
||||
'verbose_name_plural': 'historical Accounts',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,44 +1,103 @@
|
|||
# Generated by Django 3.2.14 on 2022-12-28 10:39
|
||||
# Generated by Django 4.1.13 on 2024-05-09 03:16
|
||||
|
||||
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
|
||||
from django.db import migrations, models
|
||||
|
||||
import common.db.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('assets', '0106_auto_20221228_1838'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('assets', '0001_initial'),
|
||||
('accounts', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AccountBackupAutomation',
|
||||
name='AccountBaseAutomation',
|
||||
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 backup plan',
|
||||
'ordering': ['name'],
|
||||
'unique_together': {('name', 'org_id')},
|
||||
'verbose_name': 'Account automation task',
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
)
|
||||
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')},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,198 +1,116 @@
|
|||
# Generated by Django 3.2.16 on 2022-12-30 08:08
|
||||
# Generated by Django 4.1.13 on 2024-05-09 03:16
|
||||
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
import common.db.encoder
|
||||
import common.db.fields
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('assets', '0107_automation'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('assets', '0001_initial'),
|
||||
('terminal', '0001_initial'),
|
||||
('accounts', '0002_auto_20220616_0021'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AccountBaseAutomation',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Account automation task',
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('assets.baseautomation',),
|
||||
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='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='gatheredaccount',
|
||||
name='asset',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.asset', verbose_name='Asset'),
|
||||
),
|
||||
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='account',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.account'),
|
||||
),
|
||||
|
||||
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='asset',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.asset'),
|
||||
),
|
||||
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='changesecretrecord',
|
||||
name='execution',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.automationexecution'),
|
||||
),
|
||||
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='platforms',
|
||||
field=models.ManyToManyField(blank=True, related_name='account_templates', to='assets.platform', verbose_name='Platforms'),
|
||||
),
|
||||
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='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='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='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.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_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='changesecretrecord',
|
||||
options={'ordering': ('-date_started',), 'verbose_name': 'Change secret record'},
|
||||
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')},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,17 +0,0 @@
|
|||
# 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'},
|
||||
),
|
||||
]
|
|
@ -1,38 +0,0 @@
|
|||
# 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')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# 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'},
|
||||
),
|
||||
]
|
|
@ -1,17 +0,0 @@
|
|||
# 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'},
|
||||
),
|
||||
]
|
|
@ -1,69 +0,0 @@
|
|||
# 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),
|
||||
]
|
|
@ -1,22 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,29 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,28 +0,0 @@
|
|||
# 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',
|
||||
),
|
||||
]
|
|
@ -1,77 +0,0 @@
|
|||
# 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',
|
||||
),
|
||||
|
||||
]
|
|
@ -1,31 +0,0 @@
|
|||
# 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')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,34 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,25 +0,0 @@
|
|||
# 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'},
|
||||
),
|
||||
]
|
|
@ -1,45 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,28 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -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=_('Is Password Divided'))
|
||||
is_password_divided_by_obj_storage = models.BooleanField(default=True, verbose_name=_('Is Password Divided'))
|
||||
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'))
|
||||
recipients_part_one = models.ManyToManyField(
|
||||
'users.User', related_name='recipient_part_one_plans', blank=True,
|
||||
verbose_name=_("Recipient part one")
|
||||
|
@ -37,14 +37,15 @@ 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")
|
||||
verbose_name=_("Object storage recipient part two")
|
||||
)
|
||||
zip_encrypt_password = fields.EncryptCharField(
|
||||
max_length=4096, blank=True, null=True, verbose_name=_('Zip encrypt password')
|
||||
)
|
||||
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})'
|
||||
|
|
|
@ -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 last login"))
|
||||
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date 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 last login"))
|
||||
address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address login"))
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
|
@ -41,7 +41,7 @@ class GatheredAccount(JMSOrgBaseModel):
|
|||
Account.objects.bulk_create(account_objs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Gather account automation')
|
||||
verbose_name = _("Gather asset accounts")
|
||||
unique_together = [
|
||||
('username', 'asset'),
|
||||
]
|
||||
|
@ -72,4 +72,4 @@ class GatherAccountsAutomation(AccountBaseAutomation):
|
|||
super().save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Gather asset accounts")
|
||||
verbose_name = _('Gather account automation')
|
||||
|
|
|
@ -31,7 +31,9 @@ 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'}
|
||||
decoder=None, encoder=None, required=False,
|
||||
style={'base_template': 'textarea.html'},
|
||||
label=_('Params'),
|
||||
)
|
||||
on_invalid = LabeledChoiceField(
|
||||
choices=AccountInvalidPolicy.choices, default=AccountInvalidPolicy.ERROR,
|
||||
|
|
|
@ -35,8 +35,7 @@ class AccountBackupSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSer
|
|||
]
|
||||
extra_kwargs = {
|
||||
'name': {'required': True},
|
||||
'periodic_display': {'label': _('Periodic perform')},
|
||||
'executed_amount': {'label': _('Executed amount')},
|
||||
'executed_amount': {'label': _('Executions')},
|
||||
'recipients': {
|
||||
'label': _('Recipient'),
|
||||
'help_text': _('Currently only mail sending is supported')
|
||||
|
|
|
@ -9,26 +9,34 @@ 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=_('Key password')
|
||||
allow_blank=True,
|
||||
allow_null=True,
|
||||
required=False,
|
||||
max_length=512,
|
||||
write_only=True,
|
||||
label=_("Passphrase"),
|
||||
)
|
||||
|
||||
@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
|
||||
|
@ -40,17 +48,15 @@ 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)
|
||||
|
@ -61,22 +67,34 @@ 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',
|
||||
"secret_type",
|
||||
"secret",
|
||||
"passphrase",
|
||||
"privileged",
|
||||
"is_active",
|
||||
"spec_info",
|
||||
]
|
||||
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 = [
|
||||
'date_verified', 'created_by', 'date_created',
|
||||
"spec_info",
|
||||
"date_verified",
|
||||
"created_by",
|
||||
"date_created",
|
||||
]
|
||||
extra_kwargs = {
|
||||
'username': {'help_text': _(
|
||||
"Tip: If no username is required for authentication, fill in `null`, "
|
||||
"If AD account, like `username@domain`"
|
||||
)},
|
||||
"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."
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ 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',
|
||||
|
@ -34,8 +35,7 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe
|
|||
extra_kwargs = {
|
||||
'name': {'required': True},
|
||||
'type': {'read_only': True},
|
||||
'periodic_display': {'label': _('Periodic perform')},
|
||||
'executed_amount': {'label': _('Executed amount')},
|
||||
'executed_amount': {'label': _('Executions')},
|
||||
}
|
||||
|
||||
def validate_name(self, name):
|
||||
|
|
|
@ -54,10 +54,13 @@ class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializ
|
|||
'ssh_key_change_strategy', 'passphrase', 'recipients', 'params'
|
||||
]
|
||||
extra_kwargs = {**BaseAutomationSerializer.Meta.extra_kwargs, **{
|
||||
'accounts': {'required': True},
|
||||
'accounts': {'required': True, 'help_text': _('Please enter your account username')},
|
||||
'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
|
||||
|
|
|
@ -7,7 +7,6 @@ from .change_secret import (
|
|||
|
||||
|
||||
class PushAccountAutomationSerializer(ChangeSecretAutomationSerializer):
|
||||
|
||||
class Meta(ChangeSecretAutomationSerializer.Meta):
|
||||
model = PushAccountAutomation
|
||||
fields = [
|
||||
|
|
|
@ -4,4 +4,4 @@ from django.utils.translation import gettext_lazy as _
|
|||
|
||||
class AclsConfig(AppConfig):
|
||||
name = 'acls'
|
||||
verbose_name = _('Acls')
|
||||
verbose_name = _('App Acls')
|
||||
|
|
|
@ -6,5 +6,5 @@ class ActionChoices(models.TextChoices):
|
|||
reject = 'reject', _('Reject')
|
||||
accept = 'accept', _('Accept')
|
||||
review = 'review', _('Review')
|
||||
warning = 'warning', _('Warning')
|
||||
notice = 'notice', _('Notifications')
|
||||
warning = 'warning', _('Warn')
|
||||
notice = 'notice', _('Notify')
|
||||
|
|
|
@ -1,75 +1,129 @@
|
|||
# Generated by Django 3.1 on 2021-03-11 09:53
|
||||
|
||||
import uuid
|
||||
# Generated by Django 4.1.13 on 2024-05-09 03:16
|
||||
|
||||
import common.db.fields
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LoginACL',
|
||||
name='CommandFilterACL',
|
||||
fields=[
|
||||
('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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('rules', models.JSONField(default=dict, verbose_name='Rule')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Login acl',
|
||||
'ordering': ('priority', '-is_active', 'name'),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LoginAssetACL',
|
||||
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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('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')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Login asset acl',
|
||||
'ordering': ('priority', '-is_active', 'name'),
|
||||
'unique_together': {('name', 'org_id')},
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,98 +1,54 @@
|
|||
# Generated by Django 3.1.12 on 2021-09-26 02:47
|
||||
import django
|
||||
# Generated by Django 4.1.13 on 2024-05-09 03:16
|
||||
|
||||
from django.conf import settings
|
||||
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', ])
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('acls', '0001_initial'),
|
||||
('authentication', '0004_ssotoken'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
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='loginassetacl',
|
||||
name='reviewers',
|
||||
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='loginacl',
|
||||
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'),
|
||||
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='loginacl',
|
||||
name='rules',
|
||||
field=models.JSONField(default=dict, verbose_name='Rule'),
|
||||
model_name='connectmethodacl',
|
||||
name='reviewers',
|
||||
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
|
||||
),
|
||||
migrations.RunPython(migrate_login_confirm),
|
||||
migrations.RunPython(migrate_ip_group),
|
||||
migrations.RemoveField(
|
||||
model_name='loginacl',
|
||||
name='ip_group',
|
||||
migrations.AlterUniqueTogether(
|
||||
name='commandgroup',
|
||||
unique_together={('org_id', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='loginacl',
|
||||
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login acl'},
|
||||
migrations.AddField(
|
||||
model_name='commandfilteracl',
|
||||
name='command_groups',
|
||||
field=models.ManyToManyField(related_name='command_filters', to='acls.commandgroup', verbose_name='Command group'),
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
migrations.AddField(
|
||||
model_name='commandfilteracl',
|
||||
name='reviewers',
|
||||
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='loginassetacl',
|
||||
options={'ordering': ('priority', '-is_active', 'name'), 'verbose_name': 'Login asset acl'},
|
||||
unique_together={('name', 'org_id')},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='commandfilteracl',
|
||||
unique_together={('name', 'org_id')},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
# 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'},
|
||||
),
|
||||
]
|
|
@ -1,33 +0,0 @@
|
|||
# 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',
|
||||
),
|
||||
|
||||
]
|
|
@ -1,34 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,70 +0,0 @@
|
|||
# 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')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,21 +0,0 @@
|
|||
# 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),
|
||||
]
|
|
@ -1,33 +0,0 @@
|
|||
# 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'},
|
||||
),
|
||||
]
|
|
@ -1,53 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,44 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,41 +0,0 @@
|
|||
# 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)
|
||||
]
|
|
@ -1,66 +0,0 @@
|
|||
# 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',
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,46 +0,0 @@
|
|||
# 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,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,47 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,16 +0,0 @@
|
|||
# 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'},
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -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 line one command"))
|
||||
content = models.TextField(verbose_name=_("Content"), help_text=_("One command per line"))
|
||||
ignore_case = models.BooleanField(default=True, verbose_name=_('Ignore case'))
|
||||
|
||||
TypeChoices = TypeChoices
|
||||
|
|
|
@ -39,7 +39,7 @@ class UserLoginReminderMsg(UserMessage):
|
|||
|
||||
|
||||
class AssetLoginReminderMsg(UserMessage):
|
||||
subject = _('Asset login reminder')
|
||||
subject = _('User login alert for asset')
|
||||
|
||||
def __init__(self, user, asset: Asset, login_user: User, account: Account, input_username):
|
||||
self.asset = asset
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
{% load i18n %}
|
||||
|
||||
<h3>{% trans 'Respectful' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
|
||||
<h3>{% trans 'Dear' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
|
||||
<hr>
|
||||
<p><strong>{% trans 'User' %}:</strong> [{{ name }}({{ username }})]</p>
|
||||
<p><strong>{% trans 'Assets' %}:</strong> [{{ asset }}]</p>
|
||||
<p><strong>{% trans 'Account' %}:</strong> [{{ account_name }}({{ account }})]</p>
|
||||
<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>
|
||||
<hr>
|
||||
|
||||
<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 '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>
|
|
@ -1,14 +1,16 @@
|
|||
{% load i18n %}
|
||||
|
||||
<h3>{% trans 'Respectful' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
|
||||
<h3>{% trans 'Dear' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
|
||||
<hr>
|
||||
<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>
|
||||
<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>
|
||||
<hr>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
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()
|
|
@ -1,42 +0,0 @@
|
|||
# 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')},
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# 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',
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -1,38 +0,0 @@
|
|||
# Generated by Django 2.1.11 on 2019-12-18 09:05
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0003_auto_20191210_1659'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DatabaseApp',
|
||||
fields=[
|
||||
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||
('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')),
|
||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||
('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=128, verbose_name='Type')),
|
||||
('host', models.CharField(db_index=True, max_length=128, verbose_name='Host')),
|
||||
('port', models.IntegerField(default=3306, verbose_name='Port')),
|
||||
('database', models.CharField(blank=True, db_index=True, max_length=128, null=True, verbose_name='Database')),
|
||||
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'DatabaseApp',
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='databaseapp',
|
||||
unique_together={('org_id', 'name')},
|
||||
),
|
||||
]
|
|
@ -1,34 +0,0 @@
|
|||
# Generated by Django 2.2.13 on 2020-08-07 07:13
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0004_auto_20191218_1705'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='K8sApp',
|
||||
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=[('k8s', 'Kubernetes')], default='k8s', max_length=128, verbose_name='Type')),
|
||||
('cluster', models.CharField(max_length=1024, verbose_name='Cluster')),
|
||||
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'KubernetesApp',
|
||||
'ordering': ('name',),
|
||||
'unique_together': {('org_id', 'name')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,139 +0,0 @@
|
|||
# Generated by Django 2.2.13 on 2020-10-19 12:01
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
CATEGORY_DB_LIST = ['mysql', 'oracle', 'postgresql', 'mariadb']
|
||||
CATEGORY_REMOTE_LIST = ['chrome', 'mysql_workbench', 'vmware_client', 'custom']
|
||||
CATEGORY_CLOUD_LIST = ['k8s']
|
||||
|
||||
CATEGORY_DB = 'db'
|
||||
CATEGORY_REMOTE = 'remote_app'
|
||||
CATEGORY_CLOUD = 'cloud'
|
||||
CATEGORY_LIST = [CATEGORY_DB, CATEGORY_REMOTE, CATEGORY_CLOUD]
|
||||
|
||||
|
||||
def get_application_category(old_app):
|
||||
_type = old_app.type
|
||||
if _type in CATEGORY_DB_LIST:
|
||||
category = CATEGORY_DB
|
||||
elif _type in CATEGORY_REMOTE_LIST:
|
||||
category = CATEGORY_REMOTE
|
||||
elif _type in CATEGORY_CLOUD_LIST:
|
||||
category = CATEGORY_CLOUD
|
||||
else:
|
||||
category = None
|
||||
return category
|
||||
|
||||
|
||||
def common_to_application_json(old_app):
|
||||
category = get_application_category(old_app)
|
||||
date_updated = old_app.date_updated if hasattr(old_app, 'date_updated') else old_app.date_created
|
||||
return {
|
||||
'id': old_app.id,
|
||||
'name': old_app.name,
|
||||
'type': old_app.type,
|
||||
'category': category,
|
||||
'comment': old_app.comment,
|
||||
'created_by': old_app.created_by,
|
||||
'date_created': old_app.date_created,
|
||||
'date_updated': date_updated,
|
||||
'org_id': old_app.org_id
|
||||
}
|
||||
|
||||
|
||||
def db_to_application_json(database):
|
||||
app_json = common_to_application_json(database)
|
||||
app_json.update({
|
||||
'attrs': {
|
||||
'host': database.host,
|
||||
'port': database.port,
|
||||
'database': database.database
|
||||
}
|
||||
})
|
||||
return app_json
|
||||
|
||||
|
||||
def remote_to_application_json(remote):
|
||||
app_json = common_to_application_json(remote)
|
||||
attrs = {
|
||||
'asset': str(remote.asset.id),
|
||||
'path': remote.path,
|
||||
}
|
||||
attrs.update(remote.params)
|
||||
app_json.update({
|
||||
'attrs': attrs
|
||||
})
|
||||
return app_json
|
||||
|
||||
|
||||
def k8s_to_application_json(k8s):
|
||||
app_json = common_to_application_json(k8s)
|
||||
app_json.update({
|
||||
'attrs': {
|
||||
'cluster': k8s.cluster
|
||||
}
|
||||
})
|
||||
return app_json
|
||||
|
||||
|
||||
def migrate_and_integrate_applications(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
|
||||
database_app_model = apps.get_model("applications", "DatabaseApp")
|
||||
remote_app_model = apps.get_model("applications", "RemoteApp")
|
||||
k8s_app_model = apps.get_model("applications", "K8sApp")
|
||||
|
||||
database_apps = database_app_model.objects.using(db_alias).all()
|
||||
remote_apps = remote_app_model.objects.using(db_alias).all()
|
||||
k8s_apps = k8s_app_model.objects.using(db_alias).all()
|
||||
|
||||
database_applications = [db_to_application_json(db_app) for db_app in database_apps]
|
||||
remote_applications = [remote_to_application_json(remote_app) for remote_app in remote_apps]
|
||||
k8s_applications = [k8s_to_application_json(k8s_app) for k8s_app in k8s_apps]
|
||||
|
||||
applications_json = database_applications + remote_applications + k8s_applications
|
||||
application_model = apps.get_model("applications", "Application")
|
||||
applications = [
|
||||
application_model(**application_json)
|
||||
for application_json in applications_json
|
||||
if application_json['category'] in CATEGORY_LIST
|
||||
]
|
||||
for application in applications:
|
||||
if application_model.objects.using(db_alias).filter(name=application.name).exists():
|
||||
application.name = '{}-{}'.format(application.name, application.type)
|
||||
application.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0057_fill_node_value_assets_amount_and_parent_key'),
|
||||
('applications', '0005_k8sapp'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Application',
|
||||
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')),
|
||||
('category', models.CharField(choices=[('db', 'Database'), ('remote_app', 'Remote app'), ('cloud', 'Cloud')], max_length=16, verbose_name='Category')),
|
||||
('type', models.CharField(choices=[('mysql', 'MySQL'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type')),
|
||||
('attrs', models.JSONField(default=dict)),
|
||||
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||
('domain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='applications', to='assets.Domain', verbose_name='Domain')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('name',),
|
||||
'unique_together': {('org_id', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.RunPython(migrate_and_integrate_applications),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.1 on 2020-11-19 03:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0006_application'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='attrs',
|
||||
field=models.JSONField(),
|
||||
),
|
||||
]
|
|
@ -1,28 +0,0 @@
|
|||
# Generated by Django 3.1 on 2021-01-03 20:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('perms', '0017_auto_20210104_0435'),
|
||||
('applications', '0007_auto_20201119_1110'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='DatabaseApp',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='K8sApp',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='attrs',
|
||||
field=models.JSONField(default=dict, verbose_name='Attrs'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='RemoteApp',
|
||||
),
|
||||
]
|
|
@ -1,25 +0,0 @@
|
|||
# Generated by Django 3.1.6 on 2021-06-23 09:48
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0070_auto_20210426_1515'),
|
||||
('applications', '0008_auto_20210104_0435'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ApplicationUser',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('assets.systemuser',),
|
||||
),
|
||||
]
|
|
@ -1,96 +0,0 @@
|
|||
# Generated by Django 3.1.12 on 2021-08-26 09:07
|
||||
|
||||
import uuid
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
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),
|
||||
('assets', '0076_delete_assetuser'),
|
||||
('applications', '0009_applicationuser'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalAccount',
|
||||
fields=[
|
||||
('org_id',
|
||||
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
|
||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[
|
||||
django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')],
|
||||
verbose_name='Username')),
|
||||
('password',
|
||||
common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key',
|
||||
common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(blank=True, editable=False, verbose_name='Date updated')),
|
||||
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||
('version', models.IntegerField(default=1, verbose_name='Version')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField()),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type',
|
||||
models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('app', models.ForeignKey(blank=True, db_constraint=False, null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING, related_name='+',
|
||||
to='applications.application', verbose_name='Database')),
|
||||
('history_user',
|
||||
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+',
|
||||
to=settings.AUTH_USER_MODEL)),
|
||||
('systemuser', models.ForeignKey(blank=True, db_constraint=False, null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING, related_name='+',
|
||||
to='assets.systemuser', verbose_name='System user')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Account',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': 'history_date',
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Account',
|
||||
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')),
|
||||
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[
|
||||
django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')],
|
||||
verbose_name='Username')),
|
||||
('password',
|
||||
common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key',
|
||||
common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||
('version', models.IntegerField(default=1, verbose_name='Version')),
|
||||
('app', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE,
|
||||
to='applications.application', verbose_name='Database')),
|
||||
('systemuser',
|
||||
models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.systemuser',
|
||||
verbose_name='System user')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Account',
|
||||
'unique_together': {('username', 'app', 'systemuser')},
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
|
@ -1,40 +0,0 @@
|
|||
# Generated by Django 3.1.12 on 2021-08-26 09:59
|
||||
|
||||
from django.db import migrations, transaction
|
||||
from django.db.models import F
|
||||
|
||||
|
||||
def migrate_app_account(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
app_perm_model = apps.get_model("perms", "ApplicationPermission")
|
||||
app_account_model = apps.get_model("applications", 'Account')
|
||||
|
||||
queryset = app_perm_model.objects \
|
||||
.exclude(system_users__isnull=True) \
|
||||
.exclude(applications__isnull=True) \
|
||||
.annotate(systemuser=F('system_users')) \
|
||||
.annotate(app=F('applications')) \
|
||||
.values('app', 'systemuser', 'org_id')
|
||||
|
||||
accounts = []
|
||||
for p in queryset:
|
||||
if not p['app']:
|
||||
continue
|
||||
account = app_account_model(
|
||||
app_id=p['app'], systemuser_id=p['systemuser'],
|
||||
version=1, org_id=p['org_id']
|
||||
)
|
||||
accounts.append(account)
|
||||
|
||||
app_account_model.objects.using(db_alias).bulk_create(accounts, ignore_conflicts=True)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0010_appaccount_historicalappaccount'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_app_account)
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 3.1.13 on 2021-10-14 14:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0011_auto_20210826_1759'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='account',
|
||||
name='username',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalaccount',
|
||||
name='username',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username'),
|
||||
),
|
||||
]
|
|
@ -1,17 +0,0 @@
|
|||
# Generated by Django 3.1.13 on 2021-10-26 09:11
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0012_auto_20211014_2209'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='application',
|
||||
options={'ordering': ('name',), 'verbose_name': 'Application'},
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.1.12 on 2021-11-05 08:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0013_auto_20211026_1711'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('mysql', 'MySQL'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('sqlserver', 'SQLServer'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.1.13 on 2022-01-12 12:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0014_auto_20211105_1605'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('mysql', 'MySQL'), ('redis', 'Redis'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('sqlserver', 'SQLServer'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
|
||||
),
|
||||
]
|
|
@ -1,24 +0,0 @@
|
|||
# Generated by Django 3.1.13 on 2022-01-18 06:55
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0015_auto_20220112_2035'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='account',
|
||||
name='app',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.application', verbose_name='Application'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalaccount',
|
||||
name='app',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='applications.application', verbose_name='Application'),
|
||||
),
|
||||
]
|
|
@ -1,25 +0,0 @@
|
|||
# Generated by Django 3.1.13 on 2022-02-17 13:35
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0016_auto_20220118_1455'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='account',
|
||||
options={'permissions': [('view_applicationaccountsecret', 'Can view application account secret'), ('change_appplicationaccountsecret', 'Can change application account secret')], 'verbose_name': 'Application account'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='applicationuser',
|
||||
options={'verbose_name': 'Application user'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='historicalaccount',
|
||||
options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Application account'},
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.1.13 on 2022-02-23 07:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0017_auto_20220217_2135'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('mysql', 'MySQL'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
|
||||
),
|
||||
]
|
|
@ -1,17 +0,0 @@
|
|||
# Generated by Django 3.1.14 on 2022-03-10 10:53
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0018_auto_20220223_1539'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='application',
|
||||
options={'ordering': ('name',), 'permissions': [('match_application', 'Can match application')], 'verbose_name': 'Application'},
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.1.14 on 2022-03-16 12:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0019_auto_20220310_1853'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('mysql', 'MySQL'), ('mariadb', 'MariaDB'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
|
||||
),
|
||||
]
|
|
@ -1,22 +0,0 @@
|
|||
# Generated by Django 3.1.14 on 2022-06-29 10:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0020_auto_20220316_2028'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='historicalaccount',
|
||||
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Application account', 'verbose_name_plural': 'historical Application accounts'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalaccount',
|
||||
name='history_date',
|
||||
field=models.DateTimeField(db_index=True),
|
||||
),
|
||||
]
|
|
@ -1,22 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2022-07-14 02:46
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_db_oracle_version_to_attrs(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
model = apps.get_model("applications", "Application")
|
||||
oracles = list(model.objects.using(db_alias).filter(type='oracle'))
|
||||
for o in oracles:
|
||||
o.attrs['version'] = '12c'
|
||||
model.objects.using(db_alias).bulk_update(oracles, ['attrs'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('applications', '0021_auto_20220629_1826'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_db_oracle_version_to_attrs)
|
||||
]
|
|
@ -1,47 +0,0 @@
|
|||
# Generated by Django 3.1.14 on 2022-07-15 07:56
|
||||
import time
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_account_dirty_data(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
account_model = apps.get_model('applications', 'Account')
|
||||
|
||||
count = 0
|
||||
bulk_size = 1000
|
||||
|
||||
while True:
|
||||
accounts = account_model.objects.using(db_alias) \
|
||||
.filter(org_id='')[count:count + bulk_size]
|
||||
|
||||
if not accounts:
|
||||
break
|
||||
|
||||
accounts = list(accounts)
|
||||
start = time.time()
|
||||
for i in accounts:
|
||||
if i.app:
|
||||
org_id = i.app.org_id
|
||||
elif i.systemuser:
|
||||
org_id = i.systemuser.org_id
|
||||
else:
|
||||
org_id = ''
|
||||
if org_id:
|
||||
i.org_id = org_id
|
||||
|
||||
account_model.objects.bulk_update(accounts, ['org_id', ])
|
||||
print("Update account org is empty: {}-{} using: {:.2f}s".format(
|
||||
count, count + len(accounts), time.time() - start
|
||||
))
|
||||
count += len(accounts)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('applications', '0022_auto_20220714_1046'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_account_dirty_data),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.2.14 on 2022-11-04 07:06
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0023_auto_20220715_1556'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('mysql', 'MySQL'), ('mariadb', 'MariaDB'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('clickhouse', 'ClickHouse'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
|
||||
),
|
||||
]
|
|
@ -1,60 +0,0 @@
|
|||
# Generated by Django 3.2.14 on 2022-08-17 05:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def migrate_db_oracle_version_to_attrs(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
model = apps.get_model("applications", "Application")
|
||||
oracles = list(model.objects.using(db_alias).filter(type='oracle'))
|
||||
for o in oracles:
|
||||
o.attrs['version'] = '12c'
|
||||
model.objects.using(db_alias).bulk_update(oracles, ['attrs'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('applications', '0024_alter_application_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_db_oracle_version_to_attrs),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='account',
|
||||
unique_together=None,
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='app',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='systemuser',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='application',
|
||||
name='domain',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='historicalaccount',
|
||||
name='app',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='historicalaccount',
|
||||
name='history_user',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='historicalaccount',
|
||||
name='systemuser',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='category',
|
||||
field=models.CharField(max_length=16, verbose_name='Category'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='type',
|
||||
field=models.CharField(max_length=16, verbose_name='Type'),
|
||||
),
|
||||
]
|
|
@ -1,60 +0,0 @@
|
|||
# Generated by Django 3.2.14 on 2022-08-17 09:16
|
||||
import time
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_account_dirty_data(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
account_model = apps.get_model('applications', 'Account')
|
||||
|
||||
count = 0
|
||||
bulk_size = 1000
|
||||
|
||||
while True:
|
||||
accounts = account_model.objects.using(db_alias) \
|
||||
.filter(org_id='')[count:count + bulk_size]
|
||||
|
||||
if not accounts:
|
||||
break
|
||||
|
||||
accounts = list(accounts)
|
||||
start = time.time()
|
||||
for i in accounts:
|
||||
if i.app:
|
||||
org_id = i.app.org_id
|
||||
elif i.systemuser:
|
||||
org_id = i.systemuser.org_id
|
||||
else:
|
||||
org_id = ''
|
||||
if org_id:
|
||||
i.org_id = org_id
|
||||
|
||||
account_model.objects.bulk_update(accounts, ['org_id', ])
|
||||
print("Update account org is empty: {}-{} using: {:.2f}s".format(
|
||||
count, count + len(accounts), time.time() - start
|
||||
))
|
||||
count += len(accounts)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('applications', '0025_auto_20220817_1346'),
|
||||
('perms', '0031_auto_20220816_1600'),
|
||||
('ops', '0022_auto_20220817_1346'),
|
||||
('assets', '0100_auto_20220711_1413'),
|
||||
('tickets', '0020_auto_20220817_1346'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_account_dirty_data),
|
||||
migrations.DeleteModel(
|
||||
name='Account',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='HistoricalAccount',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ApplicationUser',
|
||||
),
|
||||
]
|
|
@ -1,28 +0,0 @@
|
|||
# Generated by Django 3.2.14 on 2022-12-20 11:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0026_auto_20220817_1716'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='application',
|
||||
name='updated_by',
|
||||
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='comment',
|
||||
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='created_by',
|
||||
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||
),
|
||||
]
|
|
@ -1,24 +0,0 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from common.db.models import JMSBaseModel
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
|
||||
|
||||
class Application(JMSBaseModel, OrgModelMixin):
|
||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||
category = models.CharField(
|
||||
max_length=16, verbose_name=_('Category')
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=16, verbose_name=_('Type')
|
||||
)
|
||||
attrs = models.JSONField(default=dict, verbose_name=_('Attrs'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Application')
|
||||
unique_together = [('org_id', 'name')]
|
||||
ordering = ('name',)
|
||||
permissions = [
|
||||
('match_application', _('Can match application')),
|
||||
]
|
|
@ -1,2 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
|
@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
|
||||
class AssetsConfig(AppConfig):
|
||||
name = 'assets'
|
||||
verbose_name = _('App assets')
|
||||
verbose_name = _('App Assets')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
|
@ -324,19 +324,19 @@ class BasePlaybookManager:
|
|||
shutil.rmtree(self.runtime_dir, ignore_errors=True)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
print(">>> 任务准备阶段\n")
|
||||
print(_(">>> Task preparation phase"), end="\n")
|
||||
runners = self.get_runners()
|
||||
if len(runners) > 1:
|
||||
print("### 分次执行任务, 总共 {}\n".format(len(runners)))
|
||||
print(_(">>> Executing tasks in batches, total {runner_count}").format(runner_count=len(runners)))
|
||||
elif len(runners) == 1:
|
||||
print(">>> 开始执行任务\n")
|
||||
print(_(">>> Start executing tasks"))
|
||||
else:
|
||||
print("### 没有需要执行的任务\n")
|
||||
print(_(">>> No tasks need to be executed"), end="\n")
|
||||
|
||||
self.execution.date_start = timezone.now()
|
||||
for i, runner in enumerate(runners, start=1):
|
||||
if len(runners) > 1:
|
||||
print(">>> 开始执行第 {} 批任务".format(i))
|
||||
print(_(">>> Begin executing batch {index} of tasks").format(index=i))
|
||||
ssh_tunnel = SSHTunnelManager()
|
||||
ssh_tunnel.local_gateway_prepare(runner)
|
||||
try:
|
||||
|
|
|
@ -113,8 +113,7 @@ class BaseType(TextChoices):
|
|||
@classmethod
|
||||
def get_choices(cls):
|
||||
if not settings.XPACK_ENABLED:
|
||||
return [
|
||||
(tp.value, tp.label)
|
||||
for tp in cls.get_community_types()
|
||||
]
|
||||
return cls.choices
|
||||
choices = [(tp.value, tp.label) for tp in cls.get_community_types()]
|
||||
else:
|
||||
choices = cls.choices
|
||||
return choices
|
||||
|
|
|
@ -12,7 +12,6 @@ class Category(ChoicesMixin, models.TextChoices):
|
|||
DATABASE = 'database', _("Database")
|
||||
CLOUD = 'cloud', _("Cloud service")
|
||||
WEB = 'web', _("Web")
|
||||
GPT = 'gpt', "GPT"
|
||||
CUSTOM = 'custom', _("Custom type")
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -37,6 +37,7 @@ class DatabaseTypes(BaseType):
|
|||
'verify_account_enabled': True,
|
||||
'change_secret_enabled': True,
|
||||
'push_account_enabled': True,
|
||||
'remove_account_enabled': True,
|
||||
},
|
||||
cls.REDIS: {
|
||||
'ansible_enabled': False,
|
||||
|
|
|
@ -42,7 +42,8 @@ class DeviceTypes(BaseType):
|
|||
'gather_accounts_enabled': False,
|
||||
'verify_account_enabled': True,
|
||||
'change_secret_enabled': True,
|
||||
'push_account_enabled': False
|
||||
'push_account_enabled': False,
|
||||
'remove_account_enabled': False,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,9 @@ class HostTypes(BaseType):
|
|||
'gather_accounts_enabled': True,
|
||||
'verify_account_enabled': True,
|
||||
'change_secret_enabled': True,
|
||||
'push_account_enabled': True
|
||||
'push_account_enabled': True,
|
||||
'remove_account_enabled': True,
|
||||
|
||||
},
|
||||
cls.WINDOWS: {
|
||||
'ansible_config': {
|
||||
|
|
|
@ -166,23 +166,26 @@ class AllTypes(ChoicesMixin):
|
|||
|
||||
@classmethod
|
||||
def category_types(cls):
|
||||
return (
|
||||
types = [
|
||||
(Category.HOST, HostTypes),
|
||||
(Category.DEVICE, DeviceTypes),
|
||||
(Category.DATABASE, DatabaseTypes),
|
||||
(Category.CLOUD, CloudTypes),
|
||||
(Category.WEB, WebTypes),
|
||||
(Category.GPT, GPTTypes),
|
||||
(Category.CUSTOM, CustomTypes),
|
||||
)
|
||||
]
|
||||
if settings.XPACK_ENABLED:
|
||||
types.extend([
|
||||
(Category.CLOUD, CloudTypes),
|
||||
(Category.CUSTOM, CustomTypes),
|
||||
])
|
||||
return types
|
||||
|
||||
@classmethod
|
||||
def get_types(cls, exclude_custom=False):
|
||||
choices = []
|
||||
|
||||
for name, tp in dict(cls.category_types()).items():
|
||||
if name == Category.CUSTOM and exclude_custom:
|
||||
continue
|
||||
# if name == Category.CUSTOM and exclude_custom:
|
||||
# continue
|
||||
choices.extend(tp.get_types())
|
||||
return choices
|
||||
|
||||
|
@ -331,11 +334,13 @@ class AllTypes(ChoicesMixin):
|
|||
return data
|
||||
|
||||
@classmethod
|
||||
def create_or_update_by_platform_data(cls, platform_data, platform_cls=None):
|
||||
def create_or_update_by_platform_data(cls, platform_data, platform_cls=None, automation_cls=None):
|
||||
# 不直接用 Platform 是因为可能在 migrations 中使用
|
||||
from assets.models import Platform
|
||||
from assets.models import Platform, PlatformAutomation
|
||||
if platform_cls is None:
|
||||
platform_cls = Platform
|
||||
if automation_cls is None:
|
||||
automation_cls = PlatformAutomation
|
||||
|
||||
automation_data = platform_data.pop('automation', {})
|
||||
protocols_data = platform_data.pop('protocols', [])
|
||||
|
@ -344,12 +349,18 @@ class AllTypes(ChoicesMixin):
|
|||
platform, created = platform_cls.objects.update_or_create(
|
||||
defaults=platform_data, name=name
|
||||
)
|
||||
if not platform.automation:
|
||||
automation = platform_cls.automation.field.related_model.objects.create()
|
||||
|
||||
try:
|
||||
automation = platform.automation
|
||||
except:
|
||||
automation = None
|
||||
|
||||
if not automation:
|
||||
automation = automation_cls.objects.create()
|
||||
platform.automation = automation
|
||||
platform.save()
|
||||
else:
|
||||
automation = platform.automation
|
||||
automation = automation
|
||||
for k, v in automation_data.items():
|
||||
setattr(automation, k, v)
|
||||
automation.save()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue