feat: add generate_webui_schema GET

This commit is contained in:
Bai
2026-01-22 15:13:52 +08:00
parent 0e9a057da0
commit cb42a651f8
9 changed files with 1952 additions and 3 deletions

View File

@@ -8,6 +8,7 @@ import os
from typing import Callable
from urllib.parse import urlparse, urlsplit, urlunsplit, urlencode
from rest_framework import serializers
from django.conf import settings
from django.contrib.auth import BACKEND_SESSION_KEY
from django.contrib.auth import login as auth_login, logout as auth_logout
@@ -131,6 +132,12 @@ class UserLoginContextMixin:
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(never_cache, name='dispatch')
class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
class QuerySerializer(serializers.Serializer):
next = serializers.CharField(required=False)
admin = serializers.IntegerField(required=False)
query_serializer_class = QuerySerializer
redirect_field_name = 'next'
template_name = 'authentication/login.html'

View File

@@ -3,6 +3,7 @@
from __future__ import unicode_literals
from rest_framework import serializers
from django.shortcuts import redirect, reverse
from django.views.generic.edit import FormView
@@ -21,6 +22,11 @@ class UserLoginMFAView(mixins.AuthMixin, FormView):
form_class = forms.UserCheckOtpCodeForm
redirect_field_name = 'next'
class QuerySerializer(serializers.Serializer):
next = serializers.CharField(required=False)
query_serializer_class = QuerySerializer
def get(self, *args, **kwargs):
try:
user = self.get_user_from_session()

View File

@@ -3,6 +3,7 @@ import os
from django.conf import settings
from django.utils._os import safe_join
from rest_framework import serializers
from rest_framework.serializers import Serializer
from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import AllowAny
@@ -17,6 +18,12 @@ class ComponentI18nApi(RetrieveAPIView):
lang_data = {}
serializer_class = Serializer
class QuerySerializer(Serializer):
lang = serializers.CharField(required=False)
flat = serializers.CharField(required=False)
query_serializer_class = QuerySerializer
def get_component_translations(self, name):
if not settings.DEBUG and name in self.lang_data:
return self.lang_data[name]

View File

@@ -5,7 +5,7 @@ from django.core.cache import cache
from django.http import HttpResponse
from django.views.static import serve
from rest_framework import generics
from rest_framework import status
from rest_framework import status, serializers as drf_serializers
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
@@ -197,7 +197,11 @@ class SettingsApi(generics.RetrieveUpdateAPIView):
class SettingsLogoApi(APIView):
class QuerySerializer(drf_serializers.Serializer):
size = drf_serializers.CharField(required=False)
permission_classes = (AllowAny,)
query_serializer_class = QuerySerializer
def get(self, request, *args, **kwargs):
size = request.GET.get('size', 'small')

View File

@@ -4,6 +4,7 @@ from __future__ import unicode_literals
import time
from rest_framework import serializers
from django.conf import settings
from django.core.cache import cache
from django.shortcuts import redirect, reverse
@@ -80,6 +81,11 @@ class UserForgotPasswordView(FormView):
template_name = 'users/forgot_password.html'
form_class = forms.UserForgotPasswordForm
class QuerySerializer(serializers.Serializer):
token = serializers.CharField(required=False)
query_serializer_class = QuerySerializer
def get(self, request, *args, **kwargs):
token = self.request.GET.get('token')
userinfo = cache.get(token)
@@ -173,6 +179,11 @@ class UserResetPasswordView(FormView):
template_name = 'users/reset_password.html'
form_class = forms.UserTokenResetPasswordForm
class QuerySerializer(serializers.Serializer):
token = serializers.CharField(required=False)
query_serializer_class = QuerySerializer
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
errors = kwargs.get('errors')

View File

@@ -0,0 +1,928 @@
{
"GET": {
"/api/v1/settings/logo/": {
"query": {
"type": "object",
"properties": {
"size": {
"type": "string",
"description": "None"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/settings/public/open/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/settings/i18n/<str:name>/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/settings/client/versions/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/face/context/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/auth/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/tokens/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/verify/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/challenge/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/select/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/send-code/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/password/reset-code/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/login-confirm-ticket/status/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/user-session/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/notifications/debug-msgs/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/prometheus/metrics/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/health/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/health/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/image/(?P<key>\\w+)/$": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/image/(?P<key>\\w+)@2/$": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/audio/(?P<key>\\w+).wav$": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/refresh/$": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/mfa/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/wait-confirm/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/mfa/face/capture/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/guard/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/logout/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/forget/previewing/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/forgot/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/reset/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/verify/": {
"query": {
"type": "object",
"properties": {"": "已验证没有"},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/oauth/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/oauth/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/oauth/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/oauth/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/feishu/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/feishu/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/feishu/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/lark/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/lark/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/lark/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/slack/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/slack/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/slack/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/install-app/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/bind/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/face/enable/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/face/disable/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/cas/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/cas/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/cas/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/openid/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/openid/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/openid/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/saml2/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/saml2/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/saml2/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/saml2/metadata/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/token/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/revoke/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/.well-known/oauth-authorization-server": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/reports/export-pdf/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/reports/send-mail/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/tickets/direct-approve/<str:token>/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/common/flash-message/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/flower/(?P<path>.*)": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/download/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/redirect/confirm/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/i18n/<str:lang>/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/^static/(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/media/^(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/^luna/(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/^ui/(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/jsi18n/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
}
},
"POST": {
"/api/v1/users/^service-account-registrations/$": {
"allowIf": "prelogin"
},
"/api/v1/users/^service-account-registrations\\.(?P<format>[a-z0-9]+)/?$": {
"allowIf": "prelogin"
},
"/api/v1/users/^service-account-registrations/(?P<pk>[^/.]+)/$": {
"allowIf": "prelogin"
},
"/api/v1/users/^service-account-registrations/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$": {
"allowIf": "prelogin"
},
"/api/v1/terminal/terminal-registrations/": {
"allowIf": "prelogin"
},
"/api/v1/terminal/registration/": {
"allowIf": "prelogin"
},
"/api/v1/settings/logo/": {
"allowIf": "prelogin"
},
"/api/v1/settings/public/open/": {
"allowIf": "prelogin"
},
"/api/v1/settings/i18n/<str:name>/": {
"allowIf": "prelogin"
},
"/api/v1/settings/client/versions/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/face/context/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/auth/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/tokens/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/verify/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/challenge/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/select/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/send-code/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/password/reset-code/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/login-confirm-ticket/status/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/user-session/": {
"allowIf": "prelogin"
},
"/api/v1/prometheus/metrics/": {
"allowIf": "prelogin"
},
"/api/health/": {
"allowIf": "prelogin"
},
"/api/v1/health/": {
"allowIf": "prelogin"
},
"/core/auth/login/": {
"allowIf": "prelogin"
},
"/core/auth/login/mfa/": {
"allowIf": "prelogin"
},
"/core/auth/login/wait-confirm/": {
"allowIf": "prelogin"
},
"/core/auth/login/mfa/face/capture/": {
"allowIf": "prelogin"
},
"/core/auth/login/guard/": {
"allowIf": "prelogin"
},
"/core/auth/logout/": {
"allowIf": "prelogin"
},
"/core/auth/password/forget/previewing/": {
"allowIf": "prelogin"
},
"/core/auth/password/forgot/": {
"allowIf": "prelogin"
},
"/core/auth/password/reset/": {
"allowIf": "prelogin"
},
"/core/auth/password/verify/": {
"allowIf": "prelogin"
},
"/core/auth/wecom/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/wecom/qr/login/": {
"allowIf": "prelogin"
},
"/core/auth/wecom/qr/login/callback/": {
"allowIf": "prelogin"
},
"/core/auth/wecom/oauth/login/": {
"allowIf": "prelogin"
},
"/core/auth/wecom/oauth/login/callback/": {
"allowIf": "prelogin"
},
"/core/auth/dingtalk/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/dingtalk/qr/login/": {
"allowIf": "prelogin"
},
"/core/auth/dingtalk/qr/login/callback/": {
"allowIf": "prelogin"
},
"/core/auth/dingtalk/oauth/login/": {
"allowIf": "prelogin"
},
"/core/auth/dingtalk/oauth/login/callback/": {
"allowIf": "prelogin"
},
"/core/auth/feishu/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/feishu/qr/login/": {
"allowIf": "prelogin"
},
"/core/auth/feishu/qr/login/callback/": {
"allowIf": "prelogin"
},
"/core/auth/lark/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/lark/qr/login/": {
"allowIf": "prelogin"
},
"/core/auth/lark/qr/login/callback/": {
"allowIf": "prelogin"
},
"/core/auth/slack/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/slack/qr/login/": {
"allowIf": "prelogin"
},
"/core/auth/slack/qr/login/callback/": {
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/start/": {
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/install-app/": {
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/bind/": {
"allowIf": "prelogin"
},
"/core/auth/profile/face/enable/": {
"allowIf": "prelogin"
},
"/core/auth/profile/face/disable/": {
"allowIf": "prelogin"
},
"/core/auth/cas/login/": {
"allowIf": "prelogin"
},
"/core/auth/cas/logout/": {
"allowIf": "prelogin"
},
"/core/auth/cas/callback/": {
"allowIf": "prelogin"
},
"/core/auth/openid/logout/": {
"allowIf": "prelogin"
},
"/core/auth/saml2/login/": {
"allowIf": "prelogin"
},
"/core/auth/saml2/logout/": {
"allowIf": "prelogin"
},
"/core/auth/saml2/callback/": {
"allowIf": "prelogin"
},
"/core/auth/saml2/metadata/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2/login/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2/logout/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/token/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/revoke/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/.well-known/oauth-authorization-server": {
"allowIf": "prelogin"
},
"/core/reports/export-pdf/": {
"allowIf": "prelogin"
},
"/core/reports/send-mail/": {
"allowIf": "prelogin"
},
"/core/tickets/direct-approve/<str:token>/": {
"allowIf": "prelogin"
},
"/core/common/flash-message/": {
"allowIf": "prelogin"
},
"/core/download/": {
"allowIf": "prelogin"
},
"/core/redirect/confirm/": {
"allowIf": "prelogin"
},
"/core/i18n/<str:lang>/": {
"allowIf": "prelogin"
},
"/media/^(?P<path>.*)$": {
"allowIf": "prelogin"
},
"/core/jsi18n/": {
"allowIf": "prelogin"
}
}
}

View File

@@ -0,0 +1,743 @@
{
"GET": {
"/api/v1/settings/logo/": {
"query": {
"type": "object",
"properties": {
"size": {
"type": "string"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/settings/public/open/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/settings/i18n/<str:name>/": {
"query": {
"type": "object",
"properties": {
"lang": {
"type": "string"
},
"flat": {
"type": "string"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/settings/client/versions/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/face/context/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/login-confirm-ticket/status/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/authentication/user-session/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/notifications/debug-msgs/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/prometheus/metrics/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/health/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/api/v1/health/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/image/(?P<key>\\w+)/$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/image/(?P<key>\\w+)@2/$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/audio/(?P<key>\\w+).wav$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/captcha/refresh/$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/": {
"query": {
"type": "object",
"properties": {
"next": {
"type": "string"
},
"admin": {
"type": "integer"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/mfa/": {
"query": {
"type": "object",
"properties": {
"next": {
"type": "string"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/wait-confirm/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/mfa/face/capture/": {
"query": {
"type": "object",
"properties": {
"next": {
"type": "string"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/login/guard/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/forget/previewing/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/forgot/": {
"query": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/reset/": {
"query": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/password/verify/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/oauth/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/wecom/oauth/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/oauth/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/dingtalk/oauth/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/feishu/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/feishu/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/feishu/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/lark/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/lark/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/lark/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/slack/bind/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/slack/qr/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/slack/qr/login/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/start/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/install-app/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/bind/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/face/enable/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/profile/face/disable/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/cas/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/cas/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/cas/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/openid/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/openid/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/openid/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/saml2/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/saml2/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/saml2/metadata/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2/login/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2/callback/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2/logout/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/.well-known/oauth-authorization-server": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/reports/export-pdf/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/tickets/direct-approve/<str:token>/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/common/flash-message/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/flower/(?P<path>.*)": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/download/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/redirect/confirm/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/i18n/<str:lang>/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/^static/(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/media/^(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/^luna/(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/^ui/(?P<path>.*)$": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
},
"/core/jsi18n/": {
"query": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"allowIf": "prelogin"
}
},
"POST": {
"/api/v1/terminal/terminal-registrations/": {
"allowIf": "prelogin"
},
"/api/v1/terminal/registration/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/face/context/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/auth/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/tokens/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/verify/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/challenge/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/select/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/mfa/send-code/": {
"allowIf": "prelogin"
},
"/api/v1/authentication/password/reset-code/": {
"allowIf": "prelogin"
},
"/core/auth/login/": {
"allowIf": "prelogin"
},
"/core/auth/login/mfa/": {
"allowIf": "prelogin"
},
"/core/auth/login/mfa/face/capture/": {
"allowIf": "prelogin"
},
"/core/auth/login/guard/": {
"allowIf": "prelogin"
},
"/core/auth/password/forget/previewing/": {
"allowIf": "prelogin"
},
"/core/auth/password/forgot/": {
"allowIf": "prelogin"
},
"/core/auth/password/reset/": {
"allowIf": "prelogin"
},
"/core/auth/password/verify/": {
"allowIf": "prelogin"
},
"/core/auth/wecom/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/dingtalk/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/feishu/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/lark/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/slack/bind/start/": {
"allowIf": "prelogin"
},
"/core/auth/profile/otp/enable/bind/": {
"allowIf": "prelogin"
},
"/core/auth/profile/face/enable/": {
"allowIf": "prelogin"
},
"/core/auth/profile/face/disable/": {
"allowIf": "prelogin"
},
"/core/auth/cas/login/": {
"allowIf": "prelogin"
},
"/core/auth/cas/callback/": {
"allowIf": "prelogin"
},
"/core/auth/openid/logout/": {
"allowIf": "prelogin"
},
"/core/auth/saml2/logout/": {
"allowIf": "prelogin"
},
"/core/auth/saml2/callback/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2/logout/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/token/": {
"allowIf": "prelogin"
},
"/core/auth/oauth2-provider/revoke/": {
"allowIf": "prelogin"
},
"/core/reports/export-pdf/": {
"allowIf": "prelogin"
},
"/core/reports/send-mail/": {
"allowIf": "prelogin"
},
"/core/tickets/direct-approve/<str:token>/": {
"allowIf": "prelogin"
}
}
}

View File

@@ -0,0 +1,245 @@
import os
import json
import sys
import django
# 获取项目根目录jumpserver 目录)
BASE_DIR = os.path.dirname(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()
from django.urls import get_resolver
from django.urls.resolvers import URLPattern, URLResolver
from django.core.handlers.asgi import ASGIRequest
from django.contrib.auth.mixins import LoginRequiredMixin
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.permissions import OperandHolder, AND, OR, NOT
from rbac.permissions import RBACPermission
dirname = os.path.dirname
BASE_DIR = dirname(dirname(os.path.abspath(__file__)))
OUTPUT_FILE_DIR = os.path.join(BASE_DIR, 'output')
os.makedirs(OUTPUT_FILE_DIR, exist_ok=True)
scope = {
'type': 'http',
'method': 'GET',
'path': '/',
'query_string': b'',
'headers': [],
}
async def receive():
return {'type': 'http.request', 'body': b''}
fake_request = ASGIRequest(scope, receive)
setattr(fake_request, 'query_params', {})
def extract_url_patterns(patterns, path_prefix='/'):
routes = []
for p in patterns:
if isinstance(p, URLResolver):
_path_prefix = f'{path_prefix}{p.pattern}'
_routes = extract_url_patterns(p.url_patterns, path_prefix=_path_prefix)
routes.extend(_routes)
elif isinstance(p, URLPattern):
setattr(p, 'path_full', f'{path_prefix}{p.pattern}')
routes.append(p)
else:
print(f'Skip: unknown pattern type: {type(p)}')
return routes
def discover_routes():
resolver = get_resolver()
routes = extract_url_patterns(resolver.url_patterns)
return routes
def resolve_view_class(view_func):
view_class = getattr(view_func, 'view_class', None)
if not view_class:
view_class = getattr(view_func, 'cls', None)
return view_class
def check_permission_class_requires_auth(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 check_permission_classes_requires_auth(permission_classes, operator)
else:
if issubclass(permission_class, (IsAuthenticated, RBACPermission)):
return True
if issubclass(permission_class, (AllowAny, )):
return False
permission_class_name: str = getattr(permission_class, '__name__', None)
if not permission_class_name:
return False
if 'Authenticated' in permission_class_name:
return True
if permission_class_name.startswith('UserConfirmation'):
return True
return False
def check_permission_classes_requires_auth(permission_classes, operator):
if operator == AND:
for pc in permission_classes:
if check_permission_class_requires_auth(pc):
return True
return False
if operator == OR:
for pc in permission_classes:
if not check_permission_class_requires_auth(pc):
return False
return True
if operator == NOT:
raise ValueError('NOT operator is not supported in permission_classes')
return False
def is_required_auth(view_func):
view_class = resolve_view_class(view_func)
if view_class:
if issubclass(view_class, LoginRequiredMixin):
return True
permission_classes = getattr(view_class, 'permission_classes', [])
if not permission_classes:
return False
return check_permission_classes_requires_auth(permission_classes, operator=AND)
# func base view
return False
def get_view_http_methods(view_func):
view_class = resolve_view_class(view_func)
has_get_method = True
if not view_class:
# function based view
http_method_names = ['GET']
return http_method_names
view = view_class(request=fake_request)
http_method_names = getattr(view, '_allowed_methods', lambda: [])()
if http_method_names:
return http_method_names
http_method_names = getattr(view, 'http_method_names', [])
if http_method_names:
return http_method_names
http_method_names = []
has_get_method = hasattr(view_class, 'get') or hasattr(view_class, 'list') or hasattr(view_class, 'retrieve')
if has_get_method:
http_method_names.append('GET')
has_post_method = hasattr(view_class, 'post') or hasattr(view_class, 'create')
if has_post_method:
http_method_names.append('POST')
return http_method_names
from rest_framework import serializers
from django import forms
field_type_mapper = {
'object': [serializers.Serializer, serializers.ModelSerializer],
'string': [serializers.CharField, forms.CharField],
'integer': [serializers.IntegerField, forms.IntegerField],
'boolean': [serializers.BooleanField, forms.BooleanField],
}
def get_field_type(field):
for json_type, class_list in field_type_mapper.items():
if isinstance(field, tuple(class_list)):
return json_type
return 'string'
def get_view_query_properties(view_func):
view_class = resolve_view_class(view_func)
if not view_class:
# function based view
return {}
query_serializer_class = getattr(view_class, 'query_serializer_class', None)
if not query_serializer_class:
return {}
serializer = query_serializer_class()
fields = serializer.get_fields()
properties = {}
for name, field in fields.items():
field_schema = {}
field_schema['type'] = get_field_type(field)
help_text = getattr(field, 'help_text', '')
if help_text:
field_schema['description'] = str(help_text)
properties[name] = field_schema
return properties
def get_view_query_schema(view_func):
query_properties = get_view_query_properties(view_func)
query_schema = {
'type': 'object',
'properties': query_properties,
'additionalProperties': False,
}
return query_schema
def generate_view_schema(view_func):
http_method_names = get_view_http_methods(view_func)
schema = {}
if 'GET' in http_method_names:
schema['GET'] = {}
query_schema = get_view_query_schema(view_func)
if query_schema:
schema['GET']['query'] = query_schema
if 'POST' in http_method_names:
schema['POST'] = {}
return schema
def write_schema(schema):
filename = 'x_webui_schema.json'
file_path = os.path.join(OUTPUT_FILE_DIR, filename)
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(schema, f, indent=4, ensure_ascii=False)
def generate_schema():
schema = {
'GET': {},
'POST': {}
}
routes = discover_routes()
for route in routes:
url = route.path_full
view_func = route.callback
if is_required_auth(view_func):
continue
view_schema = generate_view_schema(view_func)
if 'GET' in view_schema:
schema['GET'][url] = {
**view_schema['GET'],
'allowIf': 'prelogin'
}
if 'POST' in view_schema:
schema['POST'][url] = {
**view_schema['POST'],
'allowIf': 'prelogin'
}
write_schema(schema)
if __name__ == '__main__':
generate_schema()

View File

@@ -27,8 +27,6 @@ class BaseExtractor:
def extract(self) -> Endpoint:
url = self.view.route.path
if url.startswith('/api/v1/users/^users'):
pass
endpoint = Endpoint(
path=self.view.route.path,
requires_auth=self.view_requires_auth()