feat(F.7+F.17): v3.11.0 — Coolify plugin (67 tools) + tool access overhaul
Catch-up sync spanning v3.7.0 → v3.11.0 of the internal repo. Platform - Total tools: 565 → 633 (+68) across 10 plugins (Coolify added) - Tests: 481 → 828 passing New plugin: Coolify (67 tools, Track F.17) - Applications (17): CRUD, lifecycle, logs, env vars - Deployments (5): list/get/cancel/deploy, app history - Servers (8): CRUD, resources, domains, validation - Projects (8), Databases (16, 6 DB types + backups), Services (13) Tool access system (Track F.7 → F.7d) - Scope → category mapping with per-tool `category` + `sensitivity` - Schema v7: `site_tool_toggles(site_id)` + `sites.tool_scope` column - Schema v8: per-site API keys (`api_keys.site_id`) - Plugin-specific access-level presets (WP / WC / Gitea / OpenPanel / Coolify 5-tier) - Credential-requirement notice tailored per plugin and tier - Admin Tools count card on service page - Dropped redundant `write` tier on WP / WP Advanced / WooCommerce (admin-scope tool count = 0 → identical to admin tier) Dashboard - Unified site manage page (Connection / Tool Access / Connect) - /dashboard/keys unified (was /api-keys and /connect) - CSRF interceptor via meta-tag; removed conflicting cookie reader - Tailwind: pre-built CSS (scripts/build-css.sh) replaces CDN Docs - README / DOCKER_README / CLAUDE updated to 633 tools / 10 plugins - CHANGELOG entries for v3.7.0 → v3.11.0 - FastMCP compatibility note updated to 3.x (post-v3.5 upgrade) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -181,24 +181,41 @@ class TestScopesToCategories:
|
||||
|
||||
|
||||
class TestScopeFilter:
|
||||
"""F.7c: Coolify uses legacy category filter (plugin_type='coolify'),
|
||||
other plugins use universal required_scope filter."""
|
||||
|
||||
def test_read_scope_drops_lifecycle_crud_system(self, access_mgr):
|
||||
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["read"])}
|
||||
# Coolify tools filtered by category
|
||||
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
|
||||
out = {
|
||||
t.name
|
||||
for t in access_mgr.apply_scope_filter(coolify_tools, ["read"], plugin_type="coolify")
|
||||
}
|
||||
assert "coolify_list_applications" in out
|
||||
assert "wordpress_list_posts" in out # legacy default category
|
||||
assert "coolify_start_application" not in out
|
||||
assert "coolify_create_application_public" not in out
|
||||
assert "coolify_delete_server" not in out
|
||||
assert "coolify_get_application_logs" not in out
|
||||
|
||||
def test_read_sensitive_includes_logs_and_backups(self, access_mgr):
|
||||
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["read:sensitive"])}
|
||||
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
|
||||
out = {
|
||||
t.name
|
||||
for t in access_mgr.apply_scope_filter(
|
||||
coolify_tools, ["read:sensitive"], plugin_type="coolify"
|
||||
)
|
||||
}
|
||||
assert "coolify_get_application_logs" in out
|
||||
assert "coolify_get_database_backups" in out
|
||||
assert "coolify_start_application" not in out
|
||||
assert "coolify_create_application_public" not in out
|
||||
|
||||
def test_deploy_scope_includes_lifecycle_only(self, access_mgr):
|
||||
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["deploy"])}
|
||||
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
|
||||
out = {
|
||||
t.name
|
||||
for t in access_mgr.apply_scope_filter(coolify_tools, ["deploy"], plugin_type="coolify")
|
||||
}
|
||||
assert "coolify_start_application" in out
|
||||
assert "coolify_stop_application" in out
|
||||
assert "coolify_list_applications" in out
|
||||
@@ -206,7 +223,11 @@ class TestScopeFilter:
|
||||
assert "coolify_delete_server" not in out
|
||||
|
||||
def test_write_scope_excludes_system(self, access_mgr):
|
||||
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["write"])}
|
||||
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
|
||||
out = {
|
||||
t.name
|
||||
for t in access_mgr.apply_scope_filter(coolify_tools, ["write"], plugin_type="coolify")
|
||||
}
|
||||
assert "coolify_create_application_public" in out
|
||||
assert "coolify_start_application" in out
|
||||
assert "coolify_create_application_env" in out
|
||||
@@ -214,9 +235,22 @@ class TestScopeFilter:
|
||||
assert "coolify_get_application_logs" not in out
|
||||
|
||||
def test_admin_keeps_everything(self, access_mgr):
|
||||
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["admin"])}
|
||||
out = {
|
||||
t.name
|
||||
for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["admin"], plugin_type="coolify")
|
||||
}
|
||||
assert out == {t.name for t in _SAMPLE_TOOLS}
|
||||
|
||||
def test_universal_read_filters_by_required_scope(self, access_mgr):
|
||||
"""Non-Coolify plugins use universal required_scope filter."""
|
||||
wp_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "wordpress"]
|
||||
out = {
|
||||
t.name
|
||||
for t in access_mgr.apply_scope_filter(wp_tools, ["read"], plugin_type="wordpress")
|
||||
}
|
||||
# All wordpress sample tools have required_scope="read"
|
||||
assert "wordpress_list_posts" in out
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Per-site toggles
|
||||
|
||||
Reference in New Issue
Block a user