- 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
40 lines
1.2 KiB
Go
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})
|
|
}
|
|
}
|