sgl-project--sglang
94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
309 行
10 KiB
Python
309 行
10 KiB
Python
"""Unit tests for srt/utils/auth.py — no server, no model loading."""
|
|
|
|
import unittest
|
|
|
|
from sglang.srt.utils.auth import (
|
|
AuthDecision,
|
|
AuthLevel,
|
|
auth_level,
|
|
decide_request_auth,
|
|
)
|
|
from sglang.test.ci.ci_register import register_cpu_ci
|
|
from sglang.test.test_utils import CustomTestCase
|
|
|
|
register_cpu_ci(1.0, "base-a-test-cpu")
|
|
|
|
|
|
class TestAuthDecision(CustomTestCase):
|
|
def test_not_allowed_with_custom_status(self):
|
|
decision = AuthDecision(allowed=False, error_status_code=403)
|
|
self.assertFalse(decision.allowed)
|
|
self.assertEqual(decision.error_status_code, 403)
|
|
|
|
def test_frozen(self):
|
|
decision = AuthDecision(allowed=True)
|
|
with self.assertRaises(AttributeError):
|
|
decision.allowed = False
|
|
|
|
|
|
class TestAuthLevel(CustomTestCase):
|
|
def test_is_string_enum(self):
|
|
self.assertIsInstance(AuthLevel.NORMAL, str)
|
|
# str mixin allows direct comparison with string values
|
|
self.assertEqual(AuthLevel.NORMAL, "normal")
|
|
|
|
|
|
class TestAuthLevelDecorator(CustomTestCase):
|
|
def test_decorator_sets_auth_level(self):
|
|
@auth_level(AuthLevel.ADMIN_FORCE)
|
|
def my_endpoint():
|
|
pass
|
|
|
|
self.assertEqual(my_endpoint._auth_level, AuthLevel.ADMIN_FORCE)
|
|
|
|
|
|
class TestDecideRequestAuth(CustomTestCase):
|
|
"""Tests for the pure decide_request_auth function."""
|
|
|
|
# ==================== Always-Allowed Paths ====================
|
|
|
|
def test_options_method_always_allowed(self):
|
|
decision = decide_request_auth(
|
|
method="OPTIONS",
|
|
path="/v1/chat/completions",
|
|
authorization_header=None,
|
|
api_key="secret",
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.ADMIN_FORCE,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_health_path_always_allowed(self):
|
|
decision = decide_request_auth(
|
|
method="GET",
|
|
path="/health",
|
|
authorization_header=None,
|
|
api_key="secret",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_health_subpath_always_allowed(self):
|
|
decision = decide_request_auth(
|
|
method="GET",
|
|
path="/health_generate",
|
|
authorization_header=None,
|
|
api_key="secret",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_metrics_path_always_allowed(self):
|
|
decision = decide_request_auth(
|
|
method="GET",
|
|
path="/metrics",
|
|
authorization_header=None,
|
|
api_key="secret",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
# ==================== NORMAL Auth Level ====================
|
|
|
|
def test_normal_no_keys_configured(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header=None,
|
|
api_key=None,
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_normal_with_api_key_correct(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header="Bearer my-api-key",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_normal_with_api_key_wrong(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header="Bearer wrong-key",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
|
|
def test_normal_with_api_key_missing_header(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header=None,
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
|
|
def test_normal_only_admin_key_configured(self):
|
|
"""When only admin_api_key is configured, normal endpoints allow all."""
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header=None,
|
|
api_key=None,
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
# ==================== ADMIN_FORCE Auth Level ====================
|
|
|
|
def test_admin_force_no_admin_key_configured(self):
|
|
"""ADMIN_FORCE without admin_api_key configured returns 403."""
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/endpoint",
|
|
authorization_header="Bearer my-api-key",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.ADMIN_FORCE,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
self.assertEqual(decision.error_status_code, 403)
|
|
|
|
def test_admin_force_correct_admin_key(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/endpoint",
|
|
authorization_header="Bearer admin-secret",
|
|
api_key="my-api-key",
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.ADMIN_FORCE,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_admin_force_wrong_admin_key(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/endpoint",
|
|
authorization_header="Bearer wrong-key",
|
|
api_key="my-api-key",
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.ADMIN_FORCE,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
self.assertEqual(decision.error_status_code, 401)
|
|
|
|
def test_admin_force_api_key_not_accepted(self):
|
|
"""ADMIN_FORCE rejects api_key, only accepts admin_api_key."""
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/endpoint",
|
|
authorization_header="Bearer my-api-key",
|
|
api_key="my-api-key",
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.ADMIN_FORCE,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
|
|
# ==================== ADMIN_OPTIONAL Auth Level ====================
|
|
|
|
def test_admin_optional_no_keys_configured(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/optional",
|
|
authorization_header=None,
|
|
api_key=None,
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.ADMIN_OPTIONAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_admin_optional_only_api_key_correct(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/optional",
|
|
authorization_header="Bearer my-api-key",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.ADMIN_OPTIONAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_admin_optional_only_api_key_wrong(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/optional",
|
|
authorization_header="Bearer wrong-key",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.ADMIN_OPTIONAL,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
|
|
def test_admin_optional_only_admin_key_correct(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/optional",
|
|
authorization_header="Bearer admin-secret",
|
|
api_key=None,
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.ADMIN_OPTIONAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
def test_admin_optional_both_keys_requires_admin(self):
|
|
"""When both keys configured, ADMIN_OPTIONAL requires admin_api_key."""
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/optional",
|
|
authorization_header="Bearer my-api-key",
|
|
api_key="my-api-key",
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.ADMIN_OPTIONAL,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
|
|
def test_admin_optional_both_keys_admin_accepted(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/admin/optional",
|
|
authorization_header="Bearer admin-secret",
|
|
api_key="my-api-key",
|
|
admin_api_key="admin-secret",
|
|
auth_level=AuthLevel.ADMIN_OPTIONAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
# ==================== Bearer Token Edge Cases ====================
|
|
|
|
def test_malformed_authorization_header(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header="NotBearer my-api-key",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
|
|
def test_empty_authorization_header(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header="",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertFalse(decision.allowed)
|
|
|
|
def test_bearer_case_insensitive(self):
|
|
decision = decide_request_auth(
|
|
method="POST",
|
|
path="/v1/chat/completions",
|
|
authorization_header="BEARER my-api-key",
|
|
api_key="my-api-key",
|
|
admin_api_key=None,
|
|
auth_level=AuthLevel.NORMAL,
|
|
)
|
|
self.assertTrue(decision.allowed)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|