Replace plaintext token storage with SHA-256 hashes so leaked database contents cannot be used to authenticate. Plaintext is generated, shown once at creation time, and never persisted. - Add `hash_token()` helper and `MCPTokenManager.create_token()` that returns `(instance, plaintext)`. - Replace `token` field with indexed `token_hash`; look up bearers by hashing the incoming value. - Update dashboard, management command, and admin to surface plaintext only at creation. Disable admin "add" since it cannot reveal plaintext. - Migration drops the old `token` column and adds `token_hash`; pre-existing tokens are invalidated and must be reissued.
59 lines
2.1 KiB
Python
59 lines
2.1 KiB
Python
"""Form tests for the MCP token dashboard."""
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
|
|
from mcp_server.forms import MCPTokenCreateForm, MCPTokenEditForm
|
|
from mcp_server.models import MCPToken
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class CreateFormTest(TestCase):
|
|
def test_required_fields(self):
|
|
form = MCPTokenCreateForm(data={})
|
|
self.assertFalse(form.is_valid())
|
|
self.assertIn("name", form.errors)
|
|
|
|
def test_name_only_is_valid(self):
|
|
form = MCPTokenCreateForm(data={"name": "Test"})
|
|
self.assertTrue(form.is_valid(), form.errors)
|
|
|
|
def test_tool_choices_match_registered_tools(self):
|
|
form = MCPTokenCreateForm()
|
|
choices = {value for value, _ in form.fields["allowed_tools"].choices}
|
|
# These five must always be present per the FastMCP server.
|
|
for expected in {"search", "get_chunk", "list_libraries", "list_collections", "list_items"}:
|
|
self.assertIn(expected, choices)
|
|
|
|
|
|
class EditFormTest(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(username="alice", password="pw")
|
|
self.token, _ = MCPToken.objects.create_token(
|
|
user=self.user, name="t", allowed_tools=["search"]
|
|
)
|
|
|
|
def test_initial_allowed_tools_populated(self):
|
|
form = MCPTokenEditForm(instance=self.token)
|
|
self.assertEqual(form.fields["allowed_tools"].initial, ["search"])
|
|
|
|
def test_save_updates_metadata(self):
|
|
form = MCPTokenEditForm(
|
|
data={
|
|
"name": "Renamed",
|
|
"is_active": False,
|
|
"expires_at": "",
|
|
"allowed_tools": ["search", "get_chunk"],
|
|
},
|
|
instance=self.token,
|
|
)
|
|
self.assertTrue(form.is_valid(), form.errors)
|
|
instance = form.save(commit=False)
|
|
instance.allowed_tools = form.cleaned_data["allowed_tools"]
|
|
instance.save()
|
|
self.token.refresh_from_db()
|
|
self.assertEqual(self.token.name, "Renamed")
|
|
self.assertFalse(self.token.is_active)
|
|
self.assertEqual(self.token.allowed_tools, ["search", "get_chunk"])
|