feat(mcp): gate abonnement + écrans de consentement OAuth (#114)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from markupsafe import escape
|
||||||
|
|
||||||
|
from src.subscriptions.db import has_active_subscription
|
||||||
|
from src.utils import TOUS_ABONNES
|
||||||
|
|
||||||
|
|
||||||
|
def subscription_ok(user_id: int) -> bool:
|
||||||
|
return bool(TOUS_ABONNES or has_active_subscription(user_id))
|
||||||
|
|
||||||
|
|
||||||
|
def render_subscription_required() -> str:
|
||||||
|
return (
|
||||||
|
"<!doctype html><html lang=fr><head><meta charset=utf-8>"
|
||||||
|
"<title>Abonnement requis — colibre</title></head><body>"
|
||||||
|
"<h1>Abonnement requis</h1>"
|
||||||
|
"<p>Le connecteur MCP colibre nécessite un abonnement actif.</p>"
|
||||||
|
'<p><a href="/compte/abonnement">Gérer mon abonnement</a></p>'
|
||||||
|
"</body></html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def render_consent(client_name: str, redirect_uri: str, scope: str) -> str:
|
||||||
|
host = urlparse(redirect_uri).netloc or redirect_uri
|
||||||
|
return (
|
||||||
|
"<!doctype html><html lang=fr><head><meta charset=utf-8>"
|
||||||
|
"<title>Autoriser l'accès — colibre</title></head><body>"
|
||||||
|
f"<h1>Autoriser {escape(client_name)} ?</h1>"
|
||||||
|
f"<p><strong>{escape(client_name)}</strong> demande à lire les données "
|
||||||
|
"colibre en votre nom via le connecteur MCP.</p>"
|
||||||
|
f"<p>Vous serez redirigé vers <strong>{escape(host)}</strong>.</p>"
|
||||||
|
'<form method="post">'
|
||||||
|
'<button name="confirm" value="yes" type="submit">Autoriser</button> '
|
||||||
|
'<button name="confirm" value="no" type="submit">Refuser</button>'
|
||||||
|
"</form></body></html>"
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
from src.mcp.oauth import consent
|
||||||
|
|
||||||
|
|
||||||
|
def test_subscription_ok_tous_abonnes(monkeypatch):
|
||||||
|
monkeypatch.setattr("src.mcp.oauth.consent.TOUS_ABONNES", True)
|
||||||
|
assert consent.subscription_ok(999) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_subscription_ok_delegates(monkeypatch):
|
||||||
|
monkeypatch.setattr("src.mcp.oauth.consent.TOUS_ABONNES", False)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"src.mcp.oauth.consent.has_active_subscription", lambda uid: uid == 7
|
||||||
|
)
|
||||||
|
assert consent.subscription_ok(7) is True
|
||||||
|
assert consent.subscription_ok(8) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_consent_shows_redirect_host():
|
||||||
|
html = consent.render_consent(
|
||||||
|
"Claude", "https://claude.ai/api/mcp/auth_callback", "mcp"
|
||||||
|
)
|
||||||
|
assert "claude.ai" in html
|
||||||
|
assert "Claude" in html
|
||||||
|
assert 'name="confirm"' in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_subscription_required_links_abonnement():
|
||||||
|
html = consent.render_subscription_required()
|
||||||
|
assert "/compte/abonnement" in html
|
||||||
Reference in New Issue
Block a user