feat: Support playbook, adhoc share

pull/14124/head
wangruidong 2024-09-06 17:41:09 +08:00 committed by Bryan
parent b6f3c23787
commit f55869a449
13 changed files with 128 additions and 34 deletions

View File

@ -69,7 +69,7 @@
"Address": "Address",
"AdhocCreate": "Create the command",
"AdhocDetail": "Command details",
"AdhocManage": "Command",
"AdhocManage": "Script",
"AdhocUpdate": "Update the command",
"Advanced": "Advanced settings",
"AfterChange": "After changes",

View File

@ -69,7 +69,7 @@
"Address": "アドレス",
"AdhocCreate": "アドホックコマンドを作成",
"AdhocDetail": "コマンド詳細",
"AdhocManage": "コマンド",
"AdhocManage": "スクリプト管理",
"AdhocUpdate": "コマンドを更新",
"Advanced": "高度な設定",
"AfterChange": "変更後",

View File

@ -69,7 +69,7 @@
"Address": "地址",
"AdhocCreate": "创建命令",
"AdhocDetail": "命令详情",
"AdhocManage": "命令管理",
"AdhocManage": "脚本管理",
"AdhocUpdate": "更新命令",
"Advanced": "高级设置",
"AfterChange": "变更后",

View File

@ -87,7 +87,7 @@
"Addressee": "收件人",
"AdhocCreate": "創建命令",
"AdhocDetail": "命令詳情",
"AdhocManage": "命令管理",
"AdhocManage": "腳本管理",
"AdhocUpdate": "更新命令",
"Admin": "管理員",
"AdminUser": "特權用戶",

View File

@ -1,22 +1,37 @@
# -*- coding: utf-8 -*-
from orgs.mixins.api import OrgBulkModelViewSet
from django.db.models import Q
from common.api.generic import JMSBulkModelViewSet
from common.utils.http import is_true
from rbac.permissions import RBACPermission
from ..const import Scope
from ..models import AdHoc
from ..serializers import (
AdHocSerializer
)
from ..serializers import AdHocSerializer
__all__ = [
'AdHocViewSet'
]
class AdHocViewSet(OrgBulkModelViewSet):
class AdHocViewSet(JMSBulkModelViewSet):
queryset = AdHoc.objects.all()
serializer_class = AdHocSerializer
permission_classes = (RBACPermission,)
search_fields = ('name', 'comment')
model = AdHoc
filterset_fields = ['scope', 'creator']
def check_object_permissions(self, request, obj):
if request.method != 'GET' and obj.creator != request.user:
self.permission_denied(
request, message={"detail": "Deleting other people's script is not allowed"}
)
return super().check_object_permissions(request, obj)
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(creator=self.request.user)
user = self.request.user
if is_true(self.request.query_params.get('only_myself')):
queryset = queryset.filter(creator=user)
else:
queryset = queryset.filter(Q(creator=user) | Q(scope=Scope.public))
return queryset

View File

@ -4,13 +4,16 @@ import zipfile
from django.conf import settings
from django.core.exceptions import SuspiciousFileOperation
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from rest_framework import status
from common.api.generic import JMSBulkModelViewSet
from common.exceptions import JMSException
from orgs.mixins.api import OrgBulkModelViewSet
from common.utils.http import is_true
from rbac.permissions import RBACPermission
from ..const import Scope
from ..exception import PlaybookNoValidEntry
from ..models import Playbook
from ..serializers.playbook import PlaybookSerializer
@ -28,11 +31,19 @@ def unzip_playbook(src, dist):
fz.extract(file, dist)
class PlaybookViewSet(OrgBulkModelViewSet):
class PlaybookViewSet(JMSBulkModelViewSet):
serializer_class = PlaybookSerializer
permission_classes = (RBACPermission,)
model = Playbook
queryset = Playbook.objects.all()
search_fields = ('name', 'comment')
filterset_fields = ['scope', 'creator']
def check_object_permissions(self, request, obj):
if request.method != 'GET' and obj.creator != request.user:
self.permission_denied(
request, message={"detail": "Deleting other people's playbook is not allowed"}
)
return super().check_object_permissions(request, obj)
def perform_destroy(self, instance):
if instance.job_set.exists():
@ -45,7 +56,11 @@ class PlaybookViewSet(OrgBulkModelViewSet):
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(creator=self.request.user)
user = self.request.user
if is_true(self.request.query_params.get('only_myself')):
queryset = queryset.filter(creator=user)
else:
queryset = queryset.filter(Q(creator=user) | Q(scope=Scope.public))
return queryset
def perform_create(self, serializer):
@ -85,7 +100,8 @@ class PlaybookFileBrowserAPIView(APIView):
def get(self, request, **kwargs):
playbook_id = kwargs.get('pk')
playbook = self.get_playbook(playbook_id)
user = self.request.user
playbook = get_object_or_404(Playbook, Q(creator=user) | Q(scope=Scope.public), id=playbook_id)
work_path = playbook.work_dir
file_key = request.query_params.get('key', '')
if file_key:

View File

@ -1,5 +1,5 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_lazy as _, pgettext_lazy
class StrategyChoice(models.TextChoices):
@ -80,3 +80,8 @@ class JobStatus(models.TextChoices):
CELERY_LOG_MAGIC_MARK = b'\x00\x00\x00\x00\x00'
COMMAND_EXECUTION_DISABLED = _('Command execution disabled')
class Scope(models.TextChoices):
public = 'public', pgettext_lazy("scope", 'Public')
private = 'private', _('Private')

View File

@ -0,0 +1,41 @@
# Generated by Django 4.1.13 on 2024-09-06 08:32
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('ops', '0002_celerytask'),
]
operations = [
migrations.AlterUniqueTogether(
name='adhoc',
unique_together={('name', 'creator')},
),
migrations.AlterUniqueTogether(
name='playbook',
unique_together={('name', 'creator')},
),
migrations.AddField(
model_name='adhoc',
name='scope',
field=models.CharField(default='public', max_length=64, verbose_name='Scope'),
),
migrations.AddField(
model_name='playbook',
name='scope',
field=models.CharField(default='public', max_length=64, verbose_name='Scope'),
),
migrations.RemoveField(
model_name='adhoc',
name='org_id',
),
migrations.RemoveField(
model_name='playbook',
name='org_id',
),
]

View File

@ -8,14 +8,13 @@ from common.utils import get_logger
__all__ = ["AdHoc"]
from ops.const import AdHocModules
from orgs.mixins.models import JMSOrgBaseModel
from common.db.models import JMSBaseModel
from ops.const import AdHocModules, Scope
logger = get_logger(__file__)
class AdHoc(JMSOrgBaseModel):
class AdHoc(JMSBaseModel):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'))
pattern = models.CharField(max_length=1024, verbose_name=_("Pattern"), default='all')
@ -24,6 +23,7 @@ class AdHoc(JMSOrgBaseModel):
args = models.CharField(max_length=8192, default='', verbose_name=_('Args'))
creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True)
comment = models.CharField(max_length=1024, default='', verbose_name=_('Comment'), null=True, blank=True)
scope = models.CharField(max_length=64, default=Scope.public, verbose_name=_('Scope'))
@property
def row_count(self):
@ -40,5 +40,5 @@ class AdHoc(JMSOrgBaseModel):
return "{}: {}".format(self.module, self.args)
class Meta:
unique_together = [('name', 'org_id', 'creator')]
unique_together = [('name', 'creator')]
verbose_name = _("Adhoc")

View File

@ -6,9 +6,9 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from private_storage.fields import PrivateFileField
from ops.const import CreateMethods
from common.db.models import JMSBaseModel
from ops.const import CreateMethods, Scope
from ops.exception import PlaybookNoValidEntry
from orgs.mixins.models import JMSOrgBaseModel
dangerous_keywords = (
'hosts:localhost',
@ -23,7 +23,9 @@ dangerous_keywords = (
)
class Playbook(JMSOrgBaseModel):
class Playbook(JMSBaseModel):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'), null=True)
path = PrivateFileField(upload_to='playbooks/')
@ -31,6 +33,7 @@ class Playbook(JMSOrgBaseModel):
comment = models.CharField(max_length=1024, default='', verbose_name=_('Comment'), null=True, blank=True)
create_method = models.CharField(max_length=128, choices=CreateMethods.choices, default=CreateMethods.blank,
verbose_name=_('CreateMethod'))
scope = models.CharField(max_length=64, default=Scope.public, verbose_name=_('Scope'))
vcs_url = models.CharField(max_length=1024, default='', verbose_name=_('VCS URL'), null=True, blank=True)
def __str__(self):
@ -84,6 +87,6 @@ class Playbook(JMSOrgBaseModel):
return work_dir
class Meta:
unique_together = [('name', 'org_id', 'creator')]
unique_together = [('name', 'creator')]
verbose_name = _("Playbook")
ordering = ['date_created']

View File

@ -1,17 +1,19 @@
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from common.serializers.fields import ReadableHiddenField
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from common.serializers.fields import ReadableHiddenField, LabeledChoiceField
from common.serializers.mixin import CommonBulkModelSerializer
from .mixin import ScopeSerializerMixin
from ..const import Scope
from ..models import AdHoc
class AdHocSerializer(BulkOrgResourceModelSerializer):
class AdHocSerializer(ScopeSerializerMixin, CommonBulkModelSerializer):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = AdHoc
read_only_field = ["id", "creator", "date_created", "date_updated"]
fields = read_only_field + ["id", "name", "module", "args", "comment"]
fields = read_only_field + ["id", "name", "scope", "module", "args", "comment"]

View File

@ -0,0 +1,11 @@
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from common.serializers.fields import LabeledChoiceField
from ..const import Scope
class ScopeSerializerMixin(serializers.Serializer):
scope = LabeledChoiceField(
choices=Scope.choices, default=Scope.public, label=_("Scope")
)

View File

@ -3,8 +3,9 @@ import os
from rest_framework import serializers
from common.serializers.fields import ReadableHiddenField
from common.serializers.mixin import CommonBulkModelSerializer
from ops.models import Playbook
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .mixin import ScopeSerializerMixin
def parse_playbook_name(path):
@ -12,7 +13,7 @@ def parse_playbook_name(path):
return file_name.split(".")[-2]
class PlaybookSerializer(BulkOrgResourceModelSerializer):
class PlaybookSerializer(ScopeSerializerMixin, CommonBulkModelSerializer):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
path = serializers.FileField(required=False)
@ -26,6 +27,6 @@ class PlaybookSerializer(BulkOrgResourceModelSerializer):
model = Playbook
read_only_fields = ["id", "date_created", "date_updated"]
fields = read_only_fields + [
"id", 'path', "name", "comment", "creator",
"id", 'path', 'scope', "name", "comment", "creator",
'create_method', 'vcs_url',
]