diff --git a/apps/authentication/api/face.py b/apps/authentication/api/face.py index 0e9ca3758..08c1ce10e 100644 --- a/apps/authentication/api/face.py +++ b/apps/authentication/api/face.py @@ -1,5 +1,6 @@ from django.core.cache import cache from django.utils.translation import gettext as _ +from rest_framework.serializers import Serializer from rest_framework.exceptions import NotFound from rest_framework.generics import CreateAPIView, RetrieveAPIView from rest_framework.permissions import AllowAny @@ -104,6 +105,7 @@ class FaceCallbackApi(AuthMixin, CreateAPIView): class FaceContextApi(AuthMixin, RetrieveAPIView, CreateAPIView): permission_classes = (AllowAny,) face_token_session_key = FACE_SESSION_KEY + serializer_class = Serializer @staticmethod def get_face_cache_key(token): diff --git a/apps/authentication/api/session.py b/apps/authentication/api/session.py index eff7b1678..df706e08d 100644 --- a/apps/authentication/api/session.py +++ b/apps/authentication/api/session.py @@ -6,6 +6,7 @@ from django.contrib.auth import logout from django.contrib.auth.models import AnonymousUser from rest_framework import generics from rest_framework import status +from rest_framework.serializers import Serializer from rest_framework.response import Response from common.sessions.cache import user_session_manager @@ -52,6 +53,7 @@ class UserSessionManager: class UserSessionApi(generics.RetrieveDestroyAPIView): permission_classes = () + serializer_class = Serializer def retrieve(self, request, *args, **kwargs): if isinstance(request.user, AnonymousUser): diff --git a/apps/authentication/forms.py b/apps/authentication/forms.py index d7a1df484..05c4a8c52 100644 --- a/apps/authentication/forms.py +++ b/apps/authentication/forms.py @@ -81,3 +81,13 @@ def get_user_login_form_cls(*, captcha=False): bases.append(CaptchaMixin) bases.append(UserLoginForm) return type('UserLoginForm', tuple(bases), {}) + + +def get_comprehensive_user_login_form_cls(): + bases = [ + ChallengeMixin, + UserCheckOtpCodeForm, + CaptchaMixin, + UserLoginForm + ] + return type('UserLoginForm', tuple(bases), {}) diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 4138c4ffa..cf8ca77e2 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -30,7 +30,7 @@ from users.utils import ( ) from .. import mixins, errors from ..const import RSA_PRIVATE_KEY, RSA_PUBLIC_KEY, USER_LOGIN_GUARD_VIEW_REDIRECT_FIELD -from ..forms import get_user_login_form_cls +from ..forms import get_user_login_form_cls, get_comprehensive_user_login_form_cls from ..utils import get_auth_methods __all__ = [ @@ -253,6 +253,9 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView): return get_user_login_form_cls(captcha=True) else: return get_user_login_form_cls() + + def get_comprehensive_form_class(self): + return get_comprehensive_user_login_form_cls() def clear_rsa_key(self): self.request.session[RSA_PRIVATE_KEY] = None diff --git a/apps/settings/api/i18n.py b/apps/settings/api/i18n.py index fecfdb15b..1dca350b7 100644 --- a/apps/settings/api/i18n.py +++ b/apps/settings/api/i18n.py @@ -3,6 +3,7 @@ import os from django.conf import settings from django.utils._os import safe_join +from rest_framework.serializers import Serializer from rest_framework.generics import RetrieveAPIView from rest_framework.permissions import AllowAny from rest_framework.response import Response @@ -14,6 +15,7 @@ class ComponentI18nApi(RetrieveAPIView): base_path = 'locale' permission_classes = [AllowAny] lang_data = {} + serializer_class = Serializer def get_component_translations(self, name): if not settings.DEBUG and name in self.lang_data: diff --git a/utils/view_schema/generate.py b/utils/view_schema/generate.py new file mode 100644 index 000000000..745e1a106 --- /dev/null +++ b/utils/view_schema/generate.py @@ -0,0 +1,537 @@ +import json +import os +import sys +from turtle import up + +import django +from django.urls import get_resolver +from django.urls.resolvers import URLPattern, URLResolver + +# 获取项目根目录(jumpserver 目录) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +APP_DIR = os.path.join(BASE_DIR, 'apps') + +# 不改变工作目录,直接加入 sys.path +sys.path.insert(0, APP_DIR) +sys.path.insert(0, BASE_DIR) + +# 设置 Django 环境 +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings") +django.setup() + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +OUTPUT_FILE_DIR = os.path.join(CURRENT_DIR, 'output') +os.makedirs(OUTPUT_FILE_DIR, exist_ok=True) + +from rest_framework.views import APIView +from rest_framework.permissions import OperandHolder, AND, OR, NOT +from django.views.generic.base import View +from django.views.generic.edit import FormView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.core.handlers.asgi import ASGIRequest + +from rest_framework.permissions import AllowAny, IsAuthenticated +from rbac.permissions import RBACPermission +from common.utils import lazyproperty +# 尝试使用模拟请求 +scope = { + 'type': 'http', + 'method': 'GET', + 'path': '/', + 'query_string': b'', + 'headers': [], +} + +async def receive(): + return {'type': 'http.request', 'body': b''} + +request = ASGIRequest(scope, receive) + + +def log(message): + print(message) + + +class FieldsGenerator: + + def write_fields_schema(self): + return {} + + def required_fields(self): + return [] + + +class FormFieldsGenerator(FieldsGenerator): + def __init__(self, raw_class, view): + self.raw_class = raw_class + self.view = view + + + @lazyproperty + def fields(self): + return self.raw_class().fields + + def write_fields_schema(self): + schema = self.get_fields_schema(self.fields) + return schema + + def required_fields(self): + fields = [name for name, field in self.fields.items() if field.required] + return fields + + def get_fields_schema(self, fields): + schemas = {} + for name, field in fields.items(): + schema = { + 'type': self.get_field_type(field), + } + description = getattr(field, 'help_text', '') + if description: + schema['description'] = str(description) + schemas[name] = schema + return schemas + + def get_field_type(self, field): + """将 Django Form Field 类型映射到 JSON Schema 类型""" + from django import forms + + type_mapping = { + forms.CharField: 'string', + forms.EmailField: 'string', + forms.URLField: 'string', + forms.SlugField: 'string', + forms.UUIDField: 'string', + forms.RegexField: 'string', + forms.FileField: 'string', + forms.ImageField: 'string', + forms.FilePathField: 'string', + forms.GenericIPAddressField: 'string', + forms.IntegerField: 'integer', + forms.FloatField: 'number', + forms.DecimalField: 'number', + forms.BooleanField: 'boolean', + forms.NullBooleanField: 'boolean', + forms.DateField: 'string', + forms.TimeField: 'string', + forms.DateTimeField: 'string', + forms.DurationField: 'string', + forms.MultipleChoiceField: 'array', + forms.TypedMultipleChoiceField: 'array', + forms.ModelMultipleChoiceField: 'array', + forms.ChoiceField: 'string', + forms.TypedChoiceField: 'string', + forms.ModelChoiceField: 'string', + forms.JSONField: 'object', + } + + for field_type, json_type in type_mapping.items(): + if isinstance(field, field_type): + return json_type + + return 'string' + + +class SerializerFieldsGenerator(FieldsGenerator): + + def __init__(self, raw_class, view): + self.raw_class = raw_class + self.view = view + + @lazyproperty + def fields(self): + fields = {} + try: + fields = self.raw_class().fields + except Exception as e: + if hasattr(self.raw_class, '_declared_fields'): + fields = self.raw_class._declared_fields + return fields + + @lazyproperty + def write_fields(self): + fields = {} + for name, field in self.fields.items(): + if field.read_only: + continue + fields[name] = field + return fields + + def get_fields_schema(self, fields): + schemas = {} + if not hasattr(fields, 'items'): + return {} + for name, field in fields.items(): + schema = self.get_field_schema(field) + if hasattr(field, 'child'): + _fields = field.child + _fields_schema = self.get_fields_schema(_fields) + schema['properties'] = _fields_schema + schemas[name] = schema + return schemas + + def get_field_schema(self, field): + schema = { + 'type': self.get_field_type(field), + } + if description := getattr(field, 'help_text', ''): + schema['description'] = str(description) + extra_schema = self.get_field_extra_schema(field) + schema.update(extra_schema) + return schema + + def field_is_nested(self, field): + from rest_framework import serializers + nested_field_types = ( + serializers.Serializer, + serializers.ModelSerializer, + ) + return isinstance(field, nested_field_types) + + def get_field_extra_schema(self, field): + """获取字段的正则表达式模式""" + patterns = {} + # 检查 validators 中是否有正则验证器 + if hasattr(field, 'validators'): + for validator in field.validators: + if hasattr(validator, 'regex'): + patterns['pattern'] = str(validator.regex.pattern) + break + # 针对特定字段类型添加模式 + from rest_framework import serializers + if isinstance(field, serializers.EmailField): + patterns['format'] = 'email' + elif isinstance(field, serializers.URLField): + patterns['format'] = 'uri' + elif isinstance(field, (serializers.DateTimeField, serializers.DateField, serializers.TimeField)): + patterns['format'] = 'date-time' + + # 添加长度限制 + if hasattr(field, 'max_length') and field.max_length: + patterns['maxLength'] = field.max_length + if hasattr(field, 'min_length') and field.min_length: + patterns['minLength'] = field.min_length + + # 添加数值范围 + if hasattr(field, 'max_value') and field.max_value is not None: + patterns['maximum'] = field.max_value + if hasattr(field, 'min_value') and field.min_value is not None: + patterns['minimum'] = field.min_value + + if choices := self.get_field_choices(field): + patterns['enum'] = choices + return patterns + + def get_field_choices(self, field): + from rest_framework import serializers + choices = [] + field_need_query_db = isinstance(field, ( + serializers.PrimaryKeyRelatedField, # 会查询数据库 + serializers.StringRelatedField, # 会查询数据库 + serializers.SlugRelatedField, # 会查询数据库 + serializers.HyperlinkedRelatedField, # 会查询数据库 + serializers.HyperlinkedIdentityField,# 会查询数据库 + serializers.RelatedField, # 基类,会查询数据库 + serializers.ManyRelatedField, # 会查询数据库 + serializers.ListSerializer # 可能会查询数据库 + )) + if field_need_query_db: + return choices # 不返回需要查询数据库的字段选项 + + choices = getattr(field, 'choices', []) + if not choices: + return choices + + if isinstance(choices, dict): + # choices 可能是字典、列表或元组 + choices = list(choices.keys()) + elif isinstance(choices, (list, tuple)): + # choices 可能是 [(value, label), ...] 或 [value, ...] + for choice in choices: + if isinstance(choice, (list, tuple)) and len(choice) == 2: + choices.append(choice[0]) + else: + choices.append(choice) + return choices + + def get_field_type(self, field): + """将 Python 字段类型映射到 JSON Schema 类型""" + from rest_framework import serializers + + if self.field_is_nested(field): + return 'object' + + type_mapping = { + serializers.CharField: 'string', + serializers.EmailField: 'string', + serializers.URLField: 'string', + serializers.UUIDField: 'string', + serializers.SlugField: 'string', + serializers.ChoiceField: 'string', + serializers.IntegerField: 'integer', + serializers.FloatField: 'number', + serializers.DecimalField: 'number', + serializers.BooleanField: 'boolean', + serializers.DateTimeField: 'string', + serializers.DateField: 'string', + serializers.TimeField: 'string', + serializers.ListField: 'array', + serializers.DictField: 'object', + serializers.JSONField: 'object', + } + + for field_type, json_type in type_mapping.items(): + if isinstance(field, field_type): + return json_type + + return 'string' # 默认类型 + + def write_fields_schema(self): + fields = self.write_fields + if not fields: + return {} + schema = self.get_fields_schema(fields) + return schema + + def required_fields(self): + required = [] + for name, field in self.write_fields.items(): + if field.required: + required.append(name) + return required + + +class CustomView: + + def __init__(self, view_func): + self.view_func = view_func + + @property + def view_class(self): + cls = getattr(self.view_func, 'view_class', None) + if not cls: + cls = getattr(self.view_func, 'cls', None) + return cls + + @property + def view_path(self): + if self.view_class: + v = self.view_class + else: + v = self.view_func + return f'{v.__module__}.{v.__name__}' + + @property + def view_type(self): + if self.view_class: + return 'class' + else: + return 'function' + + @lazyproperty + def fields_generator(self): + generator = None + if self.view_class: + if issubclass(self.view_class, FormView): + generator = self.get_form_fields_generator() + if issubclass(self.view_class, APIView): + generator= self.get_serializer_fields_generator() + if not generator: + generator = FieldsGenerator() + return generator + + @property + def write_fields_schema(self): + return self.fields_generator.write_fields_schema() + + @property + def required_fields(self): + return self.fields_generator.required_fields() + + def get_form_fields_generator(self): + if hasattr(self.view_class, 'get_comprehensive_form_class'): + view_instance = self.view_class(request=request) + form_class = view_instance.get_comprehensive_form_class() + else: + form_class = getattr(self.view_class, 'form_class', None) + if not form_class: + if hasattr(self.view_class, 'get_form_class'): + # TODO: 实例化 view 类需要传入 request 参数 + view_instance = self.view_class(request=request) + form_class = view_instance.get_form_class() + if form_class: + return FormFieldsGenerator(raw_class=form_class, view=self) + + def get_serializer_fields_generator(self): + serializer_class = getattr(self.view_class, 'serializer_class', None) + if not serializer_class: + if hasattr(self.view_class, 'get_serializer_class'): + # TODO: 实例化 view 类需要传入 request 参数 + view_instance = self.view_class(request=request) + serializer_class = view_instance.get_serializer_class() + if serializer_class: + return SerializerFieldsGenerator(raw_class=serializer_class, view=self) + + @property + def query_fields_schema(self): + return {} + + @lazyproperty + def requires_auth(self): + if self.view_class: + return self.check_view_class_requires_auth() + else: + return self.check_view_func_requires_auth() + + def check_view_class_requires_auth(self): + if issubclass(self.view_class, LoginRequiredMixin): + return True + + permission_classes = getattr(self.view_class, 'permission_classes', []) + if not permission_classes: + return False + + return self.check_permission_classes_requires_auth(permission_classes) + + def check_permission_classes_requires_auth(self, permission_classes, operator=AND): + if operator == AND: + for pc in permission_classes: + if self.check_permission_class_requires_auth(pc): + return True + return False + elif operator == OR: + for pc in permission_classes: + if not self.check_permission_class_requires_auth(pc): + return False + return True + elif operator == NOT: + raise ValueError('NOT operator is not supported in permission_classes') + else: + return False + + def check_permission_class_requires_auth(self, permission_class): + if isinstance(permission_class, OperandHolder): + operator = permission_class.operator_class + op1_class = permission_class.op1_class + op2_class = permission_class.op2_class + permission_classes = [op1_class, op2_class] + return self.check_permission_classes_requires_auth(permission_classes, operator) + else: + if issubclass(permission_class, (IsAuthenticated, RBACPermission)): + return True + if issubclass(permission_class, (AllowAny, )): + return False + if hasattr(permission_class, '__name__'): + if 'Authenticated' in permission_class.__name__: + return True + if permission_class.__name__.startswith('UserConfirmation'): + return True + return False + + def check_view_func_requires_auth(self): + if hasattr(self.view_func, '__wrapped__'): + if hasattr(self.view_func, '__name__'): + if 'login_required' in str(self.view_func): + return True + return False + + +class CustomURLPattern: + def __init__(self, raw, prefix='/'): + self.raw = raw + self.prefix = prefix + self.full_path = f'{self.prefix}{self.raw.pattern}' + self.view = CustomView(view_func=self.raw.callback) + + def __str__(self): + s = f'{self.full_path} -> {self.view.view_path}' + return s + + def __repr__(self): + return self.__str__() + + +class ViewSchemaGenerator: + + def __init__(self): + self.resolver = get_resolver() + self.url_patterns = self.get_url_patterns() + + def get_url_patterns(self): + return self._extract_url_patterns(self.resolver.url_patterns) + + def _extract_url_patterns(self, url_patterns, prefix='/'): + url_pattern_objects = [] + for pattern in url_patterns: + if isinstance(pattern, URLResolver): + resolver = pattern + _prefix = f'{prefix}{resolver.pattern}' + patterns = self._extract_url_patterns(resolver.url_patterns, prefix=_prefix) + url_pattern_objects.extend(patterns) + continue + elif isinstance(pattern, URLPattern): + p = CustomURLPattern(raw=pattern, prefix=prefix) + url_pattern_objects.append(p) + else: + log(f'Unknown pattern type: {type(pattern)}') + return url_pattern_objects + + def generate(self): + self.write_url_patterns() + self.write_webui_schema() + + def write_webui_schema(self): + data = { + 'GET': {}, + 'POST': {} + } + post_schema = {} + for pattern in self.url_patterns: + if pattern.view.requires_auth: + continue + url = pattern.full_path + item = { + 'allowIf': 'prelogin', + 'query': { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": False + + }, + 'body': { + 'type': 'object', + 'properties': pattern.view.write_fields_schema, + 'required': pattern.view.required_fields, + 'additionalProperties': False + } + } + post_schema[url] = item + data['POST'] = post_schema + self.write_to_file(data, 'webui_schema.json') + + def write_url_patterns(self): + data = [] + for pattern in self.url_patterns: + if pattern.view.requires_auth: + continue + if pattern.view.view_type == 'function': + continue + item = { + 'url': pattern.full_path, + 'view_path': pattern.view.view_path, + 'view_requires_auth': pattern.view.requires_auth, + 'view_type': pattern.view.view_type, + } + view_write_fields_schema = pattern.view.write_fields_schema + if view_write_fields_schema: + item['view_write_fields_schema'] = view_write_fields_schema + data.append(item) + self.write_to_file(data, 'all_url_patterns.json') + + def write_to_file(self, data, filename): + file_path = os.path.join(OUTPUT_FILE_DIR, filename) + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=4, ensure_ascii=False) + +if __name__ == '__main__': + ViewSchemaGenerator().generate() \ No newline at end of file diff --git a/utils/view_schema/output/all_url_patterns.json b/utils/view_schema/output/all_url_patterns.json new file mode 100644 index 000000000..af69ed04d --- /dev/null +++ b/utils/view_schema/output/all_url_patterns.json @@ -0,0 +1,799 @@ +[ + { + "url": "/api/v1/users/^service-account-registrations/$", + "view_path": "users.api.service.ServiceAccountRegistrationViewSet", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + } + }, + { + "url": "/api/v1/users/^service-account-registrations\\.(?P[a-z0-9]+)/?$", + "view_path": "users.api.service.ServiceAccountRegistrationViewSet", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + } + }, + { + "url": "/api/v1/users/^service-account-registrations/(?P[^/.]+)/$", + "view_path": "users.api.service.ServiceAccountRegistrationViewSet", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + } + }, + { + "url": "/api/v1/users/^service-account-registrations/(?P[^/.]+)\\.(?P[a-z0-9]+)/?$", + "view_path": "users.api.service.ServiceAccountRegistrationViewSet", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + } + }, + { + "url": "/api/v1/terminal/terminal-registrations/", + "view_path": "terminal.api.component.terminal.TerminalRegistrationApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "name": { + "type": "string", + "maxLength": 1024 + }, + "type": { + "type": "string", + "enum": [ + "koko", + "guacamole", + "omnidb", + "xrdp", + "lion", + "core", + "celery", + "magnus", + "razor", + "tinker", + "video_worker", + "chen", + "kael", + "panda", + "nec", + "facelive" + ] + }, + "comment": { + "type": "string" + } + } + }, + { + "url": "/api/v1/terminal/registration/", + "view_path": "terminal.api.component.terminal.TerminalRegistrationApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "name": { + "type": "string", + "maxLength": 1024 + }, + "type": { + "type": "string", + "enum": [ + "koko", + "guacamole", + "omnidb", + "xrdp", + "lion", + "core", + "celery", + "magnus", + "razor", + "tinker", + "video_worker", + "chen", + "kael", + "panda", + "nec", + "facelive" + ] + }, + "comment": { + "type": "string" + } + } + }, + { + "url": "/api/v1/settings/logo/", + "view_path": "settings.api.settings.SettingsLogoApi", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/v1/settings/public/open/", + "view_path": "settings.api.public.OpenPublicSettingApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "XPACK_ENABLED": { + "type": "boolean" + }, + "INTERFACE": { + "type": "object", + "properties": {} + }, + "LANGUAGES": { + "type": "array", + "properties": {} + } + } + }, + { + "url": "/api/v1/settings/i18n//", + "view_path": "settings.api.i18n.ComponentI18nApi", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/v1/settings/client/versions/", + "view_path": "settings.api.settings.ClientVersionView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/v1/authentication/face/context/", + "view_path": "authentication.api.face.FaceContextApi", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/v1/authentication/auth/", + "view_path": "authentication.api.token.TokenCreateApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "public_key": { + "type": "string" + } + } + }, + { + "url": "/api/v1/authentication/tokens/", + "view_path": "authentication.api.token.TokenCreateApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "public_key": { + "type": "string" + } + } + }, + { + "url": "/api/v1/authentication/mfa/verify/", + "view_path": "authentication.api.mfa.MFAChallengeVerifyApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "type": { + "type": "string" + }, + "code": { + "type": "string" + } + } + }, + { + "url": "/api/v1/authentication/mfa/challenge/", + "view_path": "authentication.api.mfa.MFAChallengeVerifyApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "type": { + "type": "string" + }, + "code": { + "type": "string" + } + } + }, + { + "url": "/api/v1/authentication/mfa/select/", + "view_path": "authentication.api.mfa.MFASendCodeApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + { + "url": "/api/v1/authentication/mfa/send-code/", + "view_path": "authentication.api.mfa.MFASendCodeApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + { + "url": "/api/v1/authentication/password/reset-code/", + "view_path": "authentication.api.password.UserResetPasswordSendCodeApi", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "form_type": { + "type": "string", + "enum": [ + "sms", + "email" + ] + }, + "email": { + "type": "string" + }, + "sms": { + "type": "string" + } + } + }, + { + "url": "/api/v1/authentication/login-confirm-ticket/status/", + "view_path": "authentication.api.login_confirm.TicketStatusApi", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/v1/authentication/user-session/", + "view_path": "authentication.api.session.UserSessionApi", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/v1/prometheus/metrics/", + "view_path": "jumpserver.api.health.PrometheusMetricsApi", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/health/", + "view_path": "jumpserver.api.health.HealthCheckView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/api/v1/health/", + "view_path": "jumpserver.api.health.HealthCheckView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/login/", + "view_path": "authentication.views.login.UserLoginView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "auto_login": { + "type": "boolean" + }, + "captcha": { + "type": "string" + }, + "code": { + "type": "string" + }, + "mfa_type": { + "type": "string" + }, + "challenge": { + "type": "string" + } + } + }, + { + "url": "/core/auth/login/mfa/", + "view_path": "authentication.views.mfa.UserLoginMFAView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "code": { + "type": "string" + }, + "mfa_type": { + "type": "string" + } + } + }, + { + "url": "/core/auth/login/wait-confirm/", + "view_path": "authentication.views.login.UserLoginWaitConfirmView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/login/mfa/face/capture/", + "view_path": "authentication.views.mfa.UserLoginMFAFaceView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "code": { + "type": "string" + } + } + }, + { + "url": "/core/auth/login/guard/", + "view_path": "authentication.views.login.UserLoginGuardView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/logout/", + "view_path": "authentication.views.login.UserLogoutView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/password/forget/previewing/", + "view_path": "users.views.profile.reset.UserForgotPasswordPreviewingView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "captcha": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + { + "url": "/core/auth/password/forgot/", + "view_path": "users.views.profile.reset.UserForgotPasswordView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "email": { + "type": "string" + }, + "country_code": { + "type": "string" + }, + "sms": { + "type": "string", + "description": "The phone number must contain an area code, for example, +86" + }, + "code": { + "type": "string" + }, + "form_type": { + "type": "string" + } + } + }, + { + "url": "/core/auth/password/reset/", + "view_path": "users.views.profile.reset.UserResetPasswordView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "new_password": { + "type": "string" + }, + "confirm_password": { + "type": "string" + } + } + }, + { + "url": "/core/auth/password/verify/", + "view_path": "users.views.profile.password.UserVerifyPasswordView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "password": { + "type": "string" + } + } + }, + { + "url": "/core/auth/wecom/bind/start/", + "view_path": "authentication.views.wecom.WeComEnableStartView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "password": { + "type": "string" + } + } + }, + { + "url": "/core/auth/wecom/qr/login/", + "view_path": "authentication.views.wecom.WeComQRLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/wecom/qr/login/callback/", + "view_path": "authentication.views.wecom.WeComQRLoginCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/wecom/oauth/login/", + "view_path": "authentication.views.wecom.WeComOAuthLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/wecom/oauth/login/callback/", + "view_path": "authentication.views.wecom.WeComOAuthLoginCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/dingtalk/bind/start/", + "view_path": "authentication.views.dingtalk.DingTalkEnableStartView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "password": { + "type": "string" + } + } + }, + { + "url": "/core/auth/dingtalk/qr/login/", + "view_path": "authentication.views.dingtalk.DingTalkQRLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/dingtalk/qr/login/callback/", + "view_path": "authentication.views.dingtalk.DingTalkQRLoginCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/dingtalk/oauth/login/", + "view_path": "authentication.views.dingtalk.DingTalkOAuthLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/dingtalk/oauth/login/callback/", + "view_path": "authentication.views.dingtalk.DingTalkOAuthLoginCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/feishu/bind/start/", + "view_path": "authentication.views.feishu.FeiShuEnableStartView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "password": { + "type": "string" + } + } + }, + { + "url": "/core/auth/feishu/qr/login/", + "view_path": "authentication.views.feishu.FeiShuQRLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/feishu/qr/login/callback/", + "view_path": "authentication.views.feishu.FeiShuQRLoginCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/lark/bind/start/", + "view_path": "authentication.views.lark.LarkEnableStartView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "password": { + "type": "string" + } + } + }, + { + "url": "/core/auth/lark/qr/login/", + "view_path": "authentication.views.lark.LarkQRLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/lark/qr/login/callback/", + "view_path": "authentication.views.lark.LarkQRLoginCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/slack/bind/start/", + "view_path": "authentication.views.slack.SlackEnableStartView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "password": { + "type": "string" + } + } + }, + { + "url": "/core/auth/slack/qr/login/", + "view_path": "authentication.views.slack.SlackQRLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/slack/qr/login/callback/", + "view_path": "authentication.views.slack.SlackQRLoginCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/profile/otp/enable/start/", + "view_path": "users.views.profile.otp.UserOtpEnableStartView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/profile/otp/enable/install-app/", + "view_path": "users.views.profile.otp.UserOtpEnableInstallAppView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/profile/otp/enable/bind/", + "view_path": "users.views.profile.otp.UserOtpEnableBindView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "otp_code": { + "type": "string" + } + } + }, + { + "url": "/core/auth/profile/face/enable/", + "view_path": "users.views.profile.face.UserFaceEnableView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "code": { + "type": "string" + } + } + }, + { + "url": "/core/auth/profile/face/disable/", + "view_path": "users.views.profile.face.UserFaceDisableView", + "view_requires_auth": false, + "view_type": "class", + "view_write_fields_schema": { + "code": { + "type": "string" + } + } + }, + { + "url": "/core/auth/cas/login/", + "view_path": "authentication.backends.cas.views.CASLoginView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/cas/logout/", + "view_path": "django_cas_ng.views.LogoutView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/cas/callback/", + "view_path": "django_cas_ng.views.CallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/openid/login/", + "view_path": "authentication.backends.oidc.views.OIDCAuthRequestView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/openid/callback/", + "view_path": "authentication.backends.oidc.views.OIDCAuthCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/openid/logout/", + "view_path": "authentication.backends.oidc.views.OIDCEndSessionView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/saml2/login/", + "view_path": "authentication.backends.saml2.views.Saml2AuthRequestView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/saml2/logout/", + "view_path": "authentication.backends.saml2.views.Saml2EndSessionView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/saml2/callback/", + "view_path": "authentication.backends.saml2.views.Saml2AuthCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/saml2/metadata/", + "view_path": "authentication.backends.saml2.views.Saml2AuthMetadataView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/oauth2/login/", + "view_path": "authentication.backends.oauth2.views.OAuth2AuthRequestView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/oauth2/callback/", + "view_path": "authentication.backends.oauth2.views.OAuth2AuthCallbackView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/oauth2/logout/", + "view_path": "authentication.backends.oauth2.views.OAuth2EndSessionView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/oauth2-provider/token/", + "view_path": "oauth2_provider.views.base.TokenView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/oauth2-provider/revoke/", + "view_path": "oauth2_provider.views.base.RevokeTokenView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/auth/oauth2-provider/.well-known/oauth-authorization-server", + "view_path": "authentication.backends.oauth2_provider.views.OAuthAuthorizationServerView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/reports/export-pdf/", + "view_path": "reports.views.ExportPdfView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/reports/send-mail/", + "view_path": "reports.views.SendMailView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/tickets/direct-approve//", + "view_path": "tickets.views.approve.TicketDirectApproveView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/common/flash-message/", + "view_path": "common.views.msg.FlashMessageMsgView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/download/", + "view_path": "jumpserver.views.other.ResourceDownload", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/redirect/confirm/", + "view_path": "jumpserver.views.other.RedirectConfirm", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/i18n//", + "view_path": "jumpserver.views.other.I18NView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/media/^(?P.*)$", + "view_path": "private_storage.views.PrivateStorageView", + "view_requires_auth": false, + "view_type": "class" + }, + { + "url": "/core/jsi18n/", + "view_path": "django.views.i18n.JavaScriptCatalog", + "view_requires_auth": false, + "view_type": "class" + } +] \ No newline at end of file diff --git a/utils/view_schema/output/webui_schema.json b/utils/view_schema/output/webui_schema.json new file mode 100644 index 000000000..f327607ad --- /dev/null +++ b/utils/view_schema/output/webui_schema.json @@ -0,0 +1,1702 @@ +{ + "GET": {}, + "POST": { + "/api/v1/users/^service-account-registrations/$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "/api/v1/users/^service-account-registrations\\.(?P[a-z0-9]+)/?$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "/api/v1/users/^service-account-registrations/(?P[^/.]+)/$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "/api/v1/users/^service-account-registrations/(?P[^/.]+)\\.(?P[a-z0-9]+)/?$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + }, + "comment": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "/api/v1/terminal/terminal-registrations/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 1024 + }, + "type": { + "type": "string", + "enum": [ + "koko", + "guacamole", + "omnidb", + "xrdp", + "lion", + "core", + "celery", + "magnus", + "razor", + "tinker", + "video_worker", + "chen", + "kael", + "panda", + "nec", + "facelive" + ] + }, + "comment": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "/api/v1/terminal/registration/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 1024 + }, + "type": { + "type": "string", + "enum": [ + "koko", + "guacamole", + "omnidb", + "xrdp", + "lion", + "core", + "celery", + "magnus", + "razor", + "tinker", + "video_worker", + "chen", + "kael", + "panda", + "nec", + "facelive" + ] + }, + "comment": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "/api/v1/settings/logo/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/settings/public/open/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "XPACK_ENABLED": { + "type": "boolean" + }, + "INTERFACE": { + "type": "object", + "properties": {} + }, + "LANGUAGES": { + "type": "array", + "properties": {} + } + }, + "required": [ + "XPACK_ENABLED", + "INTERFACE", + "LANGUAGES" + ], + "additionalProperties": false + } + }, + "/api/v1/settings/i18n//": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/settings/client/versions/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/authentication/face/context/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/authentication/auth/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "public_key": { + "type": "string" + } + }, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/authentication/tokens/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "public_key": { + "type": "string" + } + }, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/authentication/mfa/verify/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "code": { + "type": "string" + } + }, + "required": [ + "code" + ], + "additionalProperties": false + } + }, + "/api/v1/authentication/mfa/challenge/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "code": { + "type": "string" + } + }, + "required": [ + "code" + ], + "additionalProperties": false + } + }, + "/api/v1/authentication/mfa/select/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + } + }, + "/api/v1/authentication/mfa/send-code/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + } + }, + "/api/v1/authentication/password/reset-code/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "form_type": { + "type": "string", + "enum": [ + "sms", + "email" + ] + }, + "email": { + "type": "string" + }, + "sms": { + "type": "string" + } + }, + "required": [ + "email", + "sms" + ], + "additionalProperties": false + } + }, + "/api/v1/authentication/login-confirm-ticket/status/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/authentication/user-session/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/notifications/debug-msgs/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/prometheus/metrics/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/health/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/api/v1/health/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/captcha/image/(?P\\w+)/$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/captcha/image/(?P\\w+)@2/$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/captcha/audio/(?P\\w+).wav$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/captcha/refresh/$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "auto_login": { + "type": "boolean" + }, + "captcha": { + "type": "string" + }, + "code": { + "type": "string" + }, + "mfa_type": { + "type": "string" + }, + "challenge": { + "type": "string" + } + }, + "required": [ + "username", + "password", + "captcha", + "mfa_type" + ], + "additionalProperties": false + } + }, + "/core/auth/login/mfa/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "mfa_type": { + "type": "string" + } + }, + "required": [ + "mfa_type" + ], + "additionalProperties": false + } + }, + "/core/auth/login/wait-confirm/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/login/mfa/face/capture/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/login/guard/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/logout/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/password/forget/previewing/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "captcha": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "required": [ + "captcha", + "username" + ], + "additionalProperties": false + } + }, + "/core/auth/password/forgot/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "country_code": { + "type": "string" + }, + "sms": { + "type": "string", + "description": "The phone number must contain an area code, for example, +86" + }, + "code": { + "type": "string" + }, + "form_type": { + "type": "string" + } + }, + "required": [ + "form_type" + ], + "additionalProperties": false + } + }, + "/core/auth/password/reset/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "new_password": { + "type": "string" + }, + "confirm_password": { + "type": "string" + } + }, + "required": [ + "new_password", + "confirm_password" + ], + "additionalProperties": false + } + }, + "/core/auth/password/verify/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ], + "additionalProperties": false + } + }, + "/core/auth/wecom/bind/start/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ], + "additionalProperties": false + } + }, + "/core/auth/wecom/qr/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/wecom/qr/login/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/wecom/oauth/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/wecom/oauth/login/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/dingtalk/bind/start/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ], + "additionalProperties": false + } + }, + "/core/auth/dingtalk/qr/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/dingtalk/qr/login/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/dingtalk/oauth/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/dingtalk/oauth/login/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/feishu/bind/start/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ], + "additionalProperties": false + } + }, + "/core/auth/feishu/qr/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/feishu/qr/login/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/lark/bind/start/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ], + "additionalProperties": false + } + }, + "/core/auth/lark/qr/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/lark/qr/login/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/slack/bind/start/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ], + "additionalProperties": false + } + }, + "/core/auth/slack/qr/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/slack/qr/login/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/profile/otp/enable/start/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/profile/otp/enable/install-app/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/profile/otp/enable/bind/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "otp_code": { + "type": "string" + } + }, + "required": [ + "otp_code" + ], + "additionalProperties": false + } + }, + "/core/auth/profile/face/enable/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/profile/face/disable/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/cas/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/cas/logout/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/cas/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/openid/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/openid/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/openid/logout/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/saml2/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/saml2/logout/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/saml2/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/saml2/metadata/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/oauth2/login/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/oauth2/callback/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/oauth2/logout/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/oauth2-provider/token/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/oauth2-provider/revoke/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/auth/oauth2-provider/.well-known/oauth-authorization-server": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/reports/export-pdf/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/reports/send-mail/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/tickets/direct-approve//": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/common/flash-message/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/flower/(?P.*)": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/download/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/redirect/confirm/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/i18n//": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/^static/(?P.*)$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/media/^(?P.*)$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/^luna/(?P.*)$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/^ui/(?P.*)$": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + }, + "/core/jsi18n/": { + "allowIf": "prelogin", + "query": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + }, + "body": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false + } + } + } +} \ No newline at end of file