mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-16 17:12:53 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
777b655e90 | ||
|
|
6e1736c1a7 | ||
|
|
fe31eb0a44 |
@@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
list_params = [
|
list_params = [
|
||||||
{
|
{
|
||||||
"name": "search",
|
"name": "search",
|
||||||
@@ -11,41 +13,99 @@ list_params = [
|
|||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Which field to use when ordering the results.",
|
"description": "Which field to use when ordering the results.",
|
||||||
"required": False,
|
"required": False,
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"name", "date_created", "date_updated", "created_by",
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "limit",
|
"name": "limit",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Number of results to return per page. Default is 10.",
|
"description": "Number of results to return per page. Default is 10.",
|
||||||
"required": False,
|
"required": False,
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"default": 10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "offset",
|
"name": "offset",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "The initial index from which to return the results.",
|
"description": "The initial index from which to return the results.",
|
||||||
"required": False,
|
"required": False,
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"default": 0
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
unsupported_resources = [
|
||||||
|
"customs", "protocol-settings", "gathered-accounts",
|
||||||
|
"account-template-secrets", 'change-secret-records',
|
||||||
|
'account-backup-executions', 'change-secret-executions',
|
||||||
|
'change-secret-status', 'gather-account-executions',
|
||||||
|
'push-account-executions', 'check-account-executions',
|
||||||
|
'integration-apps', 'asset-permissions-users-relations',
|
||||||
|
'asset-permissions-user-groups-relations', 'asset-permissions-assets-relations',
|
||||||
|
'asset-permissions-nodes-relations', 'terminal-status', 'tasks', 'status',
|
||||||
|
'session-sharing-records', 'endpoints', 'endpoint-rules',
|
||||||
|
'chatai-prompts', 'leak-passwords', 'super-connection-tokens',
|
||||||
|
'system-role-bindings', 'org-role-bindings', 'content-types', 'system-role-permissions',
|
||||||
|
'org-role-permissions', 'system-msg-subscriptions',
|
||||||
|
'celery-period-tasks', 'task-executions', 'adhocs',
|
||||||
|
'user-sessions', 'service-access-logs',
|
||||||
|
'applet-publications', 'applet-host-deployments',
|
||||||
|
'virtual-app-publications', 'applet-host-accounts', 'applet-host-applets',
|
||||||
|
'flows'
|
||||||
|
]
|
||||||
|
|
||||||
|
supported_resources = [
|
||||||
|
# User
|
||||||
|
'users', 'user-groups', 'users-groups-relations',
|
||||||
|
# Asset
|
||||||
|
'assets', 'hosts', 'devices', 'databases', 'webs', 'clouds', 'gpts',
|
||||||
|
'ds', 'platforms', 'nodes', 'zones', 'gateways',
|
||||||
|
# Account
|
||||||
|
'virtual-accounts', 'account-templates', 'account-backups',
|
||||||
|
# Automation
|
||||||
|
'change-secret-automations', 'gather-account-automations',
|
||||||
|
'push-account-automations', 'check-account-automations',
|
||||||
|
'account-risks',
|
||||||
|
# Permission
|
||||||
|
'asset-permissions',
|
||||||
|
# Terminal
|
||||||
|
'terminals', 'replay-storages', 'command-storages',
|
||||||
|
# Applet
|
||||||
|
'applets', 'applet-hosts', 'virtual-apps',
|
||||||
|
# Ops
|
||||||
|
'playbooks', 'jobs',
|
||||||
|
# Audit
|
||||||
|
'ftp-logs', 'login-logs', 'operate-logs', 'password-change-logs', 'job-logs',
|
||||||
|
# Tickets
|
||||||
|
'tickets', 'comments', 'apply-assets', 'apply-nodes',
|
||||||
|
# Acls
|
||||||
|
'login-acls', 'login-asset-acls', 'command-filter-acls',
|
||||||
|
'command-groups', 'connect-method-acls', 'data-masking-rules',
|
||||||
|
# RBAC
|
||||||
|
'roles', 'role-bindings',
|
||||||
|
'system-roles', 'org-roles',
|
||||||
|
# Label
|
||||||
|
'labeled-resources',
|
||||||
|
'labels',
|
||||||
]
|
]
|
||||||
|
|
||||||
common_params = [
|
common_params = [
|
||||||
{
|
{
|
||||||
"name": "resource",
|
"name": "resource",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"description": """Resource to query, e.g. users, assets, permissions, acls, user-groups, policies, nodes, hosts,
|
"description": f"""Resource to query, {supported_resources}
|
||||||
devices, clouds, webs, databases,
|
|
||||||
gpts, ds, customs, platforms, zones, gateways, protocol-settings, labels, virtual-accounts,
|
|
||||||
gathered-accounts, account-templates, account-template-secrets, account-backups, account-backup-executions,
|
|
||||||
change-secret-automations, change-secret-executions, change-secret-records, gather-account-automations,
|
|
||||||
gather-account-executions, push-account-automations, push-account-executions, push-account-records,
|
|
||||||
check-account-automations, check-account-executions, account-risks, integration-apps, asset-permissions,
|
|
||||||
zones, gateways, virtual-accounts, gathered-accounts, account-templates, account-template-secrets,,
|
|
||||||
GET /api/v1/resources/ to get full supported resource.
|
GET /api/v1/resources/ to get full supported resource.
|
||||||
|
if you want to get the resource list, you can set the resource name in the url.
|
||||||
|
if you want to create a resource, you can set the resource name in the url.
|
||||||
|
if you want to get the resource detail, you can set the resource name and id in the url.
|
||||||
|
if you want to update the resource, you can set the resource name and id in the url.
|
||||||
|
if you want to delete the resource, you can set the resource name and id in the url.
|
||||||
""",
|
""",
|
||||||
"required": True,
|
"required": True,
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"enum": supported_resources
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "X-JMS-ORG",
|
"name": "X-JMS-ORG",
|
||||||
|
|||||||
@@ -69,13 +69,13 @@ class ResourceListApi(ProxyMixin, APIView):
|
|||||||
return self._proxy(request, resource)
|
return self._proxy(request, resource)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
operation_id="create_resource_by_type",
|
operation_id="create_resource",
|
||||||
summary="Create resource",
|
summary="Create resource",
|
||||||
parameters=create_params,
|
parameters=create_params,
|
||||||
description="""
|
description="""
|
||||||
Create resource,
|
Create resource,
|
||||||
OPTIONS /api/v1/resources/{resource}/?action=post to get every resource type field type and helptext, and
|
OPTIONS /api/v1/resources/{resource}/?action=post to get every resource type field type and help text,
|
||||||
you will know how to create it.
|
and you will know how to create it.
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
def post(self, request, resource, pk=None):
|
def post(self, request, resource, pk=None):
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from .const import supported_resources
|
||||||
from .utils import get_full_resource_map
|
from .utils import get_full_resource_map
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
@@ -35,6 +36,8 @@ class ResourceTypeListApi(APIView):
|
|||||||
result = []
|
result = []
|
||||||
resource_map = get_full_resource_map()
|
resource_map = get_full_resource_map()
|
||||||
for name, desc in resource_map.items():
|
for name, desc in resource_map.items():
|
||||||
|
if name not in supported_resources:
|
||||||
|
continue
|
||||||
desc = resource_map.get(name, {})
|
desc = resource_map.get(name, {})
|
||||||
resource = {
|
resource = {
|
||||||
"name": name,
|
"name": name,
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import re
|
|||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from django.urls import URLPattern
|
from django.utils.functional import LazyObject
|
||||||
from django.urls import URLResolver
|
from django.urls import URLPattern, URLResolver
|
||||||
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import OpenApiParameter
|
from drf_spectacular.utils import OpenApiParameter
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
@@ -114,9 +115,37 @@ def extract_resource_paths(urlpatterns, prefix='/api/v1/') -> Dict[str, Dict[str
|
|||||||
|
|
||||||
|
|
||||||
def param_dic_to_param(d):
|
def param_dic_to_param(d):
|
||||||
|
# 将 'in' 字段映射到 OpenApiParameter 的 location 常量
|
||||||
|
location_map = {
|
||||||
|
'query': OpenApiParameter.QUERY,
|
||||||
|
'path': OpenApiParameter.PATH,
|
||||||
|
'header': OpenApiParameter.HEADER,
|
||||||
|
'cookie': OpenApiParameter.COOKIE,
|
||||||
|
}
|
||||||
|
location = location_map.get(d['in'], OpenApiParameter.QUERY)
|
||||||
|
|
||||||
|
# 将 type 字符串映射到 OpenApiTypes
|
||||||
|
type_map = {
|
||||||
|
'string': OpenApiTypes.STR,
|
||||||
|
'integer': OpenApiTypes.INT,
|
||||||
|
'number': OpenApiTypes.FLOAT,
|
||||||
|
'boolean': OpenApiTypes.BOOL,
|
||||||
|
'array': OpenApiTypes.OBJECT, # 对于 array 类型,需要特殊处理
|
||||||
|
'object': OpenApiTypes.OBJECT,
|
||||||
|
}
|
||||||
|
param_type = type_map.get(d['type'], OpenApiTypes.STR)
|
||||||
|
|
||||||
|
enum = None
|
||||||
|
if d.get('enum'):
|
||||||
|
enum = d['enum']
|
||||||
|
|
||||||
return OpenApiParameter(
|
return OpenApiParameter(
|
||||||
name=d['name'], location=d['in'],
|
name=d['name'],
|
||||||
description=d['description'], type=d['type'], required=d.get('required', False)
|
location=location,
|
||||||
|
description=d['description'],
|
||||||
|
type=param_type,
|
||||||
|
required=d.get('required', False),
|
||||||
|
enum=enum
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ urlpatterns += [
|
|||||||
path('core/jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
|
path('core/jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
|
||||||
]
|
]
|
||||||
|
|
||||||
DOC_TTL = 60 * 60
|
DOC_TTL = 1
|
||||||
DOC_VERSION = uuid.uuid4().hex
|
DOC_VERSION = uuid.uuid4().hex
|
||||||
cache_kwargs = {
|
cache_kwargs = {
|
||||||
'cache_timeout': DOC_TTL,
|
'cache_timeout': DOC_TTL,
|
||||||
@@ -98,7 +98,7 @@ cache_kwargs = {
|
|||||||
}
|
}
|
||||||
# docs 路由
|
# docs 路由
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
path('api/swagger.json', views.get_swagger_view(ui='json', **cache_kwargs), name='schema-json'),
|
path('api/swagger.json', views.get_swagger_view(ui='json'), name='schema-json'),
|
||||||
path('api/swagger.yaml', views.get_swagger_view(ui='yaml', **cache_kwargs), name='schema'),
|
path('api/swagger.yaml', views.get_swagger_view(ui='yaml', **cache_kwargs), name='schema'),
|
||||||
re_path('api/docs/?', views.get_swagger_view(ui='swagger', **cache_kwargs), name="docs"),
|
re_path('api/docs/?', views.get_swagger_view(ui='swagger', **cache_kwargs), name="docs"),
|
||||||
re_path('api/redoc/?', views.get_swagger_view(ui='redoc', **cache_kwargs), name='redoc'),
|
re_path('api/redoc/?', views.get_swagger_view(ui='redoc', **cache_kwargs), name='redoc'),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class CustomSchemaGenerator(SchemaGenerator):
|
|||||||
|
|
||||||
class CustomAutoSchema(AutoSchema):
|
class CustomAutoSchema(AutoSchema):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.from_mcp = kwargs.get('from_mcp', False)
|
self.from_mcp = True
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def map_parsers(self):
|
def map_parsers(self):
|
||||||
@@ -30,19 +30,19 @@ class CustomAutoSchema(AutoSchema):
|
|||||||
tags = ['_'.join(operation_keys[:2])]
|
tags = ['_'.join(operation_keys[:2])]
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
def get_operation(self, path, *args, **kwargs):
|
# def get_operation(self, path, *args, **kwargs):
|
||||||
if path.endswith('render-to-json/'):
|
# if path.endswith('render-to-json/'):
|
||||||
return None
|
|
||||||
# if not path.startswith('/api/v1/users'):
|
|
||||||
# return None
|
# return None
|
||||||
operation = super().get_operation(path, *args, **kwargs)
|
# # if not path.startswith('/api/v1/users'):
|
||||||
if not operation:
|
# # return None
|
||||||
return operation
|
# operation = super().get_operation(path, *args, **kwargs)
|
||||||
|
# if not operation:
|
||||||
|
# return operation
|
||||||
|
|
||||||
if not operation.get('summary', ''):
|
# if not operation.get('summary', ''):
|
||||||
operation['summary'] = operation.get('operationId')
|
# operation['summary'] = operation.get('operationId')
|
||||||
|
|
||||||
return operation
|
# return operation
|
||||||
|
|
||||||
def get_operation_id(self):
|
def get_operation_id(self):
|
||||||
tokenized_path = self._tokenize_path()
|
tokenized_path = self._tokenize_path()
|
||||||
@@ -118,7 +118,8 @@ class CustomAutoSchema(AutoSchema):
|
|||||||
'change-secret-dashboard', '/copy-to-assets/',
|
'change-secret-dashboard', '/copy-to-assets/',
|
||||||
'/move-to-assets/', 'dashboard', 'index', 'countries',
|
'/move-to-assets/', 'dashboard', 'index', 'countries',
|
||||||
'/resources/cache/', 'profile/mfa', 'profile/password',
|
'/resources/cache/', 'profile/mfa', 'profile/password',
|
||||||
'profile/permissions', 'prometheus', 'constraints'
|
'profile/permissions', 'prometheus', 'constraints',
|
||||||
|
'/api/swagger.json', '/api/swagger.yaml',
|
||||||
]
|
]
|
||||||
for p in excludes:
|
for p in excludes:
|
||||||
if path.find(p) >= 0:
|
if path.find(p) >= 0:
|
||||||
@@ -132,14 +133,15 @@ class CustomAutoSchema(AutoSchema):
|
|||||||
|
|
||||||
apps = []
|
apps = []
|
||||||
if self.from_mcp:
|
if self.from_mcp:
|
||||||
apps = [
|
# apps = [
|
||||||
'ops', 'tickets', 'authentication',
|
# 'ops', 'tickets', 'authentication',
|
||||||
'settings', 'xpack', 'terminal', 'rbac',
|
# 'settings', 'xpack', 'terminal', 'rbac',
|
||||||
'notifications', 'promethues', 'acls'
|
# 'notifications', 'promethues', 'acls'
|
||||||
]
|
# ]
|
||||||
|
apps = ['resources']
|
||||||
|
|
||||||
app_name = parts[3]
|
app_name = parts[3]
|
||||||
if app_name in apps:
|
if app_name not in apps:
|
||||||
return True
|
return True
|
||||||
models = []
|
models = []
|
||||||
model = parts[4]
|
model = parts[4]
|
||||||
@@ -191,6 +193,9 @@ class CustomAutoSchema(AutoSchema):
|
|||||||
if not operation.get('summary', ''):
|
if not operation.get('summary', ''):
|
||||||
operation['summary'] = operation.get('operationId')
|
operation['summary'] = operation.get('operationId')
|
||||||
|
|
||||||
|
if self.is_excluded():
|
||||||
|
return None
|
||||||
|
|
||||||
exclude_operations = [
|
exclude_operations = [
|
||||||
'orgs_orgs_read', 'orgs_orgs_update', 'orgs_orgs_delete',
|
'orgs_orgs_read', 'orgs_orgs_update', 'orgs_orgs_delete',
|
||||||
'orgs_orgs_create', 'orgs_orgs_partial_update',
|
'orgs_orgs_create', 'orgs_orgs_partial_update',
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ from rest_framework.response import Response
|
|||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
class SwaggerUI(LoginRequiredMixin, SpectacularSwaggerView):
|
class SwaggerUI( SpectacularSwaggerView):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Redoc(LoginRequiredMixin, SpectacularRedocView):
|
class Redoc(LoginRequiredMixin, SpectacularRedocView):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SchemeMixin:
|
class SchemeMixin:
|
||||||
|
permission_classes = []
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
schema = super().get(request, *args, **kwargs).data
|
schema = super().get(request, *args, **kwargs).data
|
||||||
host = request.get_host()
|
host = request.get_host()
|
||||||
@@ -37,7 +38,7 @@ class SchemeMixin:
|
|||||||
}
|
}
|
||||||
return Response(schema)
|
return Response(schema)
|
||||||
|
|
||||||
@method_decorator(cache_page(60 * 5,), name="dispatch")
|
# @method_decorator(cache_page(60 * 5,), name="dispatch")
|
||||||
class JsonApi(SchemeMixin, SpectacularJSONAPIView):
|
class JsonApi(SchemeMixin, SpectacularJSONAPIView):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
6
uv.lock
generated
6
uv.lock
generated
@@ -2595,7 +2595,7 @@ requires-dist = [
|
|||||||
{ name = "python-cas", specifier = "==1.6.0" },
|
{ name = "python-cas", specifier = "==1.6.0" },
|
||||||
{ name = "python-daemon", specifier = "==3.0.1" },
|
{ name = "python-daemon", specifier = "==3.0.1" },
|
||||||
{ name = "python-dateutil", specifier = "==2.8.2" },
|
{ name = "python-dateutil", specifier = "==2.8.2" },
|
||||||
{ name = "python-ldap", specifier = "==3.4.3" },
|
{ name = "python-ldap", specifier = "==3.4.5" },
|
||||||
{ name = "python-nmap", specifier = "==0.7.1" },
|
{ name = "python-nmap", specifier = "==0.7.1" },
|
||||||
{ name = "python-redis-lock", specifier = "==4.0.0" },
|
{ name = "python-redis-lock", specifier = "==4.0.0" },
|
||||||
{ name = "python3-saml", specifier = "==1.16.0" },
|
{ name = "python3-saml", specifier = "==1.16.0" },
|
||||||
@@ -4211,13 +4211,13 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-ldap"
|
name = "python-ldap"
|
||||||
version = "3.4.3"
|
version = "3.4.5"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pyasn1" },
|
{ name = "pyasn1" },
|
||||||
{ name = "pyasn1-modules" },
|
{ name = "pyasn1-modules" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/7d/de9ae3e5843de77eae3a60c1e70ef5cad9960db50521e8459f7d567a1d1d/python-ldap-3.4.3.tar.gz", hash = "sha256:ab26c519a0ef2a443a2a10391fa3c5cb52d7871323399db949ebfaa9f25ee2a0", size = 377438, upload-time = "2022-09-20T15:46:21.283Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/0c/88/8d2797decc42e1c1cdd926df4f005e938b0643d0d1219c08c2b5ee8ae0c0/python_ldap-3.4.5.tar.gz", hash = "sha256:b2f6ef1c37fe2c6a5a85212efe71311ee21847766a7d45fcb711f3b270a5f79a", size = 388482, upload-time = "2025-10-10T20:00:39.06Z" }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-nmap"
|
name = "python-nmap"
|
||||||
|
|||||||
Reference in New Issue
Block a user