mirror of
https://github.com/langgenius/dify.git
synced 2026-03-14 05:29:45 +08:00
feat: add global license check middleware to block API access on expiry
Add before_request middleware that validates enterprise license status for all /console/api endpoints when ENTERPRISE_ENABLED is true. Behavior: - Checks license status before each console API request - Returns 403 with clear error message when license is expired/inactive/lost - Exempts auth endpoints (login, oauth, forgot-password, etc.) - Exempts /console/api/features so frontend can fetch license status - Gracefully handles errors to avoid service disruption This ensures all business APIs are blocked when license expires, addressing the issue where APIs remained callable after expiry.
This commit is contained in:
parent
2b739b9544
commit
0ed39d81e9
@ -1,6 +1,7 @@
|
||||
import logging
|
||||
import time
|
||||
|
||||
from flask import jsonify, request
|
||||
from opentelemetry.trace import get_current_span
|
||||
from opentelemetry.trace.span import INVALID_SPAN_ID, INVALID_TRACE_ID
|
||||
|
||||
@ -8,6 +9,7 @@ from configs import dify_config
|
||||
from contexts.wrapper import RecyclableContextVar
|
||||
from core.logging.context import init_request_context
|
||||
from dify_app import DifyApp
|
||||
from services.feature_service import FeatureService, LicenseStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -31,6 +33,47 @@ def create_flask_app_with_configs() -> DifyApp:
|
||||
init_request_context()
|
||||
RecyclableContextVar.increment_thread_recycles()
|
||||
|
||||
# Enterprise license validation for console API endpoints
|
||||
if dify_config.ENTERPRISE_ENABLED and request.path.startswith("/console/api"):
|
||||
# Skip license check for auth-related endpoints and system endpoints
|
||||
exempt_paths = [
|
||||
"/console/api/login",
|
||||
"/console/api/logout",
|
||||
"/console/api/oauth",
|
||||
"/console/api/setup",
|
||||
"/console/api/init",
|
||||
"/console/api/forgot-password",
|
||||
"/console/api/email-code-login",
|
||||
"/console/api/activation",
|
||||
"/console/api/data-source-oauth",
|
||||
"/console/api/features", # Allow fetching features to show license status
|
||||
]
|
||||
|
||||
# Check if current path is exempt
|
||||
is_exempt = any(request.path.startswith(path) for path in exempt_paths)
|
||||
|
||||
if not is_exempt:
|
||||
try:
|
||||
# Check license status
|
||||
system_features = FeatureService.get_system_features(is_authenticated=True)
|
||||
if system_features.license.status in [
|
||||
LicenseStatus.INACTIVE,
|
||||
LicenseStatus.EXPIRED,
|
||||
LicenseStatus.LOST,
|
||||
]:
|
||||
return jsonify({
|
||||
"code": "license_expired",
|
||||
"message": (
|
||||
f"Enterprise license is {system_features.license.status.value}. "
|
||||
"Please contact your administrator."
|
||||
),
|
||||
"status": system_features.license.status.value,
|
||||
}), 403
|
||||
except Exception:
|
||||
# If license check fails, log but don't block the request
|
||||
# This prevents service disruption if enterprise API is temporarily unavailable
|
||||
logger.exception("Failed to check enterprise license status")
|
||||
|
||||
# add after request hook for injecting trace headers from OpenTelemetry span context
|
||||
# Only adds headers when OTEL is enabled and has valid context
|
||||
@dify_app.after_request
|
||||
|
||||
Loading…
Reference in New Issue
Block a user