mirror of https://github.com/jumpserver/jumpserver
feat: Support playbook, adhoc share
parent
b6f3c23787
commit
f55869a449
|
@ -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",
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
"Address": "アドレス",
|
||||
"AdhocCreate": "アドホックコマンドを作成",
|
||||
"AdhocDetail": "コマンド詳細",
|
||||
"AdhocManage": "コマンド",
|
||||
"AdhocManage": "スクリプト管理",
|
||||
"AdhocUpdate": "コマンドを更新",
|
||||
"Advanced": "高度な設定",
|
||||
"AfterChange": "変更後",
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
"Address": "地址",
|
||||
"AdhocCreate": "创建命令",
|
||||
"AdhocDetail": "命令详情",
|
||||
"AdhocManage": "命令管理",
|
||||
"AdhocManage": "脚本管理",
|
||||
"AdhocUpdate": "更新命令",
|
||||
"Advanced": "高级设置",
|
||||
"AfterChange": "变更后",
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
"Addressee": "收件人",
|
||||
"AdhocCreate": "創建命令",
|
||||
"AdhocDetail": "命令詳情",
|
||||
"AdhocManage": "命令管理",
|
||||
"AdhocManage": "腳本管理",
|
||||
"AdhocUpdate": "更新命令",
|
||||
"Admin": "管理員",
|
||||
"AdminUser": "特權用戶",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -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")
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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")
|
||||
)
|
|
@ -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',
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue