feat(tokens): refuse minting mcp scopes without the MCP licence feature
This commit is contained in:
@@ -4,10 +4,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/auth"
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/services"
|
||||
"gitea.hostxtra.co.uk/vantage/vantage-shared/license"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -109,6 +111,30 @@ func createToken(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// The MCP scopes are refused without the licence feature, matching the
|
||||
// guard-at-source thinking in services/packages.go rather than relying on
|
||||
// RequireFeature at the /api/mcp group alone. That gate is a runtime one:
|
||||
// without this, a licence downgrade leaves live agent credentials that
|
||||
// authenticate, list as MCP tokens in the UI, and then fail mid-
|
||||
// conversation with a 403 the model cannot explain. Refusing at minting
|
||||
// means a token carrying mcp:* only ever existed while the feature did.
|
||||
//
|
||||
// Existing tokens are deliberately untouched by a downgrade: the route
|
||||
// gate already stops them reaching the endpoint, and silently revoking
|
||||
// credentials on a billing change is worse than refusing new ones.
|
||||
if !services.GetLicenseState(auth.InstanceID(c)).Feature(license.FeatureMCP) {
|
||||
for _, s := range body.Scopes {
|
||||
if strings.HasPrefix(s, "mcp:") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "feature_unavailable",
|
||||
"feature": license.FeatureMCP,
|
||||
"code": "feature_unavailable",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !services.SelectorNarrowerOrEqual(body.TagSelector, auth.ServerScope(c)) {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "a token cannot reach servers its creator cannot reach",
|
||||
|
||||
Reference in New Issue
Block a user