Files
sm-user-plugin/controllers/token_me.go
Jakub Zych 0f545f5c81 feat: user MCP guard provider, plugin frontend permissions and the user_me tool
- classes.FrontendPermissionCatalog: the seeded rows, then codes activated
  plugins declare through pact.HasFrontendPermissions (seeded row wins);
  both permission editors offer it
- classes.MCPUserGuard (guard "user"): reads user.api_token, user.scope:*
  and user.permission:*, allows with the route's checks (session scopes,
  group grants, every code), dispatches with an in-memory token credential,
  resolves usr_ personal tokens; registered in *pact.MCPGuards and published
  as pact.MCPPersonalTokenResolver
- GET /_user/api/v1/token/me (user.api_token, user.scope:read) and the MCP
  tool me (user_me)
- pact.MCPSubjectChanged on token revoke, Users saves and User Groups saves
  and deletes
2026-10-23 12:08:38 +02:00

40 lines
1.2 KiB
Go

package controllers
import (
"net/http"
"git.golem15.com/golem15/sm-user-plugin/models"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/bouncer"
)
// TokenMe serves GET /_user/api/v1/token/me behind user.api_token and
// user.scope:read: the token owner's user payload, the same {"user": ...}
// body GET /fetch answers for a session. It is the target of the MCP tool
// user_me. The guard answers 401 for a missing or unusable token and the
// scope middleware 403 for a token without read.
func TokenMe(app *backpack.App) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
principal, ok := bouncer.User(r.Context())
if !ok || principal == nil || principal.ID == 0 {
writeUnauthorized(w)
return
}
gdb, _, ok := sessionDeps(w, app)
if !ok {
return
}
var user models.User
if err := gdb.WithContext(r.Context()).Where("id = ?", principal.ID).Take(&user).Error; err != nil {
writeUnauthorized(w)
return
}
payload, err := apiArray(r.Context(), app, &user)
if err != nil {
writeOpaque500(w)
return
}
writeJSON(w, http.StatusOK, map[string]any{"user": payload})
}
}