Skip to content

Commit d0fc4d9

Browse files
committed
Add new Policy tab
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
1 parent 1c6af89 commit d0fc4d9

File tree

14 files changed

+1722
-76
lines changed

14 files changed

+1722
-76
lines changed

.secrets.baseline

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "^.secrets.baseline|package-lock.json|Cargo.lock|scripts/sign_image.sh|scripts/zap|sonar-project.properties|uv.lock|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2026-04-13T09:59:07Z",
6+
"generated_at": "2026-04-13T13:45:24Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -5086,31 +5086,31 @@
50865086
"hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3",
50875087
"is_secret": false,
50885088
"is_verified": false,
5089-
"line_number": 4240,
5089+
"line_number": 4264,
50905090
"type": "Secret Keyword",
50915091
"verified_result": null
50925092
},
50935093
{
50945094
"hashed_secret": "559b05f1b2863e725b76e216ac3dadecbf92e244",
50955095
"is_secret": false,
50965096
"is_verified": false,
5097-
"line_number": 4841,
5097+
"line_number": 4865,
50985098
"type": "Secret Keyword",
50995099
"verified_result": null
51005100
},
51015101
{
51025102
"hashed_secret": "a8af4759392d4f7496d613174f33afe2074a4b8d",
51035103
"is_secret": false,
51045104
"is_verified": false,
5105-
"line_number": 4843,
5105+
"line_number": 4867,
51065106
"type": "Secret Keyword",
51075107
"verified_result": null
51085108
},
51095109
{
51105110
"hashed_secret": "85b60d811d16ff56b3654587d4487f713bfa33b7",
51115111
"is_secret": false,
51125112
"is_verified": false,
5113-
"line_number": 15169,
5113+
"line_number": 15193,
51145114
"type": "Secret Keyword",
51155115
"verified_result": null
51165116
}
@@ -6288,7 +6288,7 @@
62886288
"hashed_secret": "b4e44716dbbf57be3dae2f819d96795a85d06652",
62896289
"is_secret": false,
62906290
"is_verified": false,
6291-
"line_number": 12498,
6291+
"line_number": 12510,
62926292
"type": "Secret Keyword",
62936293
"verified_result": null
62946294
}

mcpgateway/admin.py

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19630,10 +19630,7 @@ async def get_policy_partial(
1963019630
pdp = getattr(request.app.state, "pdp", None)
1963119631

1963219632
if pdp is None:
19633-
return HTMLResponse(
19634-
"<div class='p-8 text-center text-gray-500'>Policy engine not initialised. "
19635-
"Set up the PDP singleton in main.py startup.</div>"
19636-
)
19633+
return HTMLResponse("<div class='p-8 text-center text-gray-500'>Policy engine not initialised. Set up the PDP singleton in main.py startup.</div>")
1963719634

1963819635
# First-Party
1963919636
from plugins.unified_pdp.pdp_models import EngineType
@@ -19642,20 +19639,18 @@ async def get_policy_partial(
1964219639
cache_stats = pdp.cache_stats()
1964319640

1964419641
# Get native rules for the table
19645-
native = pdp._engines.get(EngineType.NATIVE) # pylint: disable=protected-access # pylint: disable=protected-access
19646-
rules = native._rules if native else [] # pylint: disable=protected-access # pylint: disable=protected-access
19642+
native = pdp.get_engine(EngineType.NATIVE)
19643+
rules = native.rules if native else []
1964719644

1964819645
context = {
1964919646
"request": request,
1965019647
"health": health,
19651-
"engine_count": len(pdp._engines),
19648+
"engine_count": pdp.engine_count,
1965219649
"rule_count": len(rules),
1965319650
"rules": rules,
1965419651
"cache_stats": cache_stats,
1965519652
}
19656-
return request.app.state.templates.TemplateResponse(
19657-
request, "policy_partial.html", context
19658-
)
19653+
return request.app.state.templates.TemplateResponse(request, "policy_partial.html", context)
1965919654

1966019655

1966119656
@admin_router.get("/policy/rules")
@@ -19798,22 +19793,24 @@ async def test_policy_access(
1979819793

1979919794
decision = await pdp.check_access(subject, body.action, resource, context)
1980019795

19801-
return JSONResponse({
19802-
"decision": decision.decision.value,
19803-
"reason": decision.reason,
19804-
"matching_policies": decision.matching_policies,
19805-
"duration_ms": decision.duration_ms,
19806-
"cached": decision.cached,
19807-
"engine_decisions": [
19808-
{
19809-
"engine": ed.engine.value,
19810-
"decision": ed.decision.value,
19811-
"reason": ed.reason,
19812-
"matching_policies": ed.matching_policies,
19813-
}
19814-
for ed in decision.engine_decisions
19815-
],
19816-
})
19796+
return JSONResponse(
19797+
{
19798+
"decision": decision.decision.value,
19799+
"reason": decision.reason,
19800+
"matching_policies": decision.matching_policies,
19801+
"duration_ms": decision.duration_ms,
19802+
"cached": decision.cached,
19803+
"engine_decisions": [
19804+
{
19805+
"engine": ed.engine.value,
19806+
"decision": ed.decision.value,
19807+
"reason": ed.reason,
19808+
"matching_policies": ed.matching_policies,
19809+
}
19810+
for ed in decision.engine_decisions
19811+
],
19812+
}
19813+
)
1981719814

1981819815

1981919816
@admin_router.get("/policy/health")
@@ -19837,18 +19834,20 @@ async def policy_health(
1983719834
raise HTTPException(status_code=503, detail="Policy engine not initialised")
1983819835

1983919836
health = await pdp.health()
19840-
return JSONResponse({
19841-
"healthy": health.healthy,
19842-
"engines": [
19843-
{
19844-
"engine": e.engine.value,
19845-
"status": e.status.value,
19846-
"latency_ms": e.latency_ms,
19847-
"detail": e.detail,
19848-
}
19849-
for e in health.engines
19850-
],
19851-
})
19837+
return JSONResponse(
19838+
{
19839+
"healthy": health.healthy,
19840+
"engines": [
19841+
{
19842+
"engine": e.engine.value,
19843+
"status": e.status.value,
19844+
"latency_ms": e.latency_ms,
19845+
"detail": e.detail,
19846+
}
19847+
for e in health.engines
19848+
],
19849+
}
19850+
)
1985219851

1985319852

1985419853
@admin_router.get("/policy/cache/stats")

mcpgateway/admin_ui/events.js

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ import {
2121
import { llmModelComboboxSelect } from "./llmModels.js";
2222
import { closeModal } from "./modals.js";
2323
import { initializeRealTimeMonitoring } from "./monitoring.js";
24+
import {
25+
closeAddRuleModal,
26+
deleteRule,
27+
openAddRuleModal,
28+
runPolicyTest,
29+
submitAddRule,
30+
} from "./policy.js";
2431
import { ensureAddStoreListeners } from "./servers.js";
2532
import { initializeTagFiltering, updateAvailableTags } from "./tags.js";
2633
import {
@@ -661,6 +668,62 @@ import {
661668
}
662669
});
663670

671+
// ===================================================================
672+
// Policy Page setup
673+
// ===================================================================
674+
675+
// Policy Engine button delegation
676+
document.addEventListener("click", function (e) {
677+
const btn = e.target.closest("[onclick]");
678+
if (!btn) return;
679+
const fn = btn.getAttribute("onclick");
680+
if (fn && fn.includes("openAddRuleModal")) {
681+
e.preventDefault();
682+
openAddRuleModal();
683+
}
684+
if (fn && fn.includes("closeAddRuleModal")) {
685+
e.preventDefault();
686+
closeAddRuleModal();
687+
}
688+
if (fn && fn.includes("submitAddRule")) {
689+
e.preventDefault();
690+
submitAddRule();
691+
}
692+
if (fn && fn.includes("runPolicyTest")) {
693+
e.preventDefault();
694+
runPolicyTest();
695+
}
696+
if (fn && fn.includes("deleteRule")) {
697+
e.preventDefault();
698+
const match = fn.match(/deleteRule\('(.+?)'\)/);
699+
if (match) deleteRule(match[1]);
700+
}
701+
});
702+
703+
// Policy Engine event delegation
704+
document.addEventListener("click", function (e) {
705+
const el = e.target.closest("[data-action]");
706+
if (!el) return;
707+
const action = el.getAttribute("data-action");
708+
if (action === "open-add-rule") openAddRuleModal();
709+
if (action === "close-add-rule") closeAddRuleModal();
710+
if (action === "submit-add-rule") submitAddRule();
711+
if (action === "run-policy-test") runPolicyTest();
712+
if (action === "delete-rule") deleteRule(el.getAttribute("data-rule-id"));
713+
});
714+
715+
// Policy Engine event delegation
716+
document.addEventListener("click", function (e) {
717+
const el = e.target.closest("[data-action]");
718+
if (!el) return;
719+
const action = el.getAttribute("data-action");
720+
if (action === "open-add-rule") openAddRuleModal();
721+
if (action === "close-add-rule") closeAddRuleModal();
722+
if (action === "submit-add-rule") submitAddRule();
723+
if (action === "run-policy-test") runPolicyTest();
724+
if (action === "delete-rule") deleteRule(el.getAttribute("data-rule-id"));
725+
});
726+
664727
// ===================================================================
665728
// GLOBAL ERROR HANDLERS
666729
// ===================================================================
@@ -841,7 +904,9 @@ import {
841904
// ===================================================================
842905
// Alpine Components
843906
// ===================================================================
844-
document.addEventListener('alpine:init', () => {
845-
Alpine.data('overflowMenu', (wrapperId = null) => overflowMenu(wrapperId));
907+
document.addEventListener("alpine:init", () => {
908+
window.Alpine.data("overflowMenu", (wrapperId = null) =>
909+
overflowMenu(wrapperId)
910+
);
846911
});
847912
})(window.Admin);

0 commit comments

Comments
 (0)