#!/usr/bin/env python3
"""
Test API Key Access Control Fix
Validates that:
1. Site-level keys get proper error message for system tools (not validation failure)
2. Global keys work for all tools
3. Per-project keys work for their own unified tools
"""
def test_system_tool_detection():
"""Test that system tools are detected correctly."""
SYSTEM_TOOLS = [
"list_projects",
"get_project_info",
"check_all_projects_health",
"get_project_health",
"get_system_metrics",
"get_system_uptime",
"get_rate_limit_stats",
"export_health_metrics",
"manage_api_keys_list",
"manage_api_keys_get_info",
]
# Test with and without MCP prefix
test_cases = [
("list_projects", True),
("mcp__coolify-projects__list_projects", True),
("get_project_info", True),
("wordpress_list_posts", False),
("woocommerce_list_orders", False),
("unknown", False),
]
for tool_name, expected in test_cases:
is_system_tool = any(tool_name.endswith(st) for st in SYSTEM_TOOLS)
assert (
is_system_tool == expected
), f"Failed for {tool_name}: expected {expected}, got {is_system_tool}"
print(f"✓ {tool_name}: is_system_tool={is_system_tool}")
def test_unified_tool_detection():
"""Test that unified tools are detected correctly."""
test_cases = [
("wordpress_list_posts", True),
("woocommerce_list_orders", True),
("mcp__coolify-projects__wordpress_list_posts", True),
("mcp__coolify-projects__woocommerce_list_orders", True),
("list_projects", False),
("get_project_info", False),
("unknown", True), # Falls to fallback
]
for tool_name, expected in test_cases:
if tool_name == "unknown":
is_unified_tool = True # Fallback
else:
is_unified_tool = (
tool_name.startswith("wordpress_")
or tool_name.startswith("woocommerce_")
or tool_name.startswith("mcp__coolify-projects__wordpress_")
or tool_name.startswith("mcp__coolify-projects__woocommerce_")
)
assert (
is_unified_tool == expected
), f"Failed for {tool_name}: expected {expected}, got {is_unified_tool}"
print(f"✓ {tool_name}: is_unified_tool={is_unified_tool}")
def test_skip_project_check_logic():
"""Test that skip_project_check is set correctly."""
SYSTEM_TOOLS = [
"list_projects",
"get_project_info",
"check_all_projects_health",
"get_project_health",
"get_system_metrics",
"get_system_uptime",
"get_rate_limit_stats",
"export_health_metrics",
"manage_api_keys_list",
"manage_api_keys_get_info",
]
test_cases = [
# (tool_name, expected_skip)
("list_projects", True), # System tool
("wordpress_list_posts", True), # Unified tool
("woocommerce_list_orders", True), # Unified tool
("mcp__coolify-projects__list_projects", True), # System tool with prefix
("mcp__coolify-projects__wordpress_list_posts", True), # Unified tool with prefix
("unknown", True), # Fallback to unified
]
for tool_name, expected_skip in test_cases:
# Determine is_system_tool
is_system_tool = any(tool_name.endswith(st) for st in SYSTEM_TOOLS)
# Determine is_unified_tool
if tool_name == "unknown":
is_unified_tool = True
else:
is_unified_tool = (
tool_name.startswith("wordpress_")
or tool_name.startswith("woocommerce_")
or tool_name.startswith("mcp__coolify-projects__wordpress_")
or tool_name.startswith("mcp__coolify-projects__woocommerce_")
)
# Calculate skip_project_check
skip_project_check = is_unified_tool or is_system_tool
assert skip_project_check == expected_skip, (
f"Failed for {tool_name}: expected skip={expected_skip}, got {skip_project_check} "
f"(unified={is_unified_tool}, system={is_system_tool})"
)
print(
f"✓ {tool_name}: skip_project_check={skip_project_check} (unified={is_unified_tool}, system={is_system_tool})"
)
if __name__ == "__main__":
print("\n=== Testing System Tool Detection ===")
test_system_tool_detection()
print("\n=== Testing Unified Tool Detection ===")
test_unified_tool_detection()
print("\n=== Testing skip_project_check Logic ===")
test_skip_project_check_logic()
print("\n✅ All tests passed!")
print("\nExpected behavior after fix:")
print("1. Site-level key + list_projects → Validation passes, then gets proper error:")
print(" 'System tools require global API key (project_id=\"*\")'")
print(
"2. Site-level key + wordpress_list_posts(site=other) → Validation passes, handler blocks with:"
)
print(" 'Access denied. This API key is restricted to project X'")
print("3. Global key + any tool → Works")