feat(resolvemcp): read/write limits, preload validation, query timeout, stable client error codes

Config gains DefaultLimit/MaxLimit/MaxOffset/MaxBatch/MaxPreloadDepth/MaxWriteRows/QueryTimeout/
ConfirmTTL. Reads are capped and the total COUNT is optional. Errors reach clients as
{code,message}; everything else is logged with a reference. Panics (handler and hooks) are
recovered without returning the panic value.
This commit is contained in:
Hein
2026-10-01 13:35:00 +02:00
parent 82f901a49c
commit 276c3814d8
8 changed files with 414 additions and 36 deletions
+12 -4
View File
@@ -31,7 +31,7 @@ func RegisterSecurityHooks(handler *Handler, securityList *security.SecurityList
hookCtx.Abort = true
hookCtx.AbortMessage = err.Error()
hookCtx.AbortCode = http.StatusUnauthorized
return err
return NewClientError(CodeForbidden, err.Error())
}
return nil
})
@@ -83,17 +83,17 @@ func RegisterSecurityHooks(handler *Handler, securityList *security.SecurityList
// BeforeCreate: enforce CanCreate rule.
handler.Hooks().Register(BeforeCreate, func(hookCtx *HookContext) error {
return security.CheckModelCreateAllowed(newSecurityContext(hookCtx))
return forbidden(security.CheckModelCreateAllowed(newSecurityContext(hookCtx)))
})
// BeforeUpdate: enforce CanUpdate rule.
handler.Hooks().Register(BeforeUpdate, func(hookCtx *HookContext) error {
return security.CheckModelUpdateAllowed(newSecurityContext(hookCtx))
return forbidden(security.CheckModelUpdateAllowed(newSecurityContext(hookCtx)))
})
// BeforeDelete: enforce CanDelete rule.
handler.Hooks().Register(BeforeDelete, func(hookCtx *HookContext) error {
return security.CheckModelDeleteAllowed(newSecurityContext(hookCtx))
return forbidden(security.CheckModelDeleteAllowed(newSecurityContext(hookCtx)))
})
logger.Info("Security hooks registered for resolvemcp handler")
@@ -167,3 +167,11 @@ func (s *securityContext) GetResult() interface{} {
func (s *securityContext) SetResult(result interface{}) {
s.ctx.Result = result
}
// forbidden marks a rule denial as safe to show the client; nil passes through.
func forbidden(err error) error {
if err == nil {
return nil
}
return NewClientError(CodeForbidden, err.Error())
}