feat: MCP tools for posts, categories and tags, the draft_post prompt
- journal.Plugin.MCP: list_posts, show_post, list_categories, list_tags, create_post, update_post, delete_post and the draft_post prompt, guard backend, listed with golem15.journal.access_posts; update fields are optional so only sent keys reach the route - optionalBackendPrincipal honours the MCP dispatch marker (no bearer short-circuit; anonymous reads unchanged) - require summercms v0.4.4 - TestJournalMCPSmoke, TestOptionalBackendPrincipalDispatch
This commit is contained in:
41
controllers/api/optional_principal_test.go
Normal file
41
controllers/api/optional_principal_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/bouncer"
|
||||
)
|
||||
|
||||
// TestOptionalBackendPrincipalDispatch pins the D-17 fix: a request the MCP
|
||||
// endpoint dispatches in-process carries no Authorization header but the
|
||||
// backend dispatch marker, and the list and show handlers must see its
|
||||
// principal; a request with neither stays anonymous.
|
||||
func TestOptionalBackendPrincipalDispatch(t *testing.T) {
|
||||
app := backpack.New(nil)
|
||||
admin := &bouncer.Principal{ID: 42, Backend: true}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/_journal/api/v1/posts", nil)
|
||||
req = req.WithContext(bouncer.WithDispatch(req.Context(), &bouncer.Dispatch{Guards: []string{"backend"}, Principal: admin}))
|
||||
out := optionalBackendPrincipal(app, req)
|
||||
if pr, ok := bouncer.User(out.Context()); !ok || pr != admin {
|
||||
t.Fatalf("dispatched request principal = %+v (%v), want the marker's admin", pr, ok)
|
||||
}
|
||||
|
||||
anon := httptest.NewRequest(http.MethodGet, "/_journal/api/v1/posts", nil)
|
||||
if out := optionalBackendPrincipal(app, anon); out != anon {
|
||||
t.Fatal("a request without a bearer or marker was changed")
|
||||
}
|
||||
if _, ok := bouncer.User(anon.Context()); ok {
|
||||
t.Fatal("an anonymous request gained a principal")
|
||||
}
|
||||
|
||||
// A marker for another guard is not the backend's.
|
||||
other := httptest.NewRequest(http.MethodGet, "/_journal/api/v1/posts", nil)
|
||||
other = other.WithContext(bouncer.WithDispatch(other.Context(), &bouncer.Dispatch{Guards: []string{"api_token"}, Principal: &bouncer.Principal{ID: 7}}))
|
||||
if out := optionalBackendPrincipal(app, other); out != other {
|
||||
t.Fatal("a marker of another guard yielded a backend principal")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user