mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-07-04 18:46:39 +00:00
commit
ce0632f49b
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,6 +16,7 @@ dump.rdb
|
|||||||
.cache/
|
.cache/
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.fleet/
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
config.py
|
config.py
|
||||||
config.yml
|
config.yml
|
||||||
|
18
Dockerfile
18
Dockerfile
@ -1,4 +1,4 @@
|
|||||||
FROM python:3.8-slim as stage-build
|
FROM python:3.9-slim as stage-build
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
@ -8,7 +8,7 @@ WORKDIR /opt/jumpserver
|
|||||||
ADD . .
|
ADD . .
|
||||||
RUN cd utils && bash -ixeu build.sh
|
RUN cd utils && bash -ixeu build.sh
|
||||||
|
|
||||||
FROM python:3.8-slim
|
FROM python:3.9-slim
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
MAINTAINER JumpServer Team <ibuler@qq.com>
|
MAINTAINER JumpServer Team <ibuler@qq.com>
|
||||||
|
|
||||||
@ -18,7 +18,6 @@ ARG BUILD_DEPENDENCIES=" \
|
|||||||
pkg-config"
|
pkg-config"
|
||||||
|
|
||||||
ARG DEPENDENCIES=" \
|
ARG DEPENDENCIES=" \
|
||||||
default-libmysqlclient-dev \
|
|
||||||
freetds-dev \
|
freetds-dev \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libffi-dev \
|
libffi-dev \
|
||||||
@ -28,21 +27,20 @@ ARG DEPENDENCIES=" \
|
|||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
libxmlsec1-dev \
|
libxmlsec1-dev \
|
||||||
libxmlsec1-openssl \
|
libxmlsec1-openssl \
|
||||||
libaio-dev \
|
libaio-dev"
|
||||||
openssh-client \
|
|
||||||
sshpass"
|
|
||||||
|
|
||||||
ARG TOOLS=" \
|
ARG TOOLS=" \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
|
default-libmysqlclient-dev \
|
||||||
default-mysql-client \
|
default-mysql-client \
|
||||||
iputils-ping \
|
|
||||||
locales \
|
locales \
|
||||||
|
openssh-client \
|
||||||
procps \
|
procps \
|
||||||
redis-tools \
|
sshpass \
|
||||||
telnet \
|
telnet \
|
||||||
vim \
|
|
||||||
unzip \
|
unzip \
|
||||||
|
vim \
|
||||||
wget"
|
wget"
|
||||||
|
|
||||||
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
|
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
|
||||||
@ -82,6 +80,8 @@ ENV PIP_MIRROR=$PIP_MIRROR
|
|||||||
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
|
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
|
||||||
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
|
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
|
||||||
|
|
||||||
|
ARG DEBUG
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
set -ex \
|
set -ex \
|
||||||
&& pip config set global.index-url ${PIP_MIRROR} \
|
&& pip config set global.index-url ${PIP_MIRROR} \
|
||||||
|
10
Dockerfile-ee
Normal file
10
Dockerfile-ee
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
ARG VERSION
|
||||||
|
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} as build-xpack
|
||||||
|
FROM jumpserver/core:${VERSION}
|
||||||
|
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack
|
||||||
|
|
||||||
|
WORKDIR /opt/jumpserver
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
|
set -ex \
|
||||||
|
&& pip install -r requirements/requirements_xpack.txt
|
@ -1,4 +1,4 @@
|
|||||||
FROM python:3.8-slim as stage-build
|
FROM python:3.9-slim as stage-build
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
@ -8,7 +8,7 @@ WORKDIR /opt/jumpserver
|
|||||||
ADD . .
|
ADD . .
|
||||||
RUN cd utils && bash -ixeu build.sh
|
RUN cd utils && bash -ixeu build.sh
|
||||||
|
|
||||||
FROM python:3.8-slim
|
FROM python:3.9-slim
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
MAINTAINER JumpServer Team <ibuler@qq.com>
|
MAINTAINER JumpServer Team <ibuler@qq.com>
|
||||||
|
|
||||||
@ -18,7 +18,6 @@ ARG BUILD_DEPENDENCIES=" \
|
|||||||
pkg-config"
|
pkg-config"
|
||||||
|
|
||||||
ARG DEPENDENCIES=" \
|
ARG DEPENDENCIES=" \
|
||||||
default-libmysqlclient-dev \
|
|
||||||
freetds-dev \
|
freetds-dev \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libffi-dev \
|
libffi-dev \
|
||||||
@ -28,21 +27,20 @@ ARG DEPENDENCIES=" \
|
|||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
libxmlsec1-dev \
|
libxmlsec1-dev \
|
||||||
libxmlsec1-openssl \
|
libxmlsec1-openssl \
|
||||||
libaio-dev \
|
libaio-dev"
|
||||||
openssh-client \
|
|
||||||
sshpass"
|
|
||||||
|
|
||||||
ARG TOOLS=" \
|
ARG TOOLS=" \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
|
default-libmysqlclient-dev \
|
||||||
default-mysql-client \
|
default-mysql-client \
|
||||||
iputils-ping \
|
|
||||||
locales \
|
locales \
|
||||||
netcat \
|
openssh-client \
|
||||||
redis-server \
|
procps \
|
||||||
|
sshpass \
|
||||||
telnet \
|
telnet \
|
||||||
vim \
|
|
||||||
unzip \
|
unzip \
|
||||||
|
vim \
|
||||||
wget"
|
wget"
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
|
||||||
@ -69,13 +67,15 @@ ENV PIP_MIRROR=$PIP_MIRROR
|
|||||||
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
|
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
|
||||||
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
|
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
|
||||||
|
|
||||||
|
ARG DEBUG
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
set -ex \
|
set -ex \
|
||||||
&& pip config set global.index-url ${PIP_MIRROR} \
|
&& pip config set global.index-url ${PIP_MIRROR} \
|
||||||
&& pip install --upgrade pip \
|
&& pip install --upgrade pip \
|
||||||
&& pip install --upgrade setuptools wheel \
|
&& pip install --upgrade setuptools wheel \
|
||||||
&& pip install https://download.jumpserver.org/pypi/simple/cryptography/cryptography-36.0.1-cp38-cp38-linux_loongarch64.whl \
|
&& pip install https://download.jumpserver.org/pypi/simple/cryptography/cryptography-38.0.4-cp39-cp39-linux_loongarch64.whl \
|
||||||
&& pip install https://download.jumpserver.org/pypi/simple/greenlet/greenlet-1.1.2-cp38-cp38-linux_loongarch64.whl \
|
&& pip install https://download.jumpserver.org/pypi/simple/greenlet/greenlet-1.1.2-cp39-cp39-linux_loongarch64.whl \
|
||||||
&& pip install $(grep 'PyNaCl' requirements/requirements.txt) \
|
&& pip install $(grep 'PyNaCl' requirements/requirements.txt) \
|
||||||
&& GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=true pip install grpcio \
|
&& GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=true pip install grpcio \
|
||||||
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
|
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
|
||||||
|
@ -28,6 +28,7 @@ class Migration(migrations.Migration):
|
|||||||
('type', models.CharField(choices=[('command', 'Command'), ('regex', 'Regex')], default='command',
|
('type', models.CharField(choices=[('command', 'Command'), ('regex', 'Regex')], default='command',
|
||||||
max_length=16, verbose_name='Type')),
|
max_length=16, verbose_name='Type')),
|
||||||
('content', models.TextField(help_text='One line one command', verbose_name='Content')),
|
('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')),
|
('ignore_case', models.BooleanField(default=True, verbose_name='Ignore case')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
@ -57,7 +58,8 @@ class Migration(migrations.Migration):
|
|||||||
('assets', models.JSONField(verbose_name='Asset')),
|
('assets', models.JSONField(verbose_name='Asset')),
|
||||||
('commands', models.ManyToManyField(to='acls.CommandGroup', verbose_name='Commands')),
|
('commands', models.ManyToManyField(to='acls.CommandGroup', verbose_name='Commands')),
|
||||||
(
|
(
|
||||||
'reviewers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')),
|
'reviewers',
|
||||||
|
models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Command acl',
|
'verbose_name': 'Command acl',
|
||||||
|
@ -1,18 +1,33 @@
|
|||||||
# Generated by Django 3.2.14 on 2022-12-02 04:25
|
# Generated by Django 3.2.14 on 2022-12-02 04:25
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('acls', '0007_auto_20221202_1048'),
|
('acls', '0007_auto_20221202_1048'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AlterModelOptions(
|
||||||
model_name='commandgroup',
|
name='commandgroup',
|
||||||
name='comment',
|
options={'verbose_name': 'Command group'},
|
||||||
field=models.TextField(blank=True, verbose_name='Comment'),
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='commandfilteracl',
|
||||||
|
old_name='commands',
|
||||||
|
new_name='command_groups',
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='commandfilteracl',
|
||||||
|
options={'ordering': ('priority', 'name'), 'verbose_name': 'Command acl'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='loginacl',
|
||||||
|
options={'ordering': ('priority', 'name'), 'verbose_name': 'Login acl'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='loginassetacl',
|
||||||
|
options={'ordering': ('priority', 'name'), 'verbose_name': 'Login asset acl'},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
# Generated by Django 3.2.14 on 2022-12-03 16:01
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('acls', '0008_commandgroup_comment'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='commandgroup',
|
|
||||||
options={'verbose_name': 'Command group'},
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='commandfilteracl',
|
|
||||||
old_name='commands',
|
|
||||||
new_name='command_groups',
|
|
||||||
),
|
|
||||||
]
|
|
53
apps/acls/migrations/0009_auto_20221220_1956.py
Normal file
53
apps/acls/migrations/0009_auto_20221220_1956.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Generated by Django 3.2.14 on 2022-12-20 11:56
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('acls', '0008_commandgroup_comment'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='commandfilteracl',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='loginacl',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='loginassetacl',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='commandfilteracl',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='commandgroup',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='commandgroup',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='loginacl',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='loginassetacl',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
]
|
@ -1,25 +0,0 @@
|
|||||||
# Generated by Django 3.2.14 on 2022-12-05 03:22
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('acls', '0009_auto_20221204_0001'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='commandfilteracl',
|
|
||||||
options={'ordering': ('priority', 'name'), 'verbose_name': 'Command acl'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='loginacl',
|
|
||||||
options={'ordering': ('priority', 'name'), 'verbose_name': 'Login acl'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='loginassetacl',
|
|
||||||
options={'ordering': ('priority', 'name'), 'verbose_name': 'Login asset acl'},
|
|
||||||
),
|
|
||||||
]
|
|
@ -3,7 +3,7 @@ from django.db import models
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.mixins import CommonModelMixin
|
from common.db.models import JMSBaseModel
|
||||||
from common.utils import contains_ip
|
from common.utils import contains_ip
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class UserAssetAccountACLQuerySet(BaseACLQuerySet):
|
|||||||
|
|
||||||
def filter_account(self, username):
|
def filter_account(self, username):
|
||||||
q = Q(accounts__username_group__contains=username) | \
|
q = Q(accounts__username_group__contains=username) | \
|
||||||
Q(accounts__username_group__contains='*')
|
Q(accounts__username_group__contains='*')
|
||||||
return self.filter(q)
|
return self.filter(q)
|
||||||
|
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ class ACLManager(models.Manager):
|
|||||||
return self.get_queryset().valid()
|
return self.get_queryset().valid()
|
||||||
|
|
||||||
|
|
||||||
class BaseACL(CommonModelMixin):
|
class BaseACL(JMSBaseModel):
|
||||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
priority = models.IntegerField(
|
priority = models.IntegerField(
|
||||||
default=50, verbose_name=_("Priority"),
|
default=50, verbose_name=_("Priority"),
|
||||||
@ -77,7 +77,6 @@ class BaseACL(CommonModelMixin):
|
|||||||
action = models.CharField(max_length=64, default=ActionChoices.reject, verbose_name=_('Action'))
|
action = models.CharField(max_length=64, default=ActionChoices.reject, verbose_name=_('Action'))
|
||||||
reviewers = models.ManyToManyField('users.User', blank=True, verbose_name=_("Reviewers"))
|
reviewers = models.ManyToManyField('users.User', blank=True, verbose_name=_("Reviewers"))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_("Active"))
|
is_active = models.BooleanField(default=True, verbose_name=_("Active"))
|
||||||
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
|
|
||||||
|
|
||||||
ActionChoices = ActionChoices
|
ActionChoices = ActionChoices
|
||||||
objects = ACLManager.from_queryset(BaseACLQuerySet)()
|
objects = ACLManager.from_queryset(BaseACLQuerySet)()
|
||||||
|
@ -7,7 +7,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from common.utils import lazyproperty, get_logger
|
from common.utils import lazyproperty, get_logger
|
||||||
from orgs.mixins.models import JMSOrgBaseModel
|
from orgs.mixins.models import JMSOrgBaseModel
|
||||||
|
|
||||||
from .base import UserAssetAccountBaseACL
|
from .base import UserAssetAccountBaseACL
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
@ -26,7 +25,6 @@ class CommandGroup(JMSOrgBaseModel):
|
|||||||
)
|
)
|
||||||
content = models.TextField(verbose_name=_("Content"), help_text=_("One line one command"))
|
content = models.TextField(verbose_name=_("Content"), help_text=_("One line one command"))
|
||||||
ignore_case = models.BooleanField(default=True, verbose_name=_('Ignore case'))
|
ignore_case = models.BooleanField(default=True, verbose_name=_('Ignore case'))
|
||||||
comment = models.TextField(blank=True, verbose_name=_("Comment"))
|
|
||||||
|
|
||||||
TypeChoices = TypeChoices
|
TypeChoices = TypeChoices
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
# Generated by Django 3.1.12 on 2021-08-26 09:07
|
# Generated by Django 3.1.12 on 2021-08-26 09:07
|
||||||
|
|
||||||
import assets.models.base
|
import uuid
|
||||||
import common.db.fields
|
|
||||||
from django.conf import settings
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import simple_history.models
|
import simple_history.models
|
||||||
import uuid
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import common.db.fields
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('assets', '0076_delete_assetuser'),
|
('assets', '0076_delete_assetuser'),
|
||||||
@ -22,14 +22,19 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HistoricalAccount',
|
name='HistoricalAccount',
|
||||||
fields=[
|
fields=[
|
||||||
('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')),
|
||||||
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
|
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
|
||||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
('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')),
|
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[
|
||||||
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')],
|
||||||
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
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')),
|
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||||
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
|
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
|
||||||
('date_updated', models.DateTimeField(blank=True, editable=False, verbose_name='Date updated')),
|
('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')),
|
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||||
@ -37,10 +42,17 @@ class Migration(migrations.Migration):
|
|||||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('history_date', models.DateTimeField()),
|
('history_date', models.DateTimeField()),
|
||||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
('history_type',
|
||||||
('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')),
|
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)),
|
('app', models.ForeignKey(blank=True, db_constraint=False, null=True,
|
||||||
('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')),
|
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={
|
options={
|
||||||
'verbose_name': 'historical Account',
|
'verbose_name': 'historical Account',
|
||||||
@ -52,20 +64,28 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Account',
|
name='Account',
|
||||||
fields=[
|
fields=[
|
||||||
('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')),
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
('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')),
|
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[
|
||||||
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')],
|
||||||
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
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')),
|
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||||
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
||||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||||
('version', models.IntegerField(default=1, verbose_name='Version')),
|
('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')),
|
('app', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE,
|
||||||
('systemuser', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.systemuser', verbose_name='System user')),
|
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={
|
options={
|
||||||
'verbose_name': 'Account',
|
'verbose_name': 'Account',
|
||||||
|
28
apps/applications/migrations/0027_auto_20221220_1956.py
Normal file
28
apps/applications/migrations/0027_auto_20221220_1956.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 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,12 +1,11 @@
|
|||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from common.db.models import JMSBaseModel
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from common.mixins import CommonModelMixin
|
|
||||||
|
|
||||||
|
|
||||||
class Application(CommonModelMixin, OrgModelMixin):
|
class Application(JMSBaseModel, OrgModelMixin):
|
||||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
category = models.CharField(
|
category = models.CharField(
|
||||||
max_length=16, verbose_name=_('Category')
|
max_length=16, verbose_name=_('Category')
|
||||||
@ -15,9 +14,6 @@ class Application(CommonModelMixin, OrgModelMixin):
|
|||||||
max_length=16, verbose_name=_('Type')
|
max_length=16, verbose_name=_('Type')
|
||||||
)
|
)
|
||||||
attrs = models.JSONField(default=dict, verbose_name=_('Attrs'))
|
attrs = models.JSONField(default=dict, verbose_name=_('Attrs'))
|
||||||
comment = models.TextField(
|
|
||||||
max_length=128, default='', blank=True, verbose_name=_('Comment')
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Application')
|
verbose_name = _('Application')
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from .mixin import *
|
|
||||||
from .category import *
|
|
||||||
from .platform import *
|
|
||||||
from .asset import *
|
|
||||||
from .label import *
|
|
||||||
from .account import *
|
from .account import *
|
||||||
from .node import *
|
from .asset import *
|
||||||
from .domain import *
|
|
||||||
from .automations import *
|
from .automations import *
|
||||||
from .gathered_user import *
|
from .category import *
|
||||||
|
from .domain import *
|
||||||
from .favorite_asset import *
|
from .favorite_asset import *
|
||||||
|
from .label import *
|
||||||
|
from .mixin import *
|
||||||
|
from .node import *
|
||||||
|
from .platform import *
|
||||||
|
from .tree import *
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.generics import CreateAPIView, ListAPIView
|
from rest_framework.generics import CreateAPIView, ListAPIView
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from assets import serializers
|
||||||
from rbac.permissions import RBACPermission
|
from assets.filters import AccountFilterSet
|
||||||
|
from assets.models import Account, Asset
|
||||||
|
from assets.tasks import verify_accounts_connectivity
|
||||||
|
from authentication.const import ConfirmType
|
||||||
from common.mixins import RecordViewLogMixin
|
from common.mixins import RecordViewLogMixin
|
||||||
from common.permissions import UserConfirmation
|
from common.permissions import UserConfirmation
|
||||||
from authentication.const import ConfirmType
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from assets.models import Account
|
|
||||||
from assets.filters import AccountFilterSet
|
|
||||||
from assets.tasks import verify_accounts_connectivity
|
|
||||||
from assets import serializers
|
|
||||||
|
|
||||||
__all__ = ['AccountViewSet', 'AccountSecretsViewSet', 'AccountTaskCreateAPI', 'AccountHistoriesSecretAPI']
|
__all__ = [
|
||||||
|
'AccountViewSet', 'AccountSecretsViewSet', 'AccountTaskCreateAPI', 'AccountHistoriesSecretAPI'
|
||||||
|
]
|
||||||
|
|
||||||
|
from rbac.permissions import RBACPermission
|
||||||
|
|
||||||
|
|
||||||
class AccountViewSet(OrgBulkModelViewSet):
|
class AccountViewSet(OrgBulkModelViewSet):
|
||||||
@ -30,10 +33,18 @@ class AccountViewSet(OrgBulkModelViewSet):
|
|||||||
'su_from_accounts': 'assets.view_account',
|
'su_from_accounts': 'assets.view_account',
|
||||||
}
|
}
|
||||||
|
|
||||||
@action(methods=['get'], detail=True, url_path='su-from-accounts')
|
@action(methods=['get'], detail=False, url_path='su-from-accounts')
|
||||||
def su_from_accounts(self, request, *args, **kwargs):
|
def su_from_accounts(self, request, *args, **kwargs):
|
||||||
account = super().get_object()
|
account_id = request.query_params.get('account')
|
||||||
accounts = account.get_su_from_accounts()
|
asset_id = request.query_params.get('asset')
|
||||||
|
if account_id:
|
||||||
|
account = get_object_or_404(Account, pk=account_id)
|
||||||
|
accounts = account.get_su_from_accounts()
|
||||||
|
elif asset_id:
|
||||||
|
asset = get_object_or_404(Asset, pk=asset_id)
|
||||||
|
accounts = asset.accounts.all()
|
||||||
|
else:
|
||||||
|
accounts = []
|
||||||
serializer = serializers.AccountSerializer(accounts, many=True)
|
serializer = serializers.AccountSerializer(accounts, many=True)
|
||||||
return Response(data=serializer.data)
|
return Response(data=serializer.data)
|
||||||
|
|
||||||
@ -54,8 +65,7 @@ class AccountSecretsViewSet(RecordViewLogMixin, AccountViewSet):
|
|||||||
'default': serializers.AccountSecretSerializer,
|
'default': serializers.AccountSecretSerializer,
|
||||||
}
|
}
|
||||||
http_method_names = ['get', 'options']
|
http_method_names = ['get', 'options']
|
||||||
# Todo: 记得打开
|
permission_classes = [RBACPermission, UserConfirmation.require(ConfirmType.MFA)]
|
||||||
# permission_classes = [RBACPermission, UserConfirmation.require(ConfirmType.MFA)]
|
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'list': 'assets.view_accountsecret',
|
'list': 'assets.view_accountsecret',
|
||||||
'retrieve': 'assets.view_accountsecret',
|
'retrieve': 'assets.view_accountsecret',
|
||||||
@ -66,8 +76,7 @@ class AccountHistoriesSecretAPI(RecordViewLogMixin, ListAPIView):
|
|||||||
model = Account.history.model
|
model = Account.history.model
|
||||||
serializer_class = serializers.AccountHistorySerializer
|
serializer_class = serializers.AccountHistorySerializer
|
||||||
http_method_names = ['get', 'options']
|
http_method_names = ['get', 'options']
|
||||||
# Todo: 记得打开
|
permission_classes = [RBACPermission, UserConfirmation.require(ConfirmType.MFA)]
|
||||||
# permission_classes = [RBACPermission, UserConfirmation.require(ConfirmType.MFA)]
|
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'list': 'assets.view_accountsecret',
|
'list': 'assets.view_accountsecret',
|
||||||
}
|
}
|
||||||
@ -102,4 +111,5 @@ class AccountTaskCreateAPI(CreateAPIView):
|
|||||||
def get_exception_handler(self):
|
def get_exception_handler(self):
|
||||||
def handler(e, context):
|
def handler(e, context):
|
||||||
return Response({"error": str(e)}, status=400)
|
return Response({"error": str(e)}, status=400)
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
from assets import serializers
|
from assets import serializers
|
||||||
from assets.models import AccountTemplate
|
from assets.models import AccountTemplate
|
||||||
from rbac.permissions import RBACPermission
|
|
||||||
from authentication.const import ConfirmType
|
|
||||||
from common.mixins import RecordViewLogMixin
|
from common.mixins import RecordViewLogMixin
|
||||||
from common.permissions import UserConfirmation
|
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,18 +30,23 @@ __all__ = [
|
|||||||
|
|
||||||
class AssetFilterSet(BaseFilterSet):
|
class AssetFilterSet(BaseFilterSet):
|
||||||
type = django_filters.CharFilter(field_name="platform__type", lookup_expr="exact")
|
type = django_filters.CharFilter(field_name="platform__type", lookup_expr="exact")
|
||||||
category = django_filters.CharFilter(
|
category = django_filters.CharFilter(field_name="platform__category", lookup_expr="exact")
|
||||||
field_name="platform__category", lookup_expr="exact"
|
platform = django_filters.CharFilter(method='filter_platform')
|
||||||
)
|
|
||||||
hostname = django_filters.CharFilter(field_name="name", lookup_expr="exact")
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
fields = [
|
fields = [
|
||||||
"id", "name", "address", "is_active",
|
"id", "name", "address", "is_active",
|
||||||
"type", "category", "hostname"
|
"type", "category", "platform"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def filter_platform(queryset, name, value):
|
||||||
|
if value.isdigit():
|
||||||
|
return queryset.filter(platform_id=value)
|
||||||
|
else:
|
||||||
|
return queryset.filter(platform__name=value)
|
||||||
|
|
||||||
|
|
||||||
class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
|
class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
@ -55,8 +60,9 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
|
|||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
serializer_classes = (
|
serializer_classes = (
|
||||||
("default", serializers.AssetSerializer),
|
("default", serializers.AssetSerializer),
|
||||||
("suggestion", serializers.MiniAssetSerializer),
|
("retrieve", serializers.AssetDetailSerializer),
|
||||||
("platform", serializers.PlatformSerializer),
|
("platform", serializers.PlatformSerializer),
|
||||||
|
("suggestion", serializers.MiniAssetSerializer),
|
||||||
("gateways", serializers.GatewaySerializer),
|
("gateways", serializers.GatewaySerializer),
|
||||||
)
|
)
|
||||||
rbac_perms = (
|
rbac_perms = (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from assets.models import Cloud
|
from assets.models import Cloud, Asset
|
||||||
from assets.serializers import CloudSerializer
|
from assets.serializers import CloudSerializer
|
||||||
|
|
||||||
from .asset import AssetViewSet
|
from .asset import AssetViewSet
|
||||||
@ -8,6 +8,7 @@ __all__ = ['CloudViewSet']
|
|||||||
|
|
||||||
class CloudViewSet(AssetViewSet):
|
class CloudViewSet(AssetViewSet):
|
||||||
model = Cloud
|
model = Cloud
|
||||||
|
perm_model = Asset
|
||||||
|
|
||||||
def get_serializer_classes(self):
|
def get_serializer_classes(self):
|
||||||
serializer_classes = super().get_serializer_classes()
|
serializer_classes = super().get_serializer_classes()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from assets.models import Database
|
from assets.models import Database, Asset
|
||||||
from assets.serializers import DatabaseSerializer
|
from assets.serializers import DatabaseSerializer
|
||||||
|
|
||||||
from .asset import AssetViewSet
|
from .asset import AssetViewSet
|
||||||
@ -8,6 +8,7 @@ __all__ = ['DatabaseViewSet']
|
|||||||
|
|
||||||
class DatabaseViewSet(AssetViewSet):
|
class DatabaseViewSet(AssetViewSet):
|
||||||
model = Database
|
model = Database
|
||||||
|
perm_model = Asset
|
||||||
|
|
||||||
def get_serializer_classes(self):
|
def get_serializer_classes(self):
|
||||||
serializer_classes = super().get_serializer_classes()
|
serializer_classes = super().get_serializer_classes()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
from assets.serializers import DeviceSerializer
|
from assets.serializers import DeviceSerializer
|
||||||
from assets.models import Device
|
from assets.models import Device, Asset
|
||||||
from .asset import AssetViewSet
|
from .asset import AssetViewSet
|
||||||
|
|
||||||
__all__ = ['DeviceViewSet']
|
__all__ = ['DeviceViewSet']
|
||||||
@ -8,6 +7,7 @@ __all__ = ['DeviceViewSet']
|
|||||||
|
|
||||||
class DeviceViewSet(AssetViewSet):
|
class DeviceViewSet(AssetViewSet):
|
||||||
model = Device
|
model = Device
|
||||||
|
perm_model = Asset
|
||||||
|
|
||||||
def get_serializer_classes(self):
|
def get_serializer_classes(self):
|
||||||
serializer_classes = super().get_serializer_classes()
|
serializer_classes = super().get_serializer_classes()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from assets.models import Host
|
from assets.models import Host, Asset
|
||||||
from assets.serializers import HostSerializer
|
from assets.serializers import HostSerializer
|
||||||
from .asset import AssetViewSet
|
from .asset import AssetViewSet
|
||||||
|
|
||||||
@ -7,6 +7,7 @@ __all__ = ['HostViewSet']
|
|||||||
|
|
||||||
class HostViewSet(AssetViewSet):
|
class HostViewSet(AssetViewSet):
|
||||||
model = Host
|
model = Host
|
||||||
|
perm_model = Asset
|
||||||
|
|
||||||
def get_serializer_classes(self):
|
def get_serializer_classes(self):
|
||||||
serializer_classes = super().get_serializer_classes()
|
serializer_classes = super().get_serializer_classes()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from assets.models import Web
|
from assets.models import Web, Asset
|
||||||
from assets.serializers import WebSerializer
|
from assets.serializers import WebSerializer
|
||||||
|
|
||||||
from .asset import AssetViewSet
|
from .asset import AssetViewSet
|
||||||
@ -8,6 +8,7 @@ __all__ = ['WebViewSet']
|
|||||||
|
|
||||||
class WebViewSet(AssetViewSet):
|
class WebViewSet(AssetViewSet):
|
||||||
model = Web
|
model = Web
|
||||||
|
perm_model = Asset
|
||||||
|
|
||||||
def get_serializer_classes(self):
|
def get_serializer_classes(self):
|
||||||
serializer_classes = super().get_serializer_classes()
|
serializer_classes = super().get_serializer_classes()
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework import status, mixins, viewsets
|
from rest_framework import status, mixins, viewsets
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from orgs.mixins import generics
|
|
||||||
from assets import serializers
|
from assets import serializers
|
||||||
from assets.tasks import execute_automation
|
|
||||||
from assets.models import BaseAutomation, AutomationExecution
|
from assets.models import BaseAutomation, AutomationExecution
|
||||||
|
from assets.tasks import execute_automation
|
||||||
from common.const.choices import Trigger
|
from common.const.choices import Trigger
|
||||||
|
from orgs.mixins import generics
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AutomationAssetsListApi', 'AutomationRemoveAssetApi',
|
'AutomationAssetsListApi', 'AutomationRemoveAssetApi',
|
||||||
'AutomationAddAssetApi', 'AutomationNodeAddRemoveApi', 'AutomationExecutionViewSet'
|
'AutomationAddAssetApi', 'AutomationNodeAddRemoveApi',
|
||||||
|
'AutomationExecutionViewSet',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,14 +3,15 @@
|
|||||||
|
|
||||||
from rest_framework import mixins
|
from rest_framework import mixins
|
||||||
|
|
||||||
|
from assets import serializers
|
||||||
|
from assets.models import ChangeSecretAutomation, ChangeSecretRecord, AutomationExecution
|
||||||
from common.utils import get_object_or_none
|
from common.utils import get_object_or_none
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet, OrgGenericViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet, OrgGenericViewSet
|
||||||
|
from .base import AutomationExecutionViewSet
|
||||||
from assets.models import ChangeSecretAutomation, ChangeSecretRecord, AutomationExecution
|
|
||||||
from assets import serializers
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ChangeSecretAutomationViewSet', 'ChangeSecretRecordViewSet'
|
'ChangeSecretAutomationViewSet', 'ChangeSecretRecordViewSet',
|
||||||
|
'ChangSecretExecutionViewSet'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -38,3 +39,11 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
|
|||||||
queryset = queryset.filter(execution=execution)
|
queryset = queryset.filter(execution=execution)
|
||||||
queryset = queryset.order_by('-date_started')
|
queryset = queryset.order_by('-date_started')
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class ChangSecretExecutionViewSet(AutomationExecutionViewSet):
|
||||||
|
rbac_perms = (
|
||||||
|
("list", "assets.view_changesecretexecution"),
|
||||||
|
("retrieve", "assets.view_changesecretexecution"),
|
||||||
|
("create", "assets.add_changesecretexecution"),
|
||||||
|
)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
|
||||||
|
|
||||||
from assets.models import GatherAccountsAutomation
|
|
||||||
from assets import serializers
|
from assets import serializers
|
||||||
|
from assets.models import GatherAccountsAutomation
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from .base import AutomationExecutionViewSet
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'GatherAccountsAutomationViewSet',
|
'GatherAccountsAutomationViewSet', 'GatherAccountsExecutionViewSet'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -16,3 +16,11 @@ class GatherAccountsAutomationViewSet(OrgBulkModelViewSet):
|
|||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
ordering_fields = ('name',)
|
ordering_fields = ('name',)
|
||||||
serializer_class = serializers.GatherAccountAutomationSerializer
|
serializer_class = serializers.GatherAccountAutomationSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class GatherAccountsExecutionViewSet(AutomationExecutionViewSet):
|
||||||
|
rbac_perms = (
|
||||||
|
("list", "assets.view_gatheraccountsexecution"),
|
||||||
|
("retrieve", "assets.view_gatheraccountsexecution"),
|
||||||
|
("create", "assets.add_gatheraccountsexecution"),
|
||||||
|
)
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
|
|
||||||
from orgs.mixins.api import OrgModelViewSet
|
|
||||||
from assets.models import GatheredUser
|
|
||||||
|
|
||||||
from ..serializers import GatheredUserSerializer
|
|
||||||
from ..filters import AssetRelatedByNodeFilterBackend
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['GatheredUserViewSet']
|
|
||||||
|
|
||||||
|
|
||||||
class GatheredUserViewSet(OrgModelViewSet):
|
|
||||||
model = GatheredUser
|
|
||||||
serializer_class = GatheredUserSerializer
|
|
||||||
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
|
|
||||||
|
|
||||||
filterset_fields = ['asset', 'username', 'present', 'asset__address', 'asset__name', 'asset_id']
|
|
||||||
search_fields = ['username', 'asset__address', 'asset__name']
|
|
@ -61,6 +61,7 @@ class SerializeToTreeNodeMixin:
|
|||||||
'meta': {
|
'meta': {
|
||||||
'type': 'asset',
|
'type': 'asset',
|
||||||
'data': {
|
'data': {
|
||||||
|
'platform_type': asset.platform.type,
|
||||||
'org_name': asset.org_name,
|
'org_name': asset.org_name,
|
||||||
'sftp': asset.platform_id in sftp_enabled_platform,
|
'sftp': asset.platform_id in sftp_enabled_platform,
|
||||||
},
|
},
|
||||||
|
@ -1,43 +1,37 @@
|
|||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from functools import partial
|
|
||||||
from collections import namedtuple, defaultdict
|
from collections import namedtuple, defaultdict
|
||||||
from django.core.exceptions import PermissionDenied
|
from functools import partial
|
||||||
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.generics import get_object_or_404
|
|
||||||
from rest_framework.serializers import ValidationError
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.decorators import action
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.db.models.signals import m2m_changed
|
from django.db.models.signals import m2m_changed
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.generics import get_object_or_404
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
from common.const.http import POST
|
|
||||||
from common.exceptions import SomeoneIsDoingThis
|
|
||||||
from common.const.signals import PRE_REMOVE, POST_REMOVE
|
|
||||||
from common.mixins.api import SuggestionMixin
|
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
|
from common.const.http import POST
|
||||||
|
from common.const.signals import PRE_REMOVE, POST_REMOVE
|
||||||
|
from common.exceptions import SomeoneIsDoingThis
|
||||||
|
from common.mixins.api import SuggestionMixin
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.tree import TreeNodeSerializer
|
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
|
||||||
from orgs.mixins import generics
|
from orgs.mixins import generics
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from orgs.utils import current_org
|
from orgs.utils import current_org
|
||||||
|
from .. import serializers
|
||||||
from ..models import Node
|
from ..models import Node
|
||||||
from ..tasks import (
|
from ..tasks import (
|
||||||
update_node_assets_hardware_info_manual,
|
update_node_assets_hardware_info_manual,
|
||||||
test_node_assets_connectivity_manual,
|
test_node_assets_connectivity_manual,
|
||||||
check_node_assets_amount_task
|
check_node_assets_amount_task
|
||||||
)
|
)
|
||||||
from .. import serializers
|
|
||||||
from ..const import AllTypes
|
|
||||||
from .mixin import SerializeToTreeNodeMixin
|
|
||||||
from assets.locks import NodeAddChildrenLock
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi',
|
'NodeViewSet', 'NodeAssetsApi', 'NodeAddAssetsApi',
|
||||||
'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'MoveAssetsToNodeApi',
|
'NodeRemoveAssetsApi', 'MoveAssetsToNodeApi',
|
||||||
'NodeAddChildrenApi', 'NodeListAsTreeApi', 'NodeChildrenAsTreeApi',
|
'NodeAddChildrenApi', 'NodeTaskCreateApi',
|
||||||
'NodeTaskCreateApi', 'CategoryTreeApi',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -74,153 +68,6 @@ class NodeViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
|||||||
return super().destroy(request, *args, **kwargs)
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class NodeListAsTreeApi(generics.ListAPIView):
|
|
||||||
"""
|
|
||||||
获取节点列表树
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "",
|
|
||||||
"name": "",
|
|
||||||
"pId": "",
|
|
||||||
"meta": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
model = Node
|
|
||||||
serializer_class = TreeNodeSerializer
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def to_tree_queryset(queryset):
|
|
||||||
queryset = [node.as_tree_node() for node in queryset]
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
queryset = super().filter_queryset(queryset)
|
|
||||||
queryset = self.to_tree_queryset(queryset)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class NodeChildrenApi(generics.ListCreateAPIView):
|
|
||||||
serializer_class = serializers.NodeSerializer
|
|
||||||
search_fields = ('value',)
|
|
||||||
|
|
||||||
instance = None
|
|
||||||
is_initial = False
|
|
||||||
|
|
||||||
def initial(self, request, *args, **kwargs):
|
|
||||||
self.instance = self.get_object()
|
|
||||||
return super().initial(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
|
||||||
with NodeAddChildrenLock(self.instance):
|
|
||||||
data = serializer.validated_data
|
|
||||||
_id = data.get("id")
|
|
||||||
value = data.get("value")
|
|
||||||
if not value:
|
|
||||||
value = self.instance.get_next_child_preset_name()
|
|
||||||
node = self.instance.create_child(value=value, _id=_id)
|
|
||||||
# 避免查询 full value
|
|
||||||
node._full_value = node.value
|
|
||||||
serializer.instance = node
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
|
|
||||||
key = self.request.query_params.get("key")
|
|
||||||
|
|
||||||
if not pk and not key:
|
|
||||||
self.is_initial = True
|
|
||||||
if current_org.is_root():
|
|
||||||
node = None
|
|
||||||
else:
|
|
||||||
node = Node.org_root()
|
|
||||||
return node
|
|
||||||
if pk:
|
|
||||||
node = get_object_or_404(Node, pk=pk)
|
|
||||||
else:
|
|
||||||
node = get_object_or_404(Node, key=key)
|
|
||||||
return node
|
|
||||||
|
|
||||||
def get_org_root_queryset(self, query_all):
|
|
||||||
if query_all:
|
|
||||||
return Node.objects.all()
|
|
||||||
else:
|
|
||||||
return Node.org_root_nodes()
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
query_all = self.request.query_params.get("all", "0") == "all"
|
|
||||||
|
|
||||||
if self.is_initial and current_org.is_root():
|
|
||||||
return self.get_org_root_queryset(query_all)
|
|
||||||
|
|
||||||
if self.is_initial:
|
|
||||||
with_self = True
|
|
||||||
else:
|
|
||||||
with_self = False
|
|
||||||
|
|
||||||
if not self.instance:
|
|
||||||
return Node.objects.none()
|
|
||||||
|
|
||||||
if query_all:
|
|
||||||
queryset = self.instance.get_all_children(with_self=with_self)
|
|
||||||
else:
|
|
||||||
queryset = self.instance.get_children(with_self=with_self)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
|
|
||||||
"""
|
|
||||||
节点子节点作为树返回,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "",
|
|
||||||
"name": "",
|
|
||||||
"pId": "",
|
|
||||||
"meta": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
"""
|
|
||||||
model = Node
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
if not self.request.GET.get('search'):
|
|
||||||
return queryset
|
|
||||||
queryset = super().filter_queryset(queryset)
|
|
||||||
queryset = self.model.get_ancestor_queryset(queryset)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
|
||||||
nodes = self.filter_queryset(self.get_queryset()).order_by('value')
|
|
||||||
nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
|
||||||
assets = self.get_assets()
|
|
||||||
data = [*nodes, *assets]
|
|
||||||
return Response(data=data)
|
|
||||||
|
|
||||||
def get_assets(self):
|
|
||||||
include_assets = self.request.query_params.get('assets', '0') == '1'
|
|
||||||
if not self.instance or not include_assets:
|
|
||||||
return []
|
|
||||||
assets = self.instance.get_assets().only(
|
|
||||||
"id", "name", "address", "platform_id",
|
|
||||||
"org_id", "is_active",
|
|
||||||
).prefetch_related('platform')
|
|
||||||
return self.serialize_assets(assets, self.instance.key)
|
|
||||||
|
|
||||||
|
|
||||||
class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView):
|
|
||||||
serializer_class = TreeNodeSerializer
|
|
||||||
|
|
||||||
def check_permissions(self, request):
|
|
||||||
if not request.user.has_perm('assets.view_asset'):
|
|
||||||
raise PermissionDenied
|
|
||||||
return True
|
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
|
||||||
nodes = AllTypes.to_tree_nodes()
|
|
||||||
serializer = self.get_serializer(nodes, many=True)
|
|
||||||
return Response(data=serializer.data)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeAssetsApi(generics.ListAPIView):
|
class NodeAssetsApi(generics.ListAPIView):
|
||||||
serializer_class = serializers.AssetSerializer
|
serializer_class = serializers.AssetSerializer
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
from jumpserver.utils import has_valid_xpack_license
|
||||||
from common.drf.api import JMSModelViewSet
|
from common.drf.api import JMSModelViewSet
|
||||||
from common.drf.serializers import GroupedChoiceSerializer
|
from common.drf.serializers import GroupedChoiceSerializer
|
||||||
from assets.models import Platform
|
from assets.models import Platform
|
||||||
|
from assets.const import AllTypes
|
||||||
from assets.serializers import PlatformSerializer
|
from assets.serializers import PlatformSerializer
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['AssetPlatformViewSet']
|
__all__ = ['AssetPlatformViewSet']
|
||||||
|
|
||||||
|
|
||||||
@ -22,6 +22,11 @@ class AssetPlatformViewSet(JMSModelViewSet):
|
|||||||
'ops_methods': 'assets.view_platform'
|
'ops_methods': 'assets.view_platform'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset.filter(type__in=AllTypes.get_types())
|
||||||
|
return queryset
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
pk = self.kwargs.get('pk', '')
|
pk = self.kwargs.get('pk', '')
|
||||||
if pk.isnumeric():
|
if pk.isnumeric():
|
||||||
|
153
apps/assets/api/tree.py
Normal file
153
apps/assets/api/tree.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
|
from rest_framework.generics import get_object_or_404
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from assets.locks import NodeAddChildrenLock
|
||||||
|
from common.tree import TreeNodeSerializer
|
||||||
|
from common.utils import get_logger
|
||||||
|
from orgs.mixins import generics
|
||||||
|
from orgs.utils import current_org
|
||||||
|
from .mixin import SerializeToTreeNodeMixin
|
||||||
|
from .. import serializers
|
||||||
|
from ..const import AllTypes
|
||||||
|
from ..models import Node, Platform, Asset
|
||||||
|
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
__all__ = [
|
||||||
|
'NodeChildrenApi',
|
||||||
|
'NodeChildrenAsTreeApi',
|
||||||
|
'CategoryTreeApi',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class NodeChildrenApi(generics.ListCreateAPIView):
|
||||||
|
"""
|
||||||
|
节点的增删改查
|
||||||
|
"""
|
||||||
|
serializer_class = serializers.NodeSerializer
|
||||||
|
search_fields = ('value',)
|
||||||
|
|
||||||
|
instance = None
|
||||||
|
is_initial = False
|
||||||
|
|
||||||
|
def initial(self, request, *args, **kwargs):
|
||||||
|
self.instance = self.get_object()
|
||||||
|
return super().initial(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
with NodeAddChildrenLock(self.instance):
|
||||||
|
data = serializer.validated_data
|
||||||
|
_id = data.get("id")
|
||||||
|
value = data.get("value")
|
||||||
|
if not value:
|
||||||
|
value = self.instance.get_next_child_preset_name()
|
||||||
|
node = self.instance.create_child(value=value, _id=_id)
|
||||||
|
# 避免查询 full value
|
||||||
|
node._full_value = node.value
|
||||||
|
serializer.instance = node
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
|
||||||
|
key = self.request.query_params.get("key")
|
||||||
|
|
||||||
|
if not pk and not key:
|
||||||
|
self.is_initial = True
|
||||||
|
if current_org.is_root():
|
||||||
|
node = None
|
||||||
|
else:
|
||||||
|
node = Node.org_root()
|
||||||
|
return node
|
||||||
|
if pk:
|
||||||
|
node = get_object_or_404(Node, pk=pk)
|
||||||
|
else:
|
||||||
|
node = get_object_or_404(Node, key=key)
|
||||||
|
return node
|
||||||
|
|
||||||
|
def get_org_root_queryset(self, query_all):
|
||||||
|
if query_all:
|
||||||
|
return Node.objects.all()
|
||||||
|
else:
|
||||||
|
return Node.org_root_nodes()
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
query_all = self.request.query_params.get("all", "0") == "all"
|
||||||
|
|
||||||
|
if self.is_initial and current_org.is_root():
|
||||||
|
return self.get_org_root_queryset(query_all)
|
||||||
|
|
||||||
|
if self.is_initial:
|
||||||
|
with_self = True
|
||||||
|
else:
|
||||||
|
with_self = False
|
||||||
|
|
||||||
|
if not self.instance:
|
||||||
|
return Node.objects.none()
|
||||||
|
|
||||||
|
if query_all:
|
||||||
|
queryset = self.instance.get_all_children(with_self=with_self)
|
||||||
|
else:
|
||||||
|
queryset = self.instance.get_children(with_self=with_self)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
|
||||||
|
"""
|
||||||
|
节点子节点作为树返回,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"name": "",
|
||||||
|
"pId": "",
|
||||||
|
"meta": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
"""
|
||||||
|
model = Node
|
||||||
|
|
||||||
|
def filter_queryset(self, queryset):
|
||||||
|
if not self.request.GET.get('search'):
|
||||||
|
return queryset
|
||||||
|
queryset = super().filter_queryset(queryset)
|
||||||
|
queryset = self.model.get_ancestor_queryset(queryset)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
nodes = self.filter_queryset(self.get_queryset()).order_by('value')
|
||||||
|
nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
||||||
|
assets = self.get_assets_as_node()
|
||||||
|
data = [*nodes, *assets]
|
||||||
|
return Response(data=data)
|
||||||
|
|
||||||
|
def get_assets_as_node(self):
|
||||||
|
include_assets = self.request.query_params.get('assets', '0') == '1'
|
||||||
|
if not self.instance or not include_assets:
|
||||||
|
return []
|
||||||
|
assets = self.instance.get_assets_for_tree()
|
||||||
|
return self.serialize_assets(assets, self.instance.key)
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView):
|
||||||
|
serializer_class = TreeNodeSerializer
|
||||||
|
rbac_perms = {
|
||||||
|
'GET': 'assets.view_asset',
|
||||||
|
'list': 'assets.view_asset',
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_assets(self):
|
||||||
|
key = self.request.query_params.get('key')
|
||||||
|
platform = Platform.objects.filter(id=key).first()
|
||||||
|
if not platform:
|
||||||
|
return []
|
||||||
|
assets = Asset.objects.filter(platform=platform).prefetch_related('platform')
|
||||||
|
return self.serialize_assets(assets, key)
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
include_asset = self.request.query_params.get('assets', '0') == '1'
|
||||||
|
|
||||||
|
if include_asset and self.request.query_params.get('key'):
|
||||||
|
nodes = self.get_assets()
|
||||||
|
else:
|
||||||
|
nodes = AllTypes.to_tree_nodes(include_asset)
|
||||||
|
return Response(data=nodes)
|
@ -49,23 +49,6 @@ class BaseAccountHandler:
|
|||||||
header_fields[field] = str(v.label)
|
header_fields[field] = str(v.label)
|
||||||
return header_fields
|
return header_fields
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load_auth(tp, value, system_user):
|
|
||||||
if value:
|
|
||||||
return value
|
|
||||||
if system_user:
|
|
||||||
return getattr(system_user, tp, '')
|
|
||||||
return ''
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def replace_auth(cls, account, system_user_dict):
|
|
||||||
system_user = system_user_dict.get(account.systemuser_id)
|
|
||||||
account.username = cls.load_auth('username', account.username, system_user)
|
|
||||||
account.password = cls.load_auth('password', account.password, system_user)
|
|
||||||
account.private_key = cls.load_auth('private_key', account.private_key, system_user)
|
|
||||||
account.public_key = cls.load_auth('public_key', account.public_key, system_user)
|
|
||||||
return account
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_row(cls, data, header_fields):
|
def create_row(cls, data, header_fields):
|
||||||
data = cls.unpack_data(data)
|
data = cls.unpack_data(data)
|
||||||
@ -94,30 +77,30 @@ class AssetAccountHandler(BaseAccountHandler):
|
|||||||
return filename
|
return filename
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_data_map(cls, categories: list):
|
def create_data_map(cls, types: list):
|
||||||
data_map = defaultdict(list)
|
data_map = defaultdict(list)
|
||||||
|
|
||||||
# TODO 可以优化一下查询 在账号上做 category 的缓存 避免数据量大时连表操作
|
# TODO 可以优化一下查询 在账号上做 category 的缓存 避免数据量大时连表操作
|
||||||
qs = Account.objects.filter(
|
qs = Account.objects.filter(
|
||||||
asset__platform__type__in=categories
|
asset__platform__type__in=types
|
||||||
).annotate(category=F('asset__platform__type'))
|
).annotate(type=F('asset__platform__type'))
|
||||||
print(qs, categories)
|
|
||||||
if not qs.exists():
|
if not qs.exists():
|
||||||
return data_map
|
return data_map
|
||||||
|
|
||||||
category_dict = {}
|
type_dict = {}
|
||||||
for i in AllTypes.grouped_choices_to_objs():
|
for i in AllTypes.grouped_choices_to_objs():
|
||||||
for j in i['children']:
|
for j in i['children']:
|
||||||
category_dict[j['value']] = j['display_name']
|
type_dict[j['value']] = j['display_name']
|
||||||
|
|
||||||
header_fields = cls.get_header_fields(AccountSecretSerializer(qs.first()))
|
header_fields = cls.get_header_fields(AccountSecretSerializer(qs.first()))
|
||||||
account_category_map = defaultdict(list)
|
account_type_map = defaultdict(list)
|
||||||
for account in qs:
|
for account in qs:
|
||||||
account_category_map[account.category].append(account)
|
account_type_map[account.type].append(account)
|
||||||
|
|
||||||
data_map = {}
|
data_map = {}
|
||||||
for category, accounts in account_category_map.items():
|
for tp, accounts in account_type_map.items():
|
||||||
sheet_name = category_dict.get(category, category)
|
sheet_name = type_dict.get(tp, tp)
|
||||||
data = AccountSecretSerializer(accounts, many=True).data
|
data = AccountSecretSerializer(accounts, many=True).data
|
||||||
data_map.update(cls.add_rows(data, header_fields, sheet_name))
|
data_map.update(cls.add_rows(data, header_fields, sheet_name))
|
||||||
|
|
||||||
@ -140,9 +123,9 @@ class AccountBackupHandler:
|
|||||||
# Print task start date
|
# Print task start date
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
files = []
|
files = []
|
||||||
categories = self.execution.categories
|
types = self.execution.types
|
||||||
|
|
||||||
data_map = AssetAccountHandler.create_data_map(categories)
|
data_map = AssetAccountHandler.create_data_map(types)
|
||||||
if not data_map:
|
if not data_map:
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import os
|
import os
|
||||||
import yaml
|
|
||||||
import shutil
|
import shutil
|
||||||
from hashlib import md5
|
|
||||||
from copy import deepcopy
|
|
||||||
from socket import gethostname
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from copy import deepcopy
|
||||||
|
from hashlib import md5
|
||||||
|
from socket import gethostname
|
||||||
|
|
||||||
|
import yaml
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from common.utils import get_logger
|
|
||||||
from common.utils import ssh_pubkey_gen, ssh_key_string_to_obj
|
|
||||||
from assets.const import SecretType
|
|
||||||
from assets.automations.methods import platform_automation_methods
|
from assets.automations.methods import platform_automation_methods
|
||||||
|
from assets.const import SecretType
|
||||||
|
from common.utils import get_logger, lazyproperty
|
||||||
|
from common.utils import ssh_pubkey_gen, ssh_key_string_to_obj
|
||||||
from ops.ansible import JMSInventory, PlaybookRunner, DefaultCallback
|
from ops.ansible import JMSInventory, PlaybookRunner, DefaultCallback
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
@ -93,7 +93,7 @@ class BasePlaybookManager:
|
|||||||
def get_assets_group_by_platform(self):
|
def get_assets_group_by_platform(self):
|
||||||
return self.automation.all_assets_group_by_platform()
|
return self.automation.all_assets_group_by_platform()
|
||||||
|
|
||||||
@property
|
@lazyproperty
|
||||||
def runtime_dir(self):
|
def runtime_dir(self):
|
||||||
ansible_dir = settings.ANSIBLE_DIR
|
ansible_dir = settings.ANSIBLE_DIR
|
||||||
dir_name = '{}_{}'.format(self.automation.name.replace(' ', '_'), self.execution.id)
|
dir_name = '{}_{}'.format(self.automation.name.replace(' ', '_'), self.execution.id)
|
||||||
@ -105,11 +105,6 @@ class BasePlaybookManager:
|
|||||||
os.makedirs(path, exist_ok=True, mode=0o755)
|
os.makedirs(path, exist_ok=True, mode=0o755)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def prepare_playbook_dir(self):
|
|
||||||
for d in [self.runtime_dir]:
|
|
||||||
if not os.path.exists(d):
|
|
||||||
os.makedirs(d, exist_ok=True, mode=0o755)
|
|
||||||
|
|
||||||
def host_callback(self, host, automation=None, **kwargs):
|
def host_callback(self, host, automation=None, **kwargs):
|
||||||
enabled_attr = '{}_enabled'.format(self.__class__.method_type())
|
enabled_attr = '{}_enabled'.format(self.__class__.method_type())
|
||||||
method_attr = '{}_method'.format(self.__class__.method_type())
|
method_attr = '{}_method'.format(self.__class__.method_type())
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from assets.const import AutomationTypes
|
from assets.const import AutomationTypes, Source
|
||||||
from orgs.utils import tmp_to_org
|
from orgs.utils import tmp_to_org
|
||||||
from .filter import GatherAccountsFilter
|
from .filter import GatherAccountsFilter
|
||||||
from ...models import GatheredUser
|
|
||||||
from ..base.manager import BasePlaybookManager
|
from ..base.manager import BasePlaybookManager
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
@ -26,20 +27,33 @@ class GatherAccountsManager(BasePlaybookManager):
|
|||||||
result = GatherAccountsFilter(host).run(self.method_id_meta_mapper, result)
|
result = GatherAccountsFilter(host).run(self.method_id_meta_mapper, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def bulk_create_accounts(asset, result):
|
||||||
|
account_objs = []
|
||||||
|
account_model = asset.accounts.model
|
||||||
|
account_usernames = set(asset.accounts.values_list('username', flat=True))
|
||||||
|
with tmp_to_org(asset.org_id):
|
||||||
|
accounts_dict = {}
|
||||||
|
for username, data in result.items():
|
||||||
|
comment = ''
|
||||||
|
d = {'asset': asset, 'username': username, 'name': username, 'source': Source.COLLECTED}
|
||||||
|
if data.get('date'):
|
||||||
|
comment += f"{_('Date last login')}: {data['date']}\n "
|
||||||
|
if data.get('address'):
|
||||||
|
comment += f"{_('IP last login')}: {data['address'][:32]}"
|
||||||
|
d['comment'] = comment
|
||||||
|
accounts_dict[username] = d
|
||||||
|
for username, data in accounts_dict.items():
|
||||||
|
if username in account_usernames:
|
||||||
|
continue
|
||||||
|
account_objs.append(account_model(**data))
|
||||||
|
account_model.objects.bulk_create(account_objs)
|
||||||
|
|
||||||
def on_host_success(self, host, result):
|
def on_host_success(self, host, result):
|
||||||
info = result.get('debug', {}).get('res', {}).get('info', {})
|
info = result.get('debug', {}).get('res', {}).get('info', {})
|
||||||
asset = self.host_asset_mapper.get(host)
|
asset = self.host_asset_mapper.get(host)
|
||||||
org_id = asset.org_id
|
|
||||||
if asset and info:
|
if asset and info:
|
||||||
result = self.filter_success_result(host, info)
|
result = self.filter_success_result(host, info)
|
||||||
with tmp_to_org(org_id):
|
self.bulk_create_accounts(asset, result)
|
||||||
GatheredUser.objects.filter(asset=asset, present=True).update(present=False)
|
|
||||||
for username, data in result.items():
|
|
||||||
defaults = {'asset': asset, 'present': True, 'username': username}
|
|
||||||
if data.get('date'):
|
|
||||||
defaults['date_last_login'] = data['date']
|
|
||||||
if data.get('address'):
|
|
||||||
defaults['ip_last_login'] = data['address'][:32]
|
|
||||||
GatheredUser.objects.update_or_create(defaults=defaults, asset=asset, username=username)
|
|
||||||
else:
|
else:
|
||||||
logger.error("Not found info".format(host))
|
logger.error("Not found info".format(host))
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
- hosts: mongodb
|
|
||||||
gather_facts: no
|
|
||||||
vars:
|
|
||||||
ansible_python_interpreter: /usr/local/bin/python
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Add user account.username
|
|
||||||
mongodb_user:
|
|
||||||
login_user: "{{ jms_account.username }}"
|
|
||||||
login_password: "{{ jms_account.secret }}"
|
|
||||||
login_host: "{{ jms_asset.address }}"
|
|
||||||
login_port: "{{ jms_asset.port }}"
|
|
||||||
login_database: "{{ jms_asset.specific.db_name }}"
|
|
||||||
db: "{{ jms_asset.specific.db_name }}"
|
|
||||||
name: "{{ account.username }}"
|
|
||||||
password: "{{ account.secret }}"
|
|
@ -1,6 +0,0 @@
|
|||||||
id: push_account_mongodb
|
|
||||||
name: Push account from MongoDB
|
|
||||||
category: database
|
|
||||||
type:
|
|
||||||
- mongodb
|
|
||||||
method: push_account
|
|
@ -1,15 +0,0 @@
|
|||||||
- hosts: mysql
|
|
||||||
gather_facts: no
|
|
||||||
vars:
|
|
||||||
ansible_python_interpreter: /usr/local/bin/python
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Add user account.username
|
|
||||||
community.mysql.mysql_user:
|
|
||||||
login_user: "{{ jms_account.username }}"
|
|
||||||
login_password: "{{ jms_account.secret }}"
|
|
||||||
login_host: "{{ jms_asset.address }}"
|
|
||||||
login_port: "{{ jms_asset.port }}"
|
|
||||||
name: "{{ account.username }}"
|
|
||||||
password: "{{ account.secret }}"
|
|
||||||
host: "%"
|
|
@ -1,6 +0,0 @@
|
|||||||
id: push_account_mysql
|
|
||||||
name: Push account from MySQL
|
|
||||||
category: database
|
|
||||||
type:
|
|
||||||
- mysql
|
|
||||||
method: push_account
|
|
@ -1,16 +0,0 @@
|
|||||||
- hosts: oracle
|
|
||||||
gather_facts: no
|
|
||||||
vars:
|
|
||||||
ansible_python_interpreter: /usr/local/bin/python
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Add user account.username
|
|
||||||
oracle_user:
|
|
||||||
login_user: "{{ jms_account.username }}"
|
|
||||||
login_password: "{{ jms_account.secret }}"
|
|
||||||
login_host: "{{ jms_asset.address }}"
|
|
||||||
login_port: "{{ jms_asset.port }}"
|
|
||||||
login_database: "{{ jms_asset.specific.db_name }}"
|
|
||||||
mode: "{{ jms_account.mode }}"
|
|
||||||
name: "{{ account.username }}"
|
|
||||||
password: "{{ account.secret }}"
|
|
@ -1,6 +0,0 @@
|
|||||||
id: push_account_oracle
|
|
||||||
name: Push account from Oracle
|
|
||||||
category: database
|
|
||||||
type:
|
|
||||||
- oracle
|
|
||||||
method: push_account
|
|
@ -1,16 +0,0 @@
|
|||||||
- hosts: postgresql
|
|
||||||
gather_facts: no
|
|
||||||
vars:
|
|
||||||
ansible_python_interpreter: /usr/local/bin/python
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Add user account.username
|
|
||||||
community.postgresql.postgresql_user:
|
|
||||||
login_user: "{{ jms_account.username }}"
|
|
||||||
login_password: "{{ jms_account.secret }}"
|
|
||||||
login_host: "{{ jms_asset.address }}"
|
|
||||||
login_port: "{{ jms_asset.port }}"
|
|
||||||
db: "{{ jms_asset.specific.db_name }}"
|
|
||||||
name: "{{ account.username }}"
|
|
||||||
password: "{{ account.secret }}"
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
id: push_account_postgresql
|
|
||||||
name: Push account for PostgreSQL
|
|
||||||
category: database
|
|
||||||
type:
|
|
||||||
- postgresql
|
|
||||||
method: push_account
|
|
@ -1,19 +0,0 @@
|
|||||||
- hosts: demo
|
|
||||||
gather_facts: no
|
|
||||||
tasks:
|
|
||||||
- name: Add user account.username
|
|
||||||
ansible.builtin.user:
|
|
||||||
name: "{{ account.username }}"
|
|
||||||
|
|
||||||
- name: Set account.username password
|
|
||||||
ansible.builtin.user:
|
|
||||||
name: "{{ account.username }}"
|
|
||||||
password: "{{ account.secret | password_hash('sha512') }}"
|
|
||||||
update_password: always
|
|
||||||
when: secret_type == "password"
|
|
||||||
|
|
||||||
- name: Set account.username SSH key
|
|
||||||
ansible.builtin.authorized_key:
|
|
||||||
user: "{{ account.username }}"
|
|
||||||
key: "{{ account.secret }}"
|
|
||||||
when: secret_type == "ssh_key"
|
|
@ -1,7 +0,0 @@
|
|||||||
id: push_account_posix
|
|
||||||
name: Push posix account
|
|
||||||
category: host
|
|
||||||
type:
|
|
||||||
- linux
|
|
||||||
- unix
|
|
||||||
method: push_account
|
|
@ -1,13 +0,0 @@
|
|||||||
- hosts: windows
|
|
||||||
gather_facts: yes
|
|
||||||
tasks:
|
|
||||||
- name: Add user account.username
|
|
||||||
ansible.windows.win_user:
|
|
||||||
vars:
|
|
||||||
fullname: "{{ account.username }}"
|
|
||||||
name: "{{ account.username }}"
|
|
||||||
password: "{{ account.secret }}"
|
|
||||||
state: present
|
|
||||||
password_expired: no
|
|
||||||
update_password: always
|
|
||||||
password_never_expires: yes
|
|
@ -1,7 +0,0 @@
|
|||||||
id: push_account_windows
|
|
||||||
name: Push account windows
|
|
||||||
version: 1
|
|
||||||
method: push_account
|
|
||||||
category: host
|
|
||||||
type:
|
|
||||||
- windows
|
|
@ -13,3 +13,14 @@ class SecretType(TextChoices):
|
|||||||
SSH_KEY = 'ssh_key', _('SSH key')
|
SSH_KEY = 'ssh_key', _('SSH key')
|
||||||
ACCESS_KEY = 'access_key', _('Access key')
|
ACCESS_KEY = 'access_key', _('Access key')
|
||||||
TOKEN = 'token', _('Token')
|
TOKEN = 'token', _('Token')
|
||||||
|
|
||||||
|
|
||||||
|
class AliasAccount(TextChoices):
|
||||||
|
ALL = '@ALL', _('All')
|
||||||
|
INPUT = '@INPUT', _('Manual input')
|
||||||
|
USER = '@USER', _('Dynamic user')
|
||||||
|
|
||||||
|
|
||||||
|
class Source(TextChoices):
|
||||||
|
LOCAL = 'local', _('Local')
|
||||||
|
COLLECTED = 'collected', _('Collected')
|
||||||
|
@ -12,7 +12,7 @@ DEFAULT_PASSWORD_RULES = {
|
|||||||
class AutomationTypes(TextChoices):
|
class AutomationTypes(TextChoices):
|
||||||
ping = 'ping', _('Ping')
|
ping = 'ping', _('Ping')
|
||||||
gather_facts = 'gather_facts', _('Gather facts')
|
gather_facts = 'gather_facts', _('Gather facts')
|
||||||
push_account = 'push_account', _('Create account')
|
push_account = 'push_account', _('Push account')
|
||||||
change_secret = 'change_secret', _('Change secret')
|
change_secret = 'change_secret', _('Change secret')
|
||||||
verify_account = 'verify_account', _('Verify account')
|
verify_account = 'verify_account', _('Verify account')
|
||||||
gather_accounts = 'gather_accounts', _('Gather accounts')
|
gather_accounts = 'gather_accounts', _('Gather accounts')
|
||||||
@ -20,8 +20,9 @@ class AutomationTypes(TextChoices):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_type_model(cls, tp):
|
def get_type_model(cls, tp):
|
||||||
from assets.models import (
|
from assets.models import (
|
||||||
PingAutomation, GatherFactsAutomation, PushAccountAutomation,
|
PingAutomation, GatherFactsAutomation,
|
||||||
ChangeSecretAutomation, VerifyAccountAutomation, GatherAccountsAutomation,
|
PushAccountAutomation, ChangeSecretAutomation,
|
||||||
|
VerifyAccountAutomation, GatherAccountsAutomation,
|
||||||
)
|
)
|
||||||
type_model_dict = {
|
type_model_dict = {
|
||||||
cls.ping: PingAutomation,
|
cls.ping: PingAutomation,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.db.models import TextChoices
|
from django.db.models import TextChoices
|
||||||
|
|
||||||
|
from jumpserver.utils import has_valid_xpack_license
|
||||||
from .protocol import Protocol
|
from .protocol import Protocol
|
||||||
|
|
||||||
|
|
||||||
@ -53,3 +54,25 @@ class BaseType(TextChoices):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def internal_platforms(cls):
|
def internal_platforms(cls):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_community_types(cls):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_types(cls):
|
||||||
|
tps = [tp for tp in cls]
|
||||||
|
if not has_valid_xpack_license():
|
||||||
|
tps = cls.get_community_types()
|
||||||
|
return tps
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_choices(cls):
|
||||||
|
tps = cls.get_types()
|
||||||
|
cls_choices = cls.choices
|
||||||
|
return [
|
||||||
|
choice for choice in cls_choices
|
||||||
|
if choice[0] in tps
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ class CloudTypes(BaseType):
|
|||||||
'gather_facts_enabled': False,
|
'gather_facts_enabled': False,
|
||||||
'verify_account_enabled': False,
|
'verify_account_enabled': False,
|
||||||
'change_secret_enabled': False,
|
'change_secret_enabled': False,
|
||||||
'push_account_enabled': False,
|
|
||||||
'gather_accounts_enabled': False,
|
'gather_accounts_enabled': False,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,3 +48,9 @@ class CloudTypes(BaseType):
|
|||||||
cls.PRIVATE: [{'name': 'Vmware-vSphere'}],
|
cls.PRIVATE: [{'name': 'Vmware-vSphere'}],
|
||||||
cls.K8S: [{'name': 'Kubernetes'}],
|
cls.K8S: [{'name': 'Kubernetes'}],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_community_types(cls):
|
||||||
|
return [
|
||||||
|
cls.K8S, cls.PUBLIC, cls.PRIVATE
|
||||||
|
]
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
from .base import BaseType
|
from .base import BaseType
|
||||||
|
|
||||||
|
|
||||||
@ -34,7 +33,6 @@ class DatabaseTypes(BaseType):
|
|||||||
'gather_accounts_enabled': True,
|
'gather_accounts_enabled': True,
|
||||||
'verify_account_enabled': True,
|
'verify_account_enabled': True,
|
||||||
'change_secret_enabled': True,
|
'change_secret_enabled': True,
|
||||||
'push_account_enabled': True,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return constrains
|
return constrains
|
||||||
@ -62,3 +60,8 @@ class DatabaseTypes(BaseType):
|
|||||||
cls.REDIS: [{'name': 'Redis'}],
|
cls.REDIS: [{'name': 'Redis'}],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_community_types(cls):
|
||||||
|
return [
|
||||||
|
cls.MYSQL, cls.MARIADB, cls.MONGODB, cls.REDIS
|
||||||
|
]
|
||||||
|
@ -40,7 +40,6 @@ class DeviceTypes(BaseType):
|
|||||||
'gather_accounts_enabled': False,
|
'gather_accounts_enabled': False,
|
||||||
'verify_account_enabled': False,
|
'verify_account_enabled': False,
|
||||||
'change_secret_enabled': False,
|
'change_secret_enabled': False,
|
||||||
'push_account_enabled': False,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,3 +51,9 @@ class DeviceTypes(BaseType):
|
|||||||
cls.ROUTER: [],
|
cls.ROUTER: [],
|
||||||
cls.FIREWALL: []
|
cls.FIREWALL: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_community_types(cls):
|
||||||
|
return [
|
||||||
|
cls.GENERAL, cls.SWITCH, cls.ROUTER, cls.FIREWALL
|
||||||
|
]
|
||||||
|
@ -34,7 +34,7 @@ class HostTypes(BaseType):
|
|||||||
def _get_protocol_constrains(cls) -> dict:
|
def _get_protocol_constrains(cls) -> dict:
|
||||||
return {
|
return {
|
||||||
'*': {
|
'*': {
|
||||||
'choices': ['ssh', 'telnet', 'vnc', 'rdp']
|
'choices': ['ssh', 'telnet', 'vnc', 'rdp']
|
||||||
},
|
},
|
||||||
cls.WINDOWS: {
|
cls.WINDOWS: {
|
||||||
'choices': ['rdp', 'ssh', 'vnc']
|
'choices': ['rdp', 'ssh', 'vnc']
|
||||||
@ -54,7 +54,6 @@ class HostTypes(BaseType):
|
|||||||
'gather_accounts_enabled': True,
|
'gather_accounts_enabled': True,
|
||||||
'verify_account_enabled': True,
|
'verify_account_enabled': True,
|
||||||
'change_secret_enabled': True,
|
'change_secret_enabled': True,
|
||||||
'push_account_enabled': True,
|
|
||||||
},
|
},
|
||||||
cls.WINDOWS: {
|
cls.WINDOWS: {
|
||||||
'ansible_config': {
|
'ansible_config': {
|
||||||
@ -76,7 +75,6 @@ class HostTypes(BaseType):
|
|||||||
{'name': 'macOS'},
|
{'name': 'macOS'},
|
||||||
{'name': 'BSD'},
|
{'name': 'BSD'},
|
||||||
{'name': 'AIX', 'automation': {
|
{'name': 'AIX', 'automation': {
|
||||||
'push_account_method': 'push_account_aix',
|
|
||||||
'change_secret_method': 'push_secret_aix'
|
'change_secret_method': 'push_secret_aix'
|
||||||
}}
|
}}
|
||||||
],
|
],
|
||||||
@ -97,7 +95,7 @@ class HostTypes(BaseType):
|
|||||||
{
|
{
|
||||||
'name': 'RemoteAppHost',
|
'name': 'RemoteAppHost',
|
||||||
'_protocols': ['rdp', 'ssh'],
|
'_protocols': ['rdp', 'ssh'],
|
||||||
'protocols_setting': {
|
'protocols_setting': {
|
||||||
'ssh': {
|
'ssh': {
|
||||||
'required': True
|
'required': True
|
||||||
}
|
}
|
||||||
@ -106,3 +104,9 @@ class HostTypes(BaseType):
|
|||||||
],
|
],
|
||||||
cls.OTHER_HOST: []
|
cls.OTHER_HOST: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_community_types(cls) -> list:
|
||||||
|
return [
|
||||||
|
cls.LINUX, cls.UNIX, cls.WINDOWS, cls.OTHER_HOST
|
||||||
|
]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from common.db.models import ChoicesMixin
|
from common.db.models import ChoicesMixin
|
||||||
|
|
||||||
__all__ = ['Protocol']
|
__all__ = ['Protocol']
|
||||||
@ -102,9 +103,9 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
|||||||
'port': 80,
|
'port': 80,
|
||||||
'secret_types': ['password'],
|
'secret_types': ['password'],
|
||||||
'setting': {
|
'setting': {
|
||||||
'username_selector': 'input[type=text]',
|
'username_selector': 'name=username',
|
||||||
'password_selector': 'input[type=password]',
|
'password_selector': 'name=password',
|
||||||
'submit_selector': 'button[type=submit]',
|
'submit_selector': 'id=longin_button',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -112,7 +113,7 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def settings(cls):
|
def settings(cls):
|
||||||
return {
|
return {
|
||||||
**cls.device_protocols(),
|
**cls.device_protocols(),
|
||||||
**cls.database_protocols(),
|
**cls.database_protocols(),
|
||||||
**cls.cloud_protocols()
|
**cls.cloud_protocols()
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
|
from collections import defaultdict
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from common.db.models import ChoicesMixin
|
from django.utils.translation import gettext as _
|
||||||
from common.tree import TreeNode
|
|
||||||
|
|
||||||
|
from common.db.models import ChoicesMixin
|
||||||
from .category import Category
|
from .category import Category
|
||||||
from .host import HostTypes
|
|
||||||
from .device import DeviceTypes
|
|
||||||
from .database import DatabaseTypes
|
|
||||||
from .web import WebTypes
|
|
||||||
from .cloud import CloudTypes
|
from .cloud import CloudTypes
|
||||||
|
from .database import DatabaseTypes
|
||||||
|
from .device import DeviceTypes
|
||||||
|
from .host import HostTypes
|
||||||
|
from .web import WebTypes
|
||||||
|
|
||||||
|
|
||||||
class AllTypes(ChoicesMixin):
|
class AllTypes(ChoicesMixin):
|
||||||
@ -54,7 +55,7 @@ class AllTypes(ChoicesMixin):
|
|||||||
item_name = item.replace('_enabled', '')
|
item_name = item.replace('_enabled', '')
|
||||||
methods = filter_platform_methods(category, tp, item_name)
|
methods = filter_platform_methods(category, tp, item_name)
|
||||||
methods = [{'name': m['name'], 'id': m['id']} for m in methods]
|
methods = [{'name': m['name'], 'id': m['id']} for m in methods]
|
||||||
automation_methods[item_name+'_methods'] = methods
|
automation_methods[item_name + '_methods'] = methods
|
||||||
automation.update(automation_methods)
|
automation.update(automation_methods)
|
||||||
constraints['automation'] = automation
|
constraints['automation'] = automation
|
||||||
return constraints
|
return constraints
|
||||||
@ -62,14 +63,18 @@ class AllTypes(ChoicesMixin):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def types(cls, with_constraints=True):
|
def types(cls, with_constraints=True):
|
||||||
types = []
|
types = []
|
||||||
for category, tps in cls.category_types():
|
for category, type_cls in cls.category_types():
|
||||||
|
tps = type_cls.get_types()
|
||||||
types.extend([cls.serialize_type(category, tp, with_constraints) for tp in tps])
|
types.extend([cls.serialize_type(category, tp, with_constraints) for tp in tps])
|
||||||
return types
|
return types
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def categories(cls, with_constraints=True):
|
def categories(cls, with_constraints=True):
|
||||||
categories = []
|
categories = []
|
||||||
for category, tps in cls.category_types():
|
for category, type_cls in cls.category_types():
|
||||||
|
tps = type_cls.get_types()
|
||||||
|
if not tps:
|
||||||
|
continue
|
||||||
category_data = {
|
category_data = {
|
||||||
'value': category.value,
|
'value': category.value,
|
||||||
'label': category.label,
|
'label': category.label,
|
||||||
@ -121,30 +126,82 @@ class AllTypes(ChoicesMixin):
|
|||||||
(Category.CLOUD, CloudTypes)
|
(Category.CLOUD, CloudTypes)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_types(cls):
|
||||||
|
tps = []
|
||||||
|
for i in dict(cls.category_types()).values():
|
||||||
|
tps.extend(i.get_types())
|
||||||
|
return tps
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def choice_to_node(choice, pid, opened=True, is_parent=True, meta=None):
|
def choice_to_node(choice, pid, opened=True, is_parent=True, meta=None):
|
||||||
node = TreeNode(**{
|
node = {
|
||||||
'id': choice.name,
|
'id': pid + '_' + choice.name,
|
||||||
'name': choice.label,
|
'name': choice.label,
|
||||||
'title': choice.label,
|
'title': choice.label,
|
||||||
'pId': pid,
|
'pId': pid,
|
||||||
'open': opened,
|
'open': opened,
|
||||||
'isParent': is_parent,
|
'isParent': is_parent,
|
||||||
})
|
}
|
||||||
if meta:
|
if meta:
|
||||||
node.meta = meta
|
node['meta'] = meta
|
||||||
return node
|
return node
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_tree_nodes(cls):
|
def platform_to_node(cls, p, pid, include_asset):
|
||||||
root = TreeNode(id='ROOT', name='类型节点', title='类型节点')
|
node = {
|
||||||
|
'id': '{}'.format(p.id),
|
||||||
|
'name': p.name,
|
||||||
|
'title': p.name,
|
||||||
|
'pId': pid,
|
||||||
|
'isParent': include_asset,
|
||||||
|
'meta': {
|
||||||
|
'type': 'platform'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_tree_nodes(cls, include_asset):
|
||||||
|
from ..models import Asset, Platform
|
||||||
|
asset_platforms = Asset.objects.all().values_list('platform_id', flat=True)
|
||||||
|
platform_count = defaultdict(int)
|
||||||
|
for platform_id in asset_platforms:
|
||||||
|
platform_count[platform_id] += 1
|
||||||
|
|
||||||
|
category_type_mapper = defaultdict(int)
|
||||||
|
platforms = Platform.objects.all()
|
||||||
|
tp_platforms = defaultdict(list)
|
||||||
|
|
||||||
|
for p in platforms:
|
||||||
|
category_type_mapper[p.category + '_' + p.type] += platform_count[p.id]
|
||||||
|
category_type_mapper[p.category] += platform_count[p.id]
|
||||||
|
tp_platforms[p.category + '_' + p.type].append(p)
|
||||||
|
|
||||||
|
root = dict(id='ROOT', name=_('All types'), title='所有类型', open=True, isParent=True)
|
||||||
nodes = [root]
|
nodes = [root]
|
||||||
for category, types in cls.category_types():
|
for category, type_cls in cls.category_types():
|
||||||
category_node = cls.choice_to_node(category, 'ROOT', meta={'type': 'category'})
|
# Category 格式化
|
||||||
|
meta = {'type': 'category', 'category': category.value}
|
||||||
|
category_node = cls.choice_to_node(category, 'ROOT', meta=meta)
|
||||||
|
category_count = category_type_mapper.get(category, 0)
|
||||||
|
category_node['name'] += f'({category_count})'
|
||||||
nodes.append(category_node)
|
nodes.append(category_node)
|
||||||
|
|
||||||
|
# Type 格式化
|
||||||
|
types = type_cls.get_types()
|
||||||
for tp in types:
|
for tp in types:
|
||||||
tp_node = cls.choice_to_node(tp, category_node.id, meta={'type': 'type'})
|
meta = {'type': 'type', 'category': category.value, '_type': tp.value}
|
||||||
|
tp_node = cls.choice_to_node(tp, category_node['id'], opened=False, meta=meta)
|
||||||
|
tp_count = category_type_mapper.get(category + '_' + tp, 0)
|
||||||
|
tp_node['name'] += f'({tp_count})'
|
||||||
nodes.append(tp_node)
|
nodes.append(tp_node)
|
||||||
|
|
||||||
|
# Platform 格式化
|
||||||
|
for p in tp_platforms.get(category + '_' + tp, []):
|
||||||
|
platform_node = cls.platform_to_node(p, tp_node['id'], include_asset)
|
||||||
|
platform_node['name'] += f'({platform_count.get(p.id, 0)})'
|
||||||
|
nodes.append(platform_node)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -253,8 +310,3 @@ class AllTypes(ChoicesMixin):
|
|||||||
print("\t- Update platform: {}".format(platform.name))
|
print("\t- Update platform: {}".format(platform.name))
|
||||||
platform_data = cls.get_type_default_platform(platform.category, platform.type)
|
platform_data = cls.get_type_default_platform(platform.category, platform.type)
|
||||||
cls.create_or_update_by_platform_data(platform.name, platform_data)
|
cls.create_or_update_by_platform_data(platform.name, platform_data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,3 +44,9 @@ class WebTypes(BaseType):
|
|||||||
{'name': 'Website'},
|
{'name': 'Website'},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_community_types(cls):
|
||||||
|
return [
|
||||||
|
cls.WEBSITE,
|
||||||
|
]
|
||||||
|
@ -126,17 +126,6 @@ class LabelFilterBackend(filters.BaseFilterBackend):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class AssetRelatedByNodeFilterBackend(AssetByNodeFilterBackend):
|
|
||||||
def filter_node_related_all(self, queryset, node):
|
|
||||||
return queryset.filter(
|
|
||||||
Q(asset__nodes__key__istartswith=f'{node.key}:') |
|
|
||||||
Q(asset__nodes__key=node.key)
|
|
||||||
).distinct()
|
|
||||||
|
|
||||||
def filter_node_related_direct(self, queryset, node):
|
|
||||||
return queryset.filter(asset__nodes__key=node.key).distinct()
|
|
||||||
|
|
||||||
|
|
||||||
class IpInFilterBackend(filters.BaseFilterBackend):
|
class IpInFilterBackend(filters.BaseFilterBackend):
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
ips = request.query_params.get('ips')
|
ips = request.query_params.get('ips')
|
||||||
|
@ -12,7 +12,6 @@ def migrate_platform_type_to_lower(apps, *args):
|
|||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('assets', '0094_auto_20220402_1736'),
|
('assets', '0094_auto_20220402_1736'),
|
||||||
]
|
]
|
||||||
@ -51,7 +50,7 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='platform',
|
model_name='platform',
|
||||||
name='su_method',
|
name='su_method',
|
||||||
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='SU method'),
|
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Su method'),
|
||||||
),
|
),
|
||||||
migrations.RunPython(migrate_platform_type_to_lower)
|
migrations.RunPython(migrate_platform_type_to_lower)
|
||||||
]
|
]
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
# Generated by Django 3.1.14 on 2022-04-26 07:54
|
# Generated by Django 3.1.14 on 2022-04-26 07:54
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('assets', '0095_auto_20220407_1726'),
|
('assets', '0095_auto_20220407_1726'),
|
||||||
]
|
]
|
||||||
@ -18,7 +16,8 @@ class Migration(migrations.Migration):
|
|||||||
('name', models.CharField(max_length=32, verbose_name='Name')),
|
('name', models.CharField(max_length=32, verbose_name='Name')),
|
||||||
('port', models.IntegerField(verbose_name='Port')),
|
('port', models.IntegerField(verbose_name='Port')),
|
||||||
('setting', models.JSONField(default=dict, verbose_name='Setting')),
|
('setting', models.JSONField(default=dict, verbose_name='Setting')),
|
||||||
('platform', models.ForeignKey(on_delete=models.deletion.CASCADE, related_name='protocols', to='assets.platform'),),
|
('platform',
|
||||||
|
models.ForeignKey(on_delete=models.deletion.CASCADE, related_name='protocols', to='assets.platform'),),
|
||||||
('default', models.BooleanField(default=True, verbose_name='Default')),
|
('default', models.BooleanField(default=True, verbose_name='Default')),
|
||||||
('required', models.BooleanField(default=False, verbose_name='Required')),
|
('required', models.BooleanField(default=False, verbose_name='Required')),
|
||||||
],
|
],
|
||||||
@ -32,20 +31,24 @@ class Migration(migrations.Migration):
|
|||||||
('ping_enabled', models.BooleanField(default=False, verbose_name='Ping enabled')),
|
('ping_enabled', models.BooleanField(default=False, verbose_name='Ping enabled')),
|
||||||
('ping_method', models.CharField(blank=True, max_length=32, null=True, verbose_name='Ping method')),
|
('ping_method', models.CharField(blank=True, max_length=32, null=True, verbose_name='Ping method')),
|
||||||
('gather_facts_enabled', models.BooleanField(default=False, verbose_name='Gather facts enabled')),
|
('gather_facts_enabled', models.BooleanField(default=False, verbose_name='Gather facts enabled')),
|
||||||
('gather_facts_method', models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method')),
|
('gather_facts_method',
|
||||||
('push_account_enabled', models.BooleanField(default=False, verbose_name='Create account enabled')),
|
models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method')),
|
||||||
('push_account_method', models.TextField(blank=True, max_length=32, null=True, verbose_name='Create account method')),
|
|
||||||
('change_secret_enabled', models.BooleanField(default=False, verbose_name='Change password enabled')),
|
('change_secret_enabled', models.BooleanField(default=False, verbose_name='Change password enabled')),
|
||||||
('change_secret_method', models.TextField(blank=True, max_length=32, null=True, verbose_name='Change password method')),
|
('change_secret_method',
|
||||||
|
models.TextField(blank=True, max_length=32, null=True, verbose_name='Change password method')),
|
||||||
('verify_account_enabled', models.BooleanField(default=False, verbose_name='Verify account enabled')),
|
('verify_account_enabled', models.BooleanField(default=False, verbose_name='Verify account enabled')),
|
||||||
('verify_account_method', models.TextField(blank=True, max_length=32, null=True, verbose_name='Verify account method')),
|
('verify_account_method',
|
||||||
|
models.TextField(blank=True, max_length=32, null=True, verbose_name='Verify account method')),
|
||||||
('gather_accounts_enabled', models.BooleanField(default=False, verbose_name='Gather facts enabled')),
|
('gather_accounts_enabled', models.BooleanField(default=False, verbose_name='Gather facts enabled')),
|
||||||
('gather_accounts_method', models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method')),
|
('gather_accounts_method',
|
||||||
|
models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='platform',
|
model_name='platform',
|
||||||
name='automation',
|
name='automation',
|
||||||
field=models.OneToOneField(blank=True, null=True, on_delete=models.deletion.CASCADE, related_name='platform', to='assets.platformautomation', verbose_name='Automation'),
|
field=models.OneToOneField(blank=True, null=True, on_delete=models.deletion.CASCADE,
|
||||||
|
related_name='platform', to='assets.platformautomation',
|
||||||
|
verbose_name='Automation'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
# Generated by Django 3.2.12 on 2022-07-11 08:59
|
# Generated by Django 3.2.12 on 2022-07-11 08:59
|
||||||
|
|
||||||
import common.db.fields
|
import uuid
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import simple_history.models
|
import simple_history.models
|
||||||
import uuid
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import common.db.fields
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('assets', '0098_auto_20220430_2126'),
|
('assets', '0098_auto_20220430_2126'),
|
||||||
@ -20,14 +21,19 @@ class Migration(migrations.Migration):
|
|||||||
name='HistoricalAccount',
|
name='HistoricalAccount',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
|
('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')], default='password', max_length=16, verbose_name='Secret type')),
|
('secret_type', models.CharField(
|
||||||
|
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
|
||||||
|
('token', 'Token')], default='password', max_length=16, verbose_name='Secret type')),
|
||||||
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
|
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
|
||||||
('version', models.IntegerField(default=0, verbose_name='Version'),),
|
('version', models.IntegerField(default=0, verbose_name='Version'),),
|
||||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('history_date', models.DateTimeField(db_index=True)),
|
('history_date', models.DateTimeField(db_index=True)),
|
||||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
('history_type',
|
||||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||||
|
('history_user',
|
||||||
|
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+',
|
||||||
|
to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'historical Account',
|
'verbose_name': 'historical Account',
|
||||||
@ -40,43 +46,55 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Account',
|
name='Account',
|
||||||
fields=[
|
fields=[
|
||||||
('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')),
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||||
('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')),
|
('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')], default='password', max_length=16, verbose_name='Secret type')),
|
('secret_type', models.CharField(
|
||||||
|
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
|
||||||
|
('token', 'Token')], default='password', max_length=16, verbose_name='Secret type')),
|
||||||
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
|
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
|
||||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||||
('connectivity', models.CharField(choices=[('unknown', 'Unknown'), ('ok', 'Ok'), ('failed', 'Failed')], default='unknown', max_length=16, verbose_name='Connectivity')),
|
('connectivity', models.CharField(choices=[('unknown', 'Unknown'), ('ok', 'Ok'), ('failed', 'Failed')],
|
||||||
|
default='unknown', max_length=16, verbose_name='Connectivity')),
|
||||||
('date_verified', models.DateTimeField(null=True, verbose_name='Date verified')),
|
('date_verified', models.DateTimeField(null=True, verbose_name='Date verified')),
|
||||||
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
||||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||||
('privileged', models.BooleanField(default=False, verbose_name='Privileged')),
|
('privileged', models.BooleanField(default=False, verbose_name='Privileged')),
|
||||||
('version', models.IntegerField(default=0, verbose_name='Version')),
|
('version', models.IntegerField(default=0, verbose_name='Version')),
|
||||||
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='assets.asset', verbose_name='Asset')),
|
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts',
|
||||||
|
to='assets.asset', verbose_name='Asset')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Account',
|
'verbose_name': 'Account',
|
||||||
'permissions': [('view_accountsecret', 'Can view asset account secret'), ('change_accountsecret', 'Can change asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret')],
|
'permissions': [('view_accountsecret', 'Can view asset account secret'),
|
||||||
|
('change_accountsecret', 'Can change asset account secret'),
|
||||||
|
('view_historyaccount', 'Can view asset history account'),
|
||||||
|
('view_historyaccountsecret', 'Can view asset history account secret')],
|
||||||
'unique_together': {('name', 'asset'), ('username', 'asset', 'secret_type')},
|
'unique_together': {('name', 'asset'), ('username', 'asset', 'secret_type')},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='account',
|
model_name='account',
|
||||||
name='su_from',
|
name='su_from',
|
||||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='assets.account', verbose_name='Su from'),
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to',
|
||||||
|
to='assets.account', verbose_name='Su from'),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='AccountTemplate',
|
name='AccountTemplate',
|
||||||
fields=[
|
fields=[
|
||||||
('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')),
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||||
('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')),
|
('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')], default='password', max_length=16, verbose_name='Secret type'),),
|
('secret_type', models.CharField(
|
||||||
|
choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'),
|
||||||
|
('token', 'Token')], default='password', max_length=16, verbose_name='Secret type'),),
|
||||||
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
|
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
|
||||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||||
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
||||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||||
|
@ -75,33 +75,11 @@ class Migration(migrations.Migration):
|
|||||||
name='updated_by',
|
name='updated_by',
|
||||||
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by'),
|
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by'),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
|
||||||
model_name='platformautomation',
|
|
||||||
name='push_account_enabled',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='Push account enabled'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='platformautomation',
|
|
||||||
name='push_account_method',
|
|
||||||
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Push account method'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='platformprotocol',
|
model_name='platformprotocol',
|
||||||
name='default',
|
name='default',
|
||||||
field=models.BooleanField(default=False, verbose_name='Default'),
|
field=models.BooleanField(default=False, verbose_name='Default'),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
|
||||||
name='DiscoveryAccountAutomation',
|
|
||||||
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': 'Discovery account automation',
|
|
||||||
},
|
|
||||||
bases=('assets.baseautomation',),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='GatherFactsAutomation',
|
name='GatherFactsAutomation',
|
||||||
fields=[
|
fields=[
|
||||||
|
34
apps/assets/migrations/0114_remove_redundant_macos.py
Normal file
34
apps/assets/migrations/0114_remove_redundant_macos.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 3.2.14 on 2022-12-15 07:08
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_del_macos(apps, schema_editor):
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
asset_model = apps.get_model('assets', 'Asset')
|
||||||
|
platform_model = apps.get_model('assets', 'Platform')
|
||||||
|
old_macos = platform_model.objects.using(db_alias).filter(
|
||||||
|
name='MacOS', type='macos'
|
||||||
|
).first()
|
||||||
|
new_macos = platform_model.objects.using(db_alias).filter(
|
||||||
|
name='macOS', type='unix'
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not old_macos or not new_macos:
|
||||||
|
return
|
||||||
|
|
||||||
|
asset_model.objects.using(db_alias).filter(
|
||||||
|
platform=old_macos
|
||||||
|
).update(platform=new_macos)
|
||||||
|
|
||||||
|
platform_model.objects.using(db_alias).filter(id=old_macos.id).delete()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0113_auto_20221122_2015'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrate_del_macos),
|
||||||
|
]
|
183
apps/assets/migrations/0115_auto_20221220_1956.py
Normal file
183
apps/assets/migrations/0115_auto_20221220_1956.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
# Generated by Django 3.2.14 on 2022-12-20 11:56
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0114_remove_redundant_macos'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='accountbackupplan',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='baseautomation',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='changesecretrecord',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='date_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='favoriteasset',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='favoriteasset',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gathereduser',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gathereduser',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gathereduser',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='date_created',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='date_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='accountbackupplan',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='accountbackupplan',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='accounttemplate',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='accounttemplate',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='baseautomation',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='baseautomation',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='changesecretrecord',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='changesecretrecord',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='domain',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='favoriteasset',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gathereduser',
|
||||||
|
name='date_created',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='label',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='label',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='label',
|
||||||
|
name='updated_by',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.2.16 on 2022-12-22 11:50
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0115_auto_20221220_1956'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='automationexecution',
|
||||||
|
options={'permissions': [('view_changesecretexecution', 'Can view change secret execution'), ('add_changesecretexection', 'Can add change secret execution'), ('view_gatheraccountsexecution', 'Can view gather accounts execution'), ('add_gatheraccountsexecution', 'Can add gather accounts execution')], 'verbose_name': 'Automation task execution'},
|
||||||
|
),
|
||||||
|
]
|
17
apps/assets/migrations/0117_alter_gateway_options.py
Normal file
17
apps/assets/migrations/0117_alter_gateway_options.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.2.16 on 2022-12-23 07:36
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0116_alter_automationexecution_options'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='gateway',
|
||||||
|
options={'verbose_name': 'Gateway'},
|
||||||
|
),
|
||||||
|
]
|
50
apps/assets/migrations/0118_auto_20221227_1504.py
Normal file
50
apps/assets/migrations/0118_auto_20221227_1504.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Generated by Django 3.2.14 on 2022-12-27 07:04
|
||||||
|
|
||||||
|
import common.db.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0117_alter_gateway_options'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pushaccountautomation',
|
||||||
|
name='password_rules',
|
||||||
|
field=models.JSONField(default=dict, verbose_name='Password rules'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pushaccountautomation',
|
||||||
|
name='secret',
|
||||||
|
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pushaccountautomation',
|
||||||
|
name='secret_strategy',
|
||||||
|
field=models.CharField(choices=[('specific', 'Specific'), ('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'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pushaccountautomation',
|
||||||
|
name='secret_type',
|
||||||
|
field=models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token')], default='password', max_length=16, verbose_name='Secret type'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
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 (The key generated by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pushaccountautomation',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(default='', max_length=128, verbose_name='Username'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='baseautomation',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('ping', 'Ping'), ('gather_facts', 'Gather facts'), ('push_account', 'Push account'), ('change_secret', 'Change secret'), ('verify_account', 'Verify account'), ('gather_accounts', 'Gather accounts')], max_length=16, verbose_name='Type'),
|
||||||
|
),
|
||||||
|
]
|
21
apps/assets/migrations/0119_auto_20221227_1740.py
Normal file
21
apps/assets/migrations/0119_auto_20221227_1740.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 3.2.16 on 2022-12-27 09:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0118_auto_20221227_1504'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='account',
|
||||||
|
name='source',
|
||||||
|
field=models.CharField(default='local', max_length=30, verbose_name='Source'),
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='GatheredUser',
|
||||||
|
),
|
||||||
|
]
|
@ -7,7 +7,6 @@ from .gateway import *
|
|||||||
from .domain import *
|
from .domain import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .utils import *
|
from .utils import *
|
||||||
from .gathered_user import *
|
|
||||||
from .favorite_asset import *
|
from .favorite_asset import *
|
||||||
from .account import *
|
from .account import *
|
||||||
from .backup import *
|
from .backup import *
|
||||||
|
@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
|
from ..const import AliasAccount, Source
|
||||||
from .base import AbsConnectivity, BaseAccount
|
from .base import AbsConnectivity, BaseAccount
|
||||||
|
|
||||||
__all__ = ['Account', 'AccountTemplate']
|
__all__ = ['Account', 'AccountTemplate']
|
||||||
@ -41,11 +41,6 @@ class AccountHistoricalRecords(HistoricalRecords):
|
|||||||
|
|
||||||
|
|
||||||
class Account(AbsConnectivity, BaseAccount):
|
class Account(AbsConnectivity, BaseAccount):
|
||||||
class AliasAccount(models.TextChoices):
|
|
||||||
ALL = '@ALL', _('All')
|
|
||||||
INPUT = '@INPUT', _('Manual input')
|
|
||||||
USER = '@USER', _('Dynamic user')
|
|
||||||
|
|
||||||
asset = models.ForeignKey(
|
asset = models.ForeignKey(
|
||||||
'assets.Asset', related_name='accounts',
|
'assets.Asset', related_name='accounts',
|
||||||
on_delete=models.CASCADE, verbose_name=_('Asset')
|
on_delete=models.CASCADE, verbose_name=_('Asset')
|
||||||
@ -56,6 +51,7 @@ class Account(AbsConnectivity, BaseAccount):
|
|||||||
)
|
)
|
||||||
version = models.IntegerField(default=0, verbose_name=_('Version'))
|
version = models.IntegerField(default=0, verbose_name=_('Version'))
|
||||||
history = AccountHistoricalRecords(included_fields=['id', 'secret', 'secret_type', 'version'])
|
history = AccountHistoricalRecords(included_fields=['id', 'secret', 'secret_type', 'version'])
|
||||||
|
source = models.CharField(max_length=30, default=Source.LOCAL, verbose_name=_('Source'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Account')
|
verbose_name = _('Account')
|
||||||
@ -74,21 +70,32 @@ class Account(AbsConnectivity, BaseAccount):
|
|||||||
def platform(self):
|
def platform(self):
|
||||||
return self.asset.platform
|
return self.asset.platform
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def alias(self):
|
||||||
|
if self.username.startswith('@'):
|
||||||
|
return self.username
|
||||||
|
return self.name
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}'.format(self.username)
|
return '{}'.format(self.username)
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def has_secret(self):
|
||||||
|
return bool(self.secret)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_manual_account(cls):
|
def get_manual_account(cls):
|
||||||
""" @INPUT 手动登录的账号(any) """
|
""" @INPUT 手动登录的账号(any) """
|
||||||
return cls(name=cls.AliasAccount.INPUT.label, username=cls.AliasAccount.INPUT.value, secret=None)
|
return cls(name=AliasAccount.INPUT.label, username=AliasAccount.INPUT.value, secret=None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_user_account(cls, username):
|
def get_user_account(cls, username):
|
||||||
""" @USER 动态用户的账号(self) """
|
""" @USER 动态用户的账号(self) """
|
||||||
return cls(name=cls.AliasAccount.USER.label, username=cls.AliasAccount.USER.value)
|
return cls(name=AliasAccount.USER.label, username=AliasAccount.USER.value)
|
||||||
|
|
||||||
def get_su_from_accounts(self):
|
def get_su_from_accounts(self):
|
||||||
return self.asset.accounts.exclude(id=self.id)
|
""" 排除自己和以自己为 su-from 的账号 """
|
||||||
|
return self.asset.accounts.exclude(id=self.id).exclude(su_from=self)
|
||||||
|
|
||||||
|
|
||||||
class AccountTemplate(BaseAccount):
|
class AccountTemplate(BaseAccount):
|
||||||
|
@ -2,18 +2,16 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
import uuid
|
|
||||||
import logging
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
from orgs.mixins.models import OrgManager, JMSOrgBaseModel
|
from orgs.mixins.models import OrgManager, JMSOrgBaseModel
|
||||||
from ..platform import Platform
|
|
||||||
from ..base import AbsConnectivity
|
from ..base import AbsConnectivity
|
||||||
|
from ..platform import Platform
|
||||||
|
|
||||||
__all__ = ['Asset', 'AssetQuerySet', 'default_node', 'Protocol']
|
__all__ = ['Asset', 'AssetQuerySet', 'default_node', 'Protocol']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -53,7 +51,7 @@ class NodesRelationMixin:
|
|||||||
NODES_CACHE_KEY = 'ASSET_NODES_{}'
|
NODES_CACHE_KEY = 'ASSET_NODES_{}'
|
||||||
ALL_ASSET_NODES_CACHE_KEY = 'ALL_ASSETS_NODES'
|
ALL_ASSET_NODES_CACHE_KEY = 'ALL_ASSETS_NODES'
|
||||||
CACHE_TIME = 3600 * 24 * 7
|
CACHE_TIME = 3600 * 24 * 7
|
||||||
id = ""
|
id: str
|
||||||
_all_nodes_keys = None
|
_all_nodes_keys = None
|
||||||
|
|
||||||
def get_nodes(self):
|
def get_nodes(self):
|
||||||
@ -65,16 +63,29 @@ class NodesRelationMixin:
|
|||||||
|
|
||||||
def get_all_nodes(self, flat=False):
|
def get_all_nodes(self, flat=False):
|
||||||
from ..node import Node
|
from ..node import Node
|
||||||
|
node_keys = self.get_all_node_keys()
|
||||||
|
nodes = Node.objects.filter(key__in=node_keys).distinct()
|
||||||
|
if not flat:
|
||||||
|
return nodes
|
||||||
|
node_ids = set(nodes.values_list('id', flat=True))
|
||||||
|
return node_ids
|
||||||
|
|
||||||
|
def get_all_node_keys(self):
|
||||||
node_keys = set()
|
node_keys = set()
|
||||||
for node in self.get_nodes():
|
for node in self.get_nodes():
|
||||||
ancestor_keys = node.get_ancestor_keys(with_self=True)
|
ancestor_keys = node.get_ancestor_keys(with_self=True)
|
||||||
node_keys.update(ancestor_keys)
|
node_keys.update(ancestor_keys)
|
||||||
nodes = Node.objects.filter(key__in=node_keys).distinct()
|
return node_keys
|
||||||
if flat:
|
|
||||||
node_ids = set(nodes.values_list('id', flat=True))
|
@classmethod
|
||||||
return node_ids
|
def get_all_nodes_for_assets(cls, assets):
|
||||||
else:
|
from ..node import Node
|
||||||
return nodes
|
node_keys = set()
|
||||||
|
for asset in assets:
|
||||||
|
asset_node_keys = asset.get_all_node_keys()
|
||||||
|
node_keys.update(asset_node_keys)
|
||||||
|
nodes = Node.objects.filter(key__in=node_keys)
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
class Protocol(models.Model):
|
class Protocol(models.Model):
|
||||||
@ -87,7 +98,6 @@ class Protocol(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel):
|
class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
|
||||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
address = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
|
address = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
|
||||||
platform = models.ForeignKey(Platform, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets')
|
platform = models.ForeignKey(Platform, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets')
|
||||||
@ -97,7 +107,6 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel):
|
|||||||
verbose_name=_("Nodes"))
|
verbose_name=_("Nodes"))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
||||||
labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels"))
|
labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels"))
|
||||||
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
|
|
||||||
info = models.JSONField(verbose_name='Info', default=dict, blank=True)
|
info = models.JSONField(verbose_name='Info', default=dict, blank=True)
|
||||||
|
|
||||||
objects = AssetManager.from_queryset(AssetQuerySet)()
|
objects = AssetManager.from_queryset(AssetQuerySet)()
|
||||||
|
@ -5,4 +5,3 @@ from .gather_facts import *
|
|||||||
from .change_secret import *
|
from .change_secret import *
|
||||||
from .verify_account import *
|
from .verify_account import *
|
||||||
from .gather_accounts import *
|
from .gather_accounts import *
|
||||||
from .discovery_account import *
|
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from celery import current_task
|
from celery import current_task
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.const.choices import Trigger
|
from assets.const import AutomationTypes
|
||||||
from common.mixins.models import CommonModelMixin
|
|
||||||
from common.db.fields import EncryptJsonDictTextField
|
|
||||||
from orgs.mixins.models import OrgModelMixin
|
|
||||||
from ops.mixin import PeriodTaskModelMixin
|
|
||||||
from assets.models import Node, Asset
|
from assets.models import Node, Asset
|
||||||
from assets.tasks import execute_automation
|
from assets.tasks import execute_automation
|
||||||
from assets.const import AutomationTypes
|
from common.const.choices import Trigger
|
||||||
|
from common.db.fields import EncryptJsonDictTextField
|
||||||
|
from ops.mixin import PeriodTaskModelMixin
|
||||||
|
from orgs.mixins.models import OrgModelMixin, JMSOrgBaseModel
|
||||||
|
|
||||||
|
|
||||||
class BaseAutomation(CommonModelMixin, PeriodTaskModelMixin, OrgModelMixin):
|
class BaseAutomation(PeriodTaskModelMixin, JMSOrgBaseModel):
|
||||||
accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
|
accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
|
||||||
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
|
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
|
||||||
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
|
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
|
||||||
type = models.CharField(max_length=16, choices=AutomationTypes.choices, verbose_name=_('Type'))
|
type = models.CharField(max_length=16, choices=AutomationTypes.choices, verbose_name=_('Type'))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name + '@' + str(self.created_by)
|
return self.name + '@' + str(self.created_by)
|
||||||
@ -102,6 +101,12 @@ class AutomationExecution(OrgModelMixin):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Automation task execution')
|
verbose_name = _('Automation task execution')
|
||||||
|
permissions = [
|
||||||
|
('view_changesecretexecution', _('Can view change secret execution')),
|
||||||
|
('add_changesecretexection', _('Can add change secret execution')),
|
||||||
|
('view_gatheraccountsexecution', _('Can view gather accounts execution')),
|
||||||
|
('add_gatheraccountsexecution', _('Can add gather accounts execution')),
|
||||||
|
]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def manager_type(self):
|
def manager_type(self):
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from assets.const import AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy
|
||||||
from common.db import fields
|
from common.db import fields
|
||||||
from common.db.models import JMSBaseModel
|
from common.db.models import JMSBaseModel
|
||||||
from assets.const import AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy
|
|
||||||
from .base import BaseAutomation
|
from .base import BaseAutomation
|
||||||
|
|
||||||
__all__ = ['ChangeSecretAutomation', 'ChangeSecretRecord']
|
__all__ = ['ChangeSecretAutomation', 'ChangeSecretRecord', 'ChangeSecretMixin']
|
||||||
|
|
||||||
|
|
||||||
class ChangeSecretAutomation(BaseAutomation):
|
class ChangeSecretMixin(models.Model):
|
||||||
secret_type = models.CharField(
|
secret_type = models.CharField(
|
||||||
choices=SecretType.choices, max_length=16,
|
choices=SecretType.choices, max_length=16,
|
||||||
default=SecretType.PASSWORD, verbose_name=_('Secret type')
|
default=SecretType.PASSWORD, verbose_name=_('Secret type')
|
||||||
@ -24,6 +24,12 @@ class ChangeSecretAutomation(BaseAutomation):
|
|||||||
choices=SSHKeyStrategy.choices, max_length=16,
|
choices=SSHKeyStrategy.choices, max_length=16,
|
||||||
default=SSHKeyStrategy.add, verbose_name=_('SSH key change strategy')
|
default=SSHKeyStrategy.add, verbose_name=_('SSH key change strategy')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeSecretAutomation(BaseAutomation, ChangeSecretMixin):
|
||||||
recipients = models.ManyToManyField('users.User', verbose_name=_("Recipient"), blank=True)
|
recipients = models.ManyToManyField('users.User', verbose_name=_("Recipient"), blank=True)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from .base import BaseAutomation
|
|
||||||
|
|
||||||
|
|
||||||
class DiscoveryAccountAutomation(BaseAutomation):
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("Discovery account automation")
|
|
||||||
|
|
||||||
def to_attr_json(self):
|
|
||||||
attr_json = super().to_attr_json()
|
|
||||||
attr_json.update({
|
|
||||||
'type': 'discover_account'
|
|
||||||
})
|
|
||||||
return attr_json
|
|
@ -1,12 +1,16 @@
|
|||||||
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from assets.const import AutomationTypes
|
from assets.const import AutomationTypes
|
||||||
from .base import BaseAutomation
|
from .base import BaseAutomation
|
||||||
|
from .change_secret import ChangeSecretMixin
|
||||||
|
|
||||||
__all__ = ['PushAccountAutomation']
|
__all__ = ['PushAccountAutomation']
|
||||||
|
|
||||||
|
|
||||||
class PushAccountAutomation(BaseAutomation):
|
class PushAccountAutomation(BaseAutomation, ChangeSecretMixin):
|
||||||
|
accounts = None
|
||||||
|
username = models.CharField(max_length=128, verbose_name=_('Username'))
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.type = AutomationTypes.push_account
|
self.type = AutomationTypes.push_account
|
||||||
|
@ -7,26 +7,23 @@ from celery import current_task
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orgs.mixins.models import OrgModelMixin
|
|
||||||
from ops.mixin import PeriodTaskModelMixin
|
|
||||||
from common.utils import get_logger
|
|
||||||
from common.const.choices import Trigger
|
from common.const.choices import Trigger
|
||||||
from common.db.encoder import ModelJSONFieldEncoder
|
from common.db.encoder import ModelJSONFieldEncoder
|
||||||
from common.mixins.models import CommonModelMixin
|
from common.utils import get_logger
|
||||||
|
from ops.mixin import PeriodTaskModelMixin
|
||||||
|
from orgs.mixins.models import OrgModelMixin, JMSOrgBaseModel
|
||||||
|
|
||||||
__all__ = ['AccountBackupPlan', 'AccountBackupPlanExecution']
|
__all__ = ['AccountBackupPlan', 'AccountBackupPlanExecution']
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class AccountBackupPlan(CommonModelMixin, PeriodTaskModelMixin, OrgModelMixin):
|
class AccountBackupPlan(PeriodTaskModelMixin, JMSOrgBaseModel):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
|
||||||
types = models.JSONField(default=list)
|
types = models.JSONField(default=list)
|
||||||
recipients = models.ManyToManyField(
|
recipients = models.ManyToManyField(
|
||||||
'users.User', related_name='recipient_escape_route_plans', blank=True,
|
'users.User', related_name='recipient_escape_route_plans', blank=True,
|
||||||
verbose_name=_("Recipient")
|
verbose_name=_("Recipient")
|
||||||
)
|
)
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.name}({self.org_id})'
|
return f'{self.name}({self.org_id})'
|
||||||
|
@ -69,8 +69,6 @@ class BaseAccount(JMSOrgBaseModel):
|
|||||||
secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Secret'))
|
secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Secret'))
|
||||||
privileged = models.BooleanField(verbose_name=_("Privileged"), default=False)
|
privileged = models.BooleanField(verbose_name=_("Privileged"), default=False)
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
|
||||||
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
|
|
||||||
|
|
||||||
objects = BaseAccountManager.from_queryset(BaseAccountQuerySet)()
|
objects = BaseAccountManager.from_queryset(BaseAccountQuerySet)()
|
||||||
|
|
||||||
|
@ -7,11 +7,6 @@ from django.db import models
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from users.models import User, UserGroup
|
|
||||||
from applications.models import Application
|
|
||||||
from ..models import SystemUser, Asset, Node
|
|
||||||
|
|
||||||
from common.utils import lazyproperty, get_logger, get_object_or_none
|
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import uuid
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils import get_logger, lazyproperty
|
from common.utils import get_logger, lazyproperty
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import JMSOrgBaseModel
|
||||||
|
|
||||||
from .gateway import Gateway
|
from .gateway import Gateway
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
@ -16,11 +14,8 @@ logger = get_logger(__file__)
|
|||||||
__all__ = ['Domain']
|
__all__ = ['Domain']
|
||||||
|
|
||||||
|
|
||||||
class Domain(OrgModelMixin):
|
class Domain(JMSOrgBaseModel):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
|
||||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
|
||||||
date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date created'))
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Domain")
|
verbose_name = _("Domain")
|
||||||
@ -51,5 +46,3 @@ class Domain(OrgModelMixin):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_gateway_queryset(cls):
|
def get_gateway_queryset(cls):
|
||||||
return Gateway.objects.all()
|
return Gateway.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,13 +2,12 @@
|
|||||||
#
|
#
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from common.mixins.models import CommonModelMixin
|
from common.db.models import JMSBaseModel
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['FavoriteAsset']
|
__all__ = ['FavoriteAsset']
|
||||||
|
|
||||||
|
|
||||||
class FavoriteAsset(CommonModelMixin):
|
class FavoriteAsset(JMSBaseModel):
|
||||||
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
|
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
|
||||||
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE)
|
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ class Gateway(Host):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
verbose_name = _("Gateway")
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.platform = self.default_platform()
|
self.platform = self.default_platform()
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
import uuid
|
|
||||||
from django.db import models
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from orgs.mixins.models import OrgModelMixin
|
|
||||||
|
|
||||||
__all__ = ['GatheredUser']
|
|
||||||
|
|
||||||
|
|
||||||
class GatheredUser(OrgModelMixin):
|
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
|
||||||
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'))
|
|
||||||
present = models.BooleanField(default=True, verbose_name=_("Present"))
|
|
||||||
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date last login"))
|
|
||||||
ip_last_login = models.CharField(max_length=39, default='', verbose_name=_("IP last login"))
|
|
||||||
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
|
|
||||||
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self.asset.name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ip(self):
|
|
||||||
return self.asset.address
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('GatherUser')
|
|
||||||
ordering = ['asset']
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{}: {}'.format(self.asset.name, self.username)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from common.utils import lazyproperty
|
||||||
from orgs.mixins.models import JMSOrgBaseModel
|
from orgs.mixins.models import JMSOrgBaseModel
|
||||||
|
|
||||||
|
|
||||||
@ -19,7 +20,6 @@ class Label(JMSOrgBaseModel):
|
|||||||
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES,
|
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES,
|
||||||
default=USER_CATEGORY, verbose_name=_("Category"))
|
default=USER_CATEGORY, verbose_name=_("Category"))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
||||||
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset_group_by_name(cls):
|
def get_queryset_group_by_name(cls):
|
||||||
@ -27,6 +27,10 @@ class Label(JMSOrgBaseModel):
|
|||||||
for name in names:
|
for name in names:
|
||||||
yield name, cls.objects.filter(name=name)
|
yield name, cls.objects.filter(name=name)
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def asset_count(self):
|
||||||
|
return self.assets.count()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{}:{}".format(self.name, self.value)
|
return "{}:{}".format(self.name, self.value)
|
||||||
|
|
||||||
|
@ -1,29 +1,24 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
import uuid
|
|
||||||
import threading
|
import threading
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
from django.db.utils import IntegrityError
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.utils.translation import ugettext
|
|
||||||
from django.db.transaction import atomic
|
from django.db.transaction import atomic
|
||||||
from django.core.cache import cache
|
from django.utils.translation import ugettext
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils.lock import DistributedLock
|
|
||||||
from common.utils.common import timeit
|
|
||||||
from common.db.models import output_as_string
|
from common.db.models import output_as_string
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from orgs.mixins.models import OrgModelMixin, OrgManager
|
from common.utils.lock import DistributedLock
|
||||||
from orgs.utils import get_current_org, tmp_to_org, tmp_to_root_org
|
from orgs.mixins.models import OrgManager, JMSOrgBaseModel
|
||||||
from orgs.models import Organization
|
from orgs.models import Organization
|
||||||
|
from orgs.utils import get_current_org, tmp_to_org, tmp_to_root_org
|
||||||
|
|
||||||
__all__ = ['Node', 'FamilyMixin', 'compute_parent_key', 'NodeQuerySet']
|
__all__ = ['Node', 'FamilyMixin', 'compute_parent_key', 'NodeQuerySet']
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
@ -178,9 +173,7 @@ class FamilyMixin:
|
|||||||
return parent_keys
|
return parent_keys
|
||||||
|
|
||||||
def get_ancestor_keys(self, with_self=False):
|
def get_ancestor_keys(self, with_self=False):
|
||||||
return self.get_node_ancestor_keys(
|
return self.get_node_ancestor_keys(self.key, with_self=with_self)
|
||||||
self.key, with_self=with_self
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ancestors(self):
|
def ancestors(self):
|
||||||
@ -437,6 +430,12 @@ class NodeAssetsMixin(NodeAllAssetsMappingMixin):
|
|||||||
assets = Asset.objects.filter(nodes=self)
|
assets = Asset.objects.filter(nodes=self)
|
||||||
return assets.distinct()
|
return assets.distinct()
|
||||||
|
|
||||||
|
def get_assets_for_tree(self):
|
||||||
|
return self.get_assets().only(
|
||||||
|
"id", "name", "address", "platform_id",
|
||||||
|
"org_id", "is_active"
|
||||||
|
).prefetch_related('platform')
|
||||||
|
|
||||||
def get_valid_assets(self):
|
def get_valid_assets(self):
|
||||||
return self.get_assets().valid()
|
return self.get_assets().valid()
|
||||||
|
|
||||||
@ -547,7 +546,7 @@ class SomeNodesMixin:
|
|||||||
return root_nodes
|
return root_nodes
|
||||||
|
|
||||||
|
|
||||||
class Node(OrgModelMixin, SomeNodesMixin, FamilyMixin, NodeAssetsMixin):
|
class Node(JMSOrgBaseModel, SomeNodesMixin, FamilyMixin, NodeAssetsMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
|
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
|
||||||
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
||||||
|
@ -2,9 +2,8 @@ from django.db import models
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from assets.const import AllTypes
|
from assets.const import AllTypes
|
||||||
from common.db.fields import JsonDictTextField
|
|
||||||
|
|
||||||
from assets.const import Protocol
|
from assets.const import Protocol
|
||||||
|
from common.db.fields import JsonDictTextField
|
||||||
|
|
||||||
__all__ = ['Platform', 'PlatformProtocol', 'PlatformAutomation']
|
__all__ = ['Platform', 'PlatformProtocol', 'PlatformAutomation']
|
||||||
|
|
||||||
@ -45,8 +44,6 @@ class PlatformAutomation(models.Model):
|
|||||||
ping_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("Ping method"))
|
ping_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("Ping method"))
|
||||||
gather_facts_enabled = models.BooleanField(default=False, verbose_name=_("Gather facts enabled"))
|
gather_facts_enabled = models.BooleanField(default=False, verbose_name=_("Gather facts enabled"))
|
||||||
gather_facts_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Gather facts method"))
|
gather_facts_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Gather facts method"))
|
||||||
push_account_enabled = models.BooleanField(default=False, verbose_name=_("Push account enabled"))
|
|
||||||
push_account_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Push account method"))
|
|
||||||
change_secret_enabled = models.BooleanField(default=False, verbose_name=_("Change password enabled"))
|
change_secret_enabled = models.BooleanField(default=False, verbose_name=_("Change password enabled"))
|
||||||
change_secret_method = models.TextField(
|
change_secret_method = models.TextField(
|
||||||
max_length=32, blank=True, null=True, verbose_name=_("Change password method"))
|
max_length=32, blank=True, null=True, verbose_name=_("Change password method"))
|
||||||
@ -83,7 +80,7 @@ class Platform(models.Model):
|
|||||||
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
|
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
|
||||||
# 账号有关的
|
# 账号有关的
|
||||||
su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
|
su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
|
||||||
su_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("SU method"))
|
su_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("Su method"))
|
||||||
automation = models.OneToOneField(PlatformAutomation, on_delete=models.CASCADE, related_name='platform',
|
automation = models.OneToOneField(PlatformAutomation, on_delete=models.CASCADE, related_name='platform',
|
||||||
blank=True, null=True, verbose_name=_("Automation"))
|
blank=True, null=True, verbose_name=_("Automation"))
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from common.utils import validate_ssh_private_key
|
from common.utils import validate_ssh_private_key
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'private_key_validator',
|
'private_key_validator',
|
||||||
]
|
]
|
||||||
@ -22,8 +21,6 @@ def private_key_validator(value):
|
|||||||
|
|
||||||
|
|
||||||
def update_internal_platforms(platform_model):
|
def update_internal_platforms(platform_model):
|
||||||
from assets.const import AllTypes
|
|
||||||
|
|
||||||
platforms = [
|
platforms = [
|
||||||
{'name': 'Linux', 'category': 'host', 'type': 'linux'},
|
{'name': 'Linux', 'category': 'host', 'type': 'linux'},
|
||||||
{'name': 'BSD', 'category': 'host', 'type': 'unix'},
|
{'name': 'BSD', 'category': 'host', 'type': 'unix'},
|
||||||
@ -32,7 +29,6 @@ def update_internal_platforms(platform_model):
|
|||||||
{'name': 'Windows', 'category': 'host', 'type': 'unix'},
|
{'name': 'Windows', 'category': 'host', 'type': 'unix'},
|
||||||
{
|
{
|
||||||
'name': 'AIX', 'category': 'host', 'type': 'unix',
|
'name': 'AIX', 'category': 'host', 'type': 'unix',
|
||||||
'push_account_method': 'create_account_aix',
|
|
||||||
'change_secret_method': 'change_secret_aix',
|
'change_secret_method': 'change_secret_aix',
|
||||||
},
|
},
|
||||||
{'name': 'Windows', 'category': 'host', 'type': 'windows'},
|
{'name': 'Windows', 'category': 'host', 'type': 'windows'},
|
||||||
|
@ -6,7 +6,6 @@ from .label import *
|
|||||||
from .node import *
|
from .node import *
|
||||||
from .gateway import *
|
from .gateway import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .gathered_user import *
|
|
||||||
from .favorite_asset import *
|
from .favorite_asset import *
|
||||||
from .account import *
|
from .account import *
|
||||||
from .platform import *
|
from .platform import *
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from common.drf.serializers import SecretReadableMixin
|
from assets.const import SecretType, Source
|
||||||
from common.drf.fields import ObjectRelatedField, LabeledChoiceField
|
|
||||||
from assets.tasks import push_accounts_to_assets
|
|
||||||
from assets.models import Account, AccountTemplate, Asset
|
from assets.models import Account, AccountTemplate, Asset
|
||||||
|
from assets.tasks import push_accounts_to_assets
|
||||||
|
from common.drf.fields import ObjectRelatedField, LabeledChoiceField
|
||||||
|
from common.drf.serializers import SecretReadableMixin, BulkModelSerializer
|
||||||
from .base import BaseAccountSerializer
|
from .base import BaseAccountSerializer
|
||||||
from assets.const import SecretType
|
|
||||||
|
|
||||||
|
|
||||||
class AccountSerializerCreateMixin(serializers.ModelSerializer):
|
class AccountSerializerCreateMixin(BulkModelSerializer):
|
||||||
template = serializers.UUIDField(
|
template = serializers.UUIDField(
|
||||||
required=False, allow_null=True, write_only=True,
|
required=False, allow_null=True, write_only=True,
|
||||||
label=_('Account template')
|
label=_('Account template')
|
||||||
@ -53,35 +53,47 @@ class AccountSerializerCreateMixin(serializers.ModelSerializer):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class AccountAssetSerializer(serializers.ModelSerializer):
|
||||||
|
platform = ObjectRelatedField(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Asset
|
||||||
|
fields = ['id', 'name', 'address', 'platform']
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
i = data.get('id')
|
||||||
|
else:
|
||||||
|
i = data
|
||||||
|
|
||||||
|
try:
|
||||||
|
return Asset.objects.get(id=i)
|
||||||
|
except Asset.DoesNotExist:
|
||||||
|
raise serializers.ValidationError(_('Asset not found'))
|
||||||
|
|
||||||
|
|
||||||
class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
|
class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
|
||||||
asset = ObjectRelatedField(
|
asset = AccountAssetSerializer(label=_('Asset'))
|
||||||
required=False, queryset=Asset.objects,
|
source = LabeledChoiceField(choices=Source.choices, label=_("Source"), read_only=True)
|
||||||
label=_('Asset'), attrs=('id', 'name', 'address', 'platform_id')
|
|
||||||
)
|
|
||||||
su_from = ObjectRelatedField(
|
su_from = ObjectRelatedField(
|
||||||
required=False, queryset=Account.objects,
|
required=False, queryset=Account.objects, allow_null=True, allow_empty=True,
|
||||||
label=_('Account'), attrs=('id', 'name', 'username')
|
label=_('Su from'), attrs=('id', 'name', 'username')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseAccountSerializer.Meta):
|
class Meta(BaseAccountSerializer.Meta):
|
||||||
model = Account
|
model = Account
|
||||||
fields = BaseAccountSerializer.Meta.fields \
|
fields = BaseAccountSerializer.Meta.fields \
|
||||||
+ ['su_from', 'version', 'asset'] \
|
+ ['su_from', 'version', 'asset'] \
|
||||||
+ ['template', 'push_now']
|
+ ['template', 'push_now', 'source']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
**BaseAccountSerializer.Meta.extra_kwargs,
|
**BaseAccountSerializer.Meta.extra_kwargs,
|
||||||
'name': {'required': False, 'allow_null': True},
|
'name': {'required': False, 'allow_null': True},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, data=None, **kwargs):
|
def validate_name(self, value):
|
||||||
super().__init__(*args, data=data, **kwargs)
|
if not value:
|
||||||
if data and 'name' not in data:
|
value = self.initial_data.get('username')
|
||||||
username = data.get('username')
|
return value
|
||||||
if username is not None:
|
|
||||||
data['name'] = username
|
|
||||||
if hasattr(self, 'initial_data') and \
|
|
||||||
not getattr(self, 'initial_data', None):
|
|
||||||
delattr(self, 'initial_data')
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_eager_loading(cls, queryset):
|
def setup_eager_loading(cls, queryset):
|
||||||
|
@ -34,7 +34,6 @@ class AccountBackupPlanSerializer(PeriodTaskSerializerMixin, BulkOrgResourceMode
|
|||||||
|
|
||||||
|
|
||||||
class AccountBackupPlanExecutionSerializer(serializers.ModelSerializer):
|
class AccountBackupPlanExecutionSerializer(serializers.ModelSerializer):
|
||||||
trigger = LabeledChoiceField(choices=Trigger.choices, label=_('Trigger mode'))
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AccountBackupPlanExecution
|
model = AccountBackupPlanExecution
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
from assets.models import BaseAccount
|
from assets.models import BaseAccount
|
||||||
from assets.serializers.base import AuthValidateMixin
|
from assets.serializers.base import AuthValidateMixin
|
||||||
@ -9,6 +10,8 @@ __all__ = ['BaseAccountSerializer']
|
|||||||
|
|
||||||
|
|
||||||
class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer):
|
class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer):
|
||||||
|
has_secret = serializers.BooleanField(label=_("Has secret"), read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BaseAccount
|
model = BaseAccount
|
||||||
fields_mini = ['id', 'name', 'username']
|
fields_mini = ['id', 'name', 'username']
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from common.drf.serializers import SecretReadableMixin
|
|
||||||
from assets.models import AccountTemplate
|
from assets.models import AccountTemplate
|
||||||
|
from common.drf.serializers import SecretReadableMixin
|
||||||
from .base import BaseAccountSerializer
|
from .base import BaseAccountSerializer
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework import serializers
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.db.transaction import atomic
|
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
from django.db.transaction import atomic
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
from common.drf.serializers import WritableNestedModelSerializer
|
|
||||||
from common.drf.fields import LabeledChoiceField, ObjectRelatedField
|
from common.drf.fields import LabeledChoiceField, ObjectRelatedField
|
||||||
from orgs.mixins.serializers import OrgResourceSerializerMixin
|
from common.drf.serializers import WritableNestedModelSerializer
|
||||||
|
from orgs.mixins.serializers import BulkOrgResourceSerializerMixin
|
||||||
from ..account import AccountSerializer
|
from ..account import AccountSerializer
|
||||||
from ...models import Asset, Node, Platform, Label, Domain, Account, Protocol
|
|
||||||
from ...const import Category, AllTypes
|
from ...const import Category, AllTypes
|
||||||
|
from ...models import Asset, Node, Platform, Label, Domain, Account, Protocol
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
|
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
|
||||||
'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer',
|
'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer',
|
||||||
|
'AssetDetailSerializer',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ class AssetAccountSerializer(AccountSerializer):
|
|||||||
fields = fields_mini + fields_write_only
|
fields = fields_mini + fields_write_only
|
||||||
|
|
||||||
|
|
||||||
class AssetSerializer(OrgResourceSerializerMixin, WritableNestedModelSerializer):
|
class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSerializer):
|
||||||
category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category'))
|
category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category'))
|
||||||
type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type'))
|
type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type'))
|
||||||
domain = ObjectRelatedField(required=False, queryset=Domain.objects, label=_('Domain'), allow_null=True)
|
domain = ObjectRelatedField(required=False, queryset=Domain.objects, label=_('Domain'), allow_null=True)
|
||||||
@ -66,49 +67,26 @@ class AssetSerializer(OrgResourceSerializerMixin, WritableNestedModelSerializer)
|
|||||||
nodes = ObjectRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes'))
|
nodes = ObjectRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes'))
|
||||||
labels = AssetLabelSerializer(many=True, required=False, label=_('Labels'))
|
labels = AssetLabelSerializer(many=True, required=False, label=_('Labels'))
|
||||||
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'))
|
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'))
|
||||||
accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts'))
|
accounts = AssetAccountSerializer(many=True, required=False, label=_('Account'))
|
||||||
enabled_info = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
fields_mini = ['id', 'name', 'address', 'enabled_info']
|
fields_mini = ['id', 'name', 'address']
|
||||||
fields_small = fields_mini + ['is_active', 'comment']
|
fields_small = fields_mini + ['is_active', 'comment']
|
||||||
fields_fk = ['domain', 'platform', 'platform']
|
fields_fk = ['domain', 'platform', 'platform']
|
||||||
fields_m2m = [
|
fields_m2m = [
|
||||||
'nodes', 'labels', 'protocols', 'accounts', 'nodes_display',
|
'nodes', 'labels', 'protocols', 'accounts', 'nodes_display',
|
||||||
]
|
]
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
'category', 'type', 'specific', 'info',
|
'category', 'type', 'info',
|
||||||
'connectivity', 'date_verified', 'created_by',
|
'connectivity', 'date_verified',
|
||||||
'date_created'
|
'created_by', 'date_created'
|
||||||
]
|
]
|
||||||
fields = fields_small + fields_fk + fields_m2m + read_only_fields
|
fields = fields_small + fields_fk + fields_m2m + read_only_fields
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'name': {'label': _("Name")},
|
'name': {'label': _("Name")},
|
||||||
'address': {'label': _('Address')},
|
'address': {'label': _('Address')},
|
||||||
}
|
'nodes_display': {'label': _('Node path')},
|
||||||
|
|
||||||
def get_field_names(self, declared_fields, info):
|
|
||||||
names = super().get_field_names(declared_fields, info)
|
|
||||||
if self.__class__.__name__ != 'AssetSerializer':
|
|
||||||
names.remove('specific')
|
|
||||||
return names
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_enabled_info(obj):
|
|
||||||
platform = obj.platform
|
|
||||||
automation = platform.automation
|
|
||||||
return {
|
|
||||||
'su_enabled': platform.su_enabled,
|
|
||||||
'ping_enabled': automation.ping_enabled,
|
|
||||||
'domain_enabled': platform.domain_enabled,
|
|
||||||
'ansible_enabled': automation.ansible_enabled,
|
|
||||||
'protocols_enabled': platform.protocols_enabled,
|
|
||||||
'gather_facts_enabled': automation.gather_facts_enabled,
|
|
||||||
'push_account_enabled': automation.push_account_enabled,
|
|
||||||
'change_secret_enabled': automation.change_secret_enabled,
|
|
||||||
'verify_account_enabled': automation.verify_account_enabled,
|
|
||||||
'gather_accounts_enabled': automation.gather_accounts_enabled,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -117,7 +95,7 @@ class AssetSerializer(OrgResourceSerializerMixin, WritableNestedModelSerializer)
|
|||||||
queryset = queryset.prefetch_related('domain', 'platform', 'protocols') \
|
queryset = queryset.prefetch_related('domain', 'platform', 'protocols') \
|
||||||
.annotate(category=F("platform__category")) \
|
.annotate(category=F("platform__category")) \
|
||||||
.annotate(type=F("platform__type"))
|
.annotate(type=F("platform__type"))
|
||||||
queryset = queryset.prefetch_related('nodes', 'labels')
|
queryset = queryset.prefetch_related('nodes', 'labels', 'accounts')
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def perform_nodes_display_create(self, instance, nodes_display):
|
def perform_nodes_display_create(self, instance, nodes_display):
|
||||||
@ -188,6 +166,30 @@ class AssetSerializer(OrgResourceSerializerMixin, WritableNestedModelSerializer)
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class AssetDetailSerializer(AssetSerializer):
|
||||||
|
accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts'))
|
||||||
|
enabled_info = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta(AssetSerializer.Meta):
|
||||||
|
fields = AssetSerializer.Meta.fields + ['accounts', 'enabled_info', 'info', 'specific']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_enabled_info(obj):
|
||||||
|
platform = obj.platform
|
||||||
|
automation = platform.automation
|
||||||
|
return {
|
||||||
|
'su_enabled': platform.su_enabled,
|
||||||
|
'ping_enabled': automation.ping_enabled,
|
||||||
|
'domain_enabled': platform.domain_enabled,
|
||||||
|
'ansible_enabled': automation.ansible_enabled,
|
||||||
|
'protocols_enabled': platform.protocols_enabled,
|
||||||
|
'gather_facts_enabled': automation.gather_facts_enabled,
|
||||||
|
'change_secret_enabled': automation.change_secret_enabled,
|
||||||
|
'verify_account_enabled': automation.verify_account_enabled,
|
||||||
|
'gather_accounts_enabled': automation.gather_accounts_enabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MiniAssetSerializer(serializers.ModelSerializer):
|
class MiniAssetSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
from assets.models import Web
|
from assets.models import Web
|
||||||
from .common import AssetSerializer
|
from .common import AssetSerializer
|
||||||
|
|
||||||
@ -19,12 +18,12 @@ class WebSerializer(AssetSerializer):
|
|||||||
'label': 'URL'
|
'label': 'URL'
|
||||||
},
|
},
|
||||||
'username_selector': {
|
'username_selector': {
|
||||||
'default': 'input[type=text]'
|
'default': 'name=username'
|
||||||
},
|
},
|
||||||
'password_selector': {
|
'password_selector': {
|
||||||
'default': 'input[type=password]'
|
'default': 'name=password'
|
||||||
},
|
},
|
||||||
'submit_selector': {
|
'submit_selector': {
|
||||||
'default': 'button[type=submit]',
|
'default': 'id=longin_button',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
|
||||||
from common.drf.fields import ObjectRelatedField
|
|
||||||
from ..models import GatheredUser, Asset
|
|
||||||
|
|
||||||
|
|
||||||
class GatheredUserSerializer(OrgResourceModelSerializerMixin):
|
|
||||||
asset = ObjectRelatedField(queryset=Asset.objects, label=_('Asset'))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = GatheredUser
|
|
||||||
fields_mini = ['id']
|
|
||||||
fields_small = fields_mini + [
|
|
||||||
'username', 'ip_last_login', 'present', 'name',
|
|
||||||
'date_last_login', 'date_created', 'date_updated'
|
|
||||||
]
|
|
||||||
fields_fk = ['asset', 'ip']
|
|
||||||
fields = fields_small + fields_fk
|
|
||||||
read_only_fields = fields
|
|
||||||
extra_kwargs = {
|
|
||||||
'name': {'label': _("Hostname")},
|
|
||||||
'ip': {'label': 'IP'},
|
|
||||||
}
|
|
@ -1,25 +1,22 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from rest_framework import serializers
|
from django.db.models import Count
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
|
|
||||||
from ..models import Label
|
from ..models import Label
|
||||||
|
|
||||||
|
|
||||||
class LabelSerializer(BulkOrgResourceModelSerializer):
|
class LabelSerializer(BulkOrgResourceModelSerializer):
|
||||||
asset_count = serializers.SerializerMethodField(label=_("Assets amount"))
|
asset_count = serializers.ReadOnlyField(label=_("Assets amount"))
|
||||||
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category display'))
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Label
|
model = Label
|
||||||
fields_mini = ['id', 'name']
|
fields_mini = ['id', 'name']
|
||||||
fields_small = fields_mini + [
|
fields_small = fields_mini + [
|
||||||
'value', 'category', 'category_display',
|
'value', 'category', 'is_active',
|
||||||
'is_active',
|
'date_created', 'comment',
|
||||||
'date_created',
|
|
||||||
'comment',
|
|
||||||
]
|
]
|
||||||
fields_m2m = ['asset_count', 'assets']
|
fields_m2m = ['asset_count', 'assets']
|
||||||
fields = fields_small + fields_m2m
|
fields = fields_small + fields_m2m
|
||||||
@ -30,14 +27,10 @@ class LabelSerializer(BulkOrgResourceModelSerializer):
|
|||||||
'assets': {'required': False, 'label': _('Asset')}
|
'assets': {'required': False, 'label': _('Asset')}
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def get_asset_count(obj):
|
def setup_eager_loading(cls, queryset):
|
||||||
return obj.assets.count()
|
queryset = queryset.annotate(asset_count=Count('assets'))
|
||||||
|
return queryset
|
||||||
def get_field_names(self, declared_fields, info):
|
|
||||||
fields = super().get_field_names(declared_fields, info)
|
|
||||||
fields.extend(['get_category_display'])
|
|
||||||
return fields
|
|
||||||
|
|
||||||
|
|
||||||
class LabelDistinctSerializer(BulkOrgResourceModelSerializer):
|
class LabelDistinctSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
@ -25,7 +25,7 @@ class ProtocolSettingSerializer(serializers.Serializer):
|
|||||||
sftp_home = serializers.CharField(default="/tmp", label=_("SFTP home"))
|
sftp_home = serializers.CharField(default="/tmp", label=_("SFTP home"))
|
||||||
|
|
||||||
# HTTP
|
# HTTP
|
||||||
auto_fill = serializers.BooleanField(default=False, label=_("Auto fill"))
|
autofile = serializers.BooleanField(default=False, label=_("Autofill"))
|
||||||
username_selector = serializers.CharField(
|
username_selector = serializers.CharField(
|
||||||
default="", allow_blank=True, label=_("Username selector")
|
default="", allow_blank=True, label=_("Username selector")
|
||||||
)
|
)
|
||||||
@ -38,37 +38,26 @@ class ProtocolSettingSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
|
|
||||||
class PlatformAutomationSerializer(serializers.ModelSerializer):
|
class PlatformAutomationSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PlatformAutomation
|
model = PlatformAutomation
|
||||||
fields = [
|
fields = [
|
||||||
"id",
|
"id",
|
||||||
"ansible_enabled",
|
"ansible_enabled", "ansible_config",
|
||||||
"ansible_config",
|
"ping_enabled", "ping_method",
|
||||||
"ping_enabled",
|
"gather_facts_enabled", "gather_facts_method",
|
||||||
"ping_method",
|
"change_secret_enabled", "change_secret_method",
|
||||||
"gather_facts_enabled",
|
"verify_account_enabled", "verify_account_method",
|
||||||
"gather_facts_method",
|
"gather_accounts_enabled", "gather_accounts_method",
|
||||||
"push_account_enabled",
|
|
||||||
"push_account_method",
|
|
||||||
"change_secret_enabled",
|
|
||||||
"change_secret_method",
|
|
||||||
"verify_account_enabled",
|
|
||||||
"verify_account_method",
|
|
||||||
"gather_accounts_enabled",
|
|
||||||
"gather_accounts_method",
|
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"ping_enabled": {"label": "启用资产探测"},
|
"ping_enabled": {"label": "启用资产探测"},
|
||||||
"ping_method": {"label": "探测方式"},
|
"ping_method": {"label": "资产探测方式"},
|
||||||
"gather_facts_enabled": {"label": "启用收集信息"},
|
"gather_facts_enabled": {"label": "收集资产信息"},
|
||||||
"gather_facts_method": {"label": "收集信息方式"},
|
"gather_facts_method": {"label": "收集信息方式"},
|
||||||
"verify_account_enabled": {"label": "启用校验账号"},
|
"verify_account_enabled": {"label": "启用校验账号"},
|
||||||
"verify_account_method": {"label": "校验账号方式"},
|
"verify_account_method": {"label": "校验账号方式"},
|
||||||
"push_account_enabled": {"label": "启用推送账号"},
|
|
||||||
"push_account_method": {"label": "推送账号方式"},
|
|
||||||
"change_secret_enabled": {"label": "启用账号改密"},
|
"change_secret_enabled": {"label": "启用账号改密"},
|
||||||
"change_secret_method": {"label": "账号创建改密方式"},
|
"change_secret_method": {"label": "账号改密方式"},
|
||||||
"gather_accounts_enabled": {"label": "启用账号收集"},
|
"gather_accounts_enabled": {"label": "启用账号收集"},
|
||||||
"gather_accounts_method": {"label": "收集账号方式"},
|
"gather_accounts_method": {"label": "收集账号方式"},
|
||||||
}
|
}
|
||||||
@ -81,13 +70,8 @@ class PlatformProtocolsSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PlatformProtocol
|
model = PlatformProtocol
|
||||||
fields = [
|
fields = [
|
||||||
"id",
|
"id", "name", "port", "primary",
|
||||||
"name",
|
"default", "required", "secret_types",
|
||||||
"port",
|
|
||||||
"primary",
|
|
||||||
"default",
|
|
||||||
"required",
|
|
||||||
"secret_types",
|
|
||||||
"setting",
|
"setting",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -113,17 +97,12 @@ class PlatformSerializer(WritableNestedModelSerializer):
|
|||||||
model = Platform
|
model = Platform
|
||||||
fields_mini = ["id", "name", "internal"]
|
fields_mini = ["id", "name", "internal"]
|
||||||
fields_small = fields_mini + [
|
fields_small = fields_mini + [
|
||||||
"category",
|
"category", "type", "charset",
|
||||||
"type",
|
|
||||||
"charset",
|
|
||||||
]
|
]
|
||||||
fields = fields_small + [
|
fields = fields_small + [
|
||||||
"protocols_enabled",
|
"protocols_enabled", "protocols",
|
||||||
"protocols",
|
"domain_enabled", "su_enabled",
|
||||||
"domain_enabled",
|
"su_method", "automation",
|
||||||
"su_enabled",
|
|
||||||
"su_method",
|
|
||||||
"automation",
|
|
||||||
"comment",
|
"comment",
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
|
@ -103,6 +103,7 @@ def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs):
|
|||||||
# m2m_model.objects.bulk_create(to_create)
|
# m2m_model.objects.bulk_create(to_create)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
RELATED_NODE_IDS = '_related_node_ids'
|
RELATED_NODE_IDS = '_related_node_ids'
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,13 +20,9 @@ logger = get_logger(__file__)
|
|||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def get_node_assets_mapping_for_memory_pub_sub():
|
|
||||||
return RedisPubSub('fm.node_all_asset_ids_memory_mapping')
|
|
||||||
|
|
||||||
|
|
||||||
class NodeAssetsMappingForMemoryPubSub(LazyObject):
|
class NodeAssetsMappingForMemoryPubSub(LazyObject):
|
||||||
def _setup(self):
|
def _setup(self):
|
||||||
self._wrapped = get_node_assets_mapping_for_memory_pub_sub()
|
self._wrapped = RedisPubSub('fm.node_all_asset_ids_memory_mapping')
|
||||||
|
|
||||||
|
|
||||||
node_assets_mapping_for_memory_pub_sub = NodeAssetsMappingForMemoryPubSub()
|
node_assets_mapping_for_memory_pub_sub = NodeAssetsMappingForMemoryPubSub()
|
||||||
|
@ -23,13 +23,13 @@ router.register(r'labels', api.LabelViewSet, 'label')
|
|||||||
router.register(r'nodes', api.NodeViewSet, 'node')
|
router.register(r'nodes', api.NodeViewSet, 'node')
|
||||||
router.register(r'domains', api.DomainViewSet, 'domain')
|
router.register(r'domains', api.DomainViewSet, 'domain')
|
||||||
router.register(r'gateways', api.GatewayViewSet, 'gateway')
|
router.register(r'gateways', api.GatewayViewSet, 'gateway')
|
||||||
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
|
||||||
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
|
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
|
||||||
router.register(r'account-backup-plans', api.AccountBackupPlanViewSet, 'account-backup')
|
router.register(r'account-backup-plans', api.AccountBackupPlanViewSet, 'account-backup')
|
||||||
router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution')
|
router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution')
|
||||||
|
|
||||||
router.register(r'change-secret-automations', api.ChangeSecretAutomationViewSet, 'change-secret-automation')
|
router.register(r'change-secret-automations', api.ChangeSecretAutomationViewSet, 'change-secret-automation')
|
||||||
router.register(r'automation-executions', api.AutomationExecutionViewSet, 'automation-execution')
|
router.register(r'change-secret-executions', api.ChangSecretExecutionViewSet, 'change-secret-execution')
|
||||||
|
router.register(r'gather-account-executions', api.GatherAccountsExecutionViewSet, 'gather-account-execution')
|
||||||
router.register(r'change-secret-records', api.ChangeSecretRecordViewSet, 'change-secret-record')
|
router.register(r'change-secret-records', api.ChangeSecretRecordViewSet, 'change-secret-record')
|
||||||
router.register(r'gather-account-automations', api.GatherAccountsAutomationViewSet, 'gather-account-automation')
|
router.register(r'gather-account-automations', api.GatherAccountsAutomationViewSet, 'gather-account-automation')
|
||||||
|
|
||||||
@ -50,7 +50,6 @@ urlpatterns = [
|
|||||||
name='account-secret-history'),
|
name='account-secret-history'),
|
||||||
|
|
||||||
path('nodes/category/tree/', api.CategoryTreeApi.as_view(), name='asset-category-tree'),
|
path('nodes/category/tree/', api.CategoryTreeApi.as_view(), name='asset-category-tree'),
|
||||||
path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'),
|
|
||||||
path('nodes/children/tree/', api.NodeChildrenAsTreeApi.as_view(), name='node-children-tree'),
|
path('nodes/children/tree/', api.NodeChildrenAsTreeApi.as_view(), name='node-children-tree'),
|
||||||
path('nodes/<uuid:pk>/children/', api.NodeChildrenApi.as_view(), name='node-children'),
|
path('nodes/<uuid:pk>/children/', api.NodeChildrenApi.as_view(), name='node-children'),
|
||||||
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
||||||
|
2
apps/assets/utils/__init__.py
Normal file
2
apps/assets/utils/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .k8s import *
|
||||||
|
from .node import *
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user