feat(captain): secure custom tool auth config

This commit is contained in:
2026-06-07 16:32:13 +08:00
parent 8f9adecd04
commit f45fbfd5f0
4 changed files with 135 additions and 11 deletions
@@ -33,6 +33,9 @@ func (h *CaptainCustomToolHandler) Create(c *gin.Context) {
if !h.ensureCustomToolsEnabled(c, accountID) {
return
}
if !h.ensureCustomToolAdmin(c) {
return
}
var req service.CreateCustomToolRequest
if err := bindNestedJSONPayload(c, "custom_tool", &req); err != nil {
@@ -54,7 +57,7 @@ func (h *CaptainCustomToolHandler) Create(c *gin.Context) {
return
}
c.JSON(http.StatusOK, captainCustomToolPayload(tool))
c.JSON(http.StatusOK, captainCustomToolPayload(c, tool))
}
// Get retrieves a custom tool by ID.
@@ -81,7 +84,7 @@ func (h *CaptainCustomToolHandler) Get(c *gin.Context) {
return
}
c.JSON(http.StatusOK, captainCustomToolPayload(tool))
c.JSON(http.StatusOK, captainCustomToolPayload(c, tool))
}
// Update updates an existing custom tool.
@@ -95,6 +98,9 @@ func (h *CaptainCustomToolHandler) Update(c *gin.Context) {
if !h.ensureCustomToolsEnabled(c, accountID) {
return
}
if !h.ensureCustomToolAdmin(c) {
return
}
id, err := parseUintAnyParam(c, "tool_id", "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
@@ -117,7 +123,7 @@ func (h *CaptainCustomToolHandler) Update(c *gin.Context) {
return
}
c.JSON(http.StatusOK, captainCustomToolPayload(tool))
c.JSON(http.StatusOK, captainCustomToolPayload(c, tool))
}
// Delete deletes a custom tool.
@@ -131,6 +137,9 @@ func (h *CaptainCustomToolHandler) Delete(c *gin.Context) {
if !h.ensureCustomToolsEnabled(c, accountID) {
return
}
if !h.ensureCustomToolAdmin(c) {
return
}
id, err := parseUintAnyParam(c, "tool_id", "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
@@ -167,7 +176,7 @@ func (h *CaptainCustomToolHandler) List(c *gin.Context) {
payload := make([]gin.H, 0, len(tools))
for i := range tools {
payload = append(payload, captainCustomToolPayload(&tools[i]))
payload = append(payload, captainCustomToolPayload(c, &tools[i]))
}
c.JSON(http.StatusOK, gin.H{"payload": payload, "meta": gin.H{"total_count": count, "page": 1}})
}
@@ -208,6 +217,9 @@ func (h *CaptainCustomToolHandler) TestTool(c *gin.Context) {
if !h.ensureCustomToolsEnabled(c, accountID) {
return
}
if !h.ensureCustomToolAdmin(c) {
return
}
var req service.TestToolRequest
if err := bindNestedJSONPayload(c, "custom_tool", &req); err != nil {
@@ -233,6 +245,17 @@ func (h *CaptainCustomToolHandler) ensureCustomToolsEnabled(c *gin.Context, acco
return false
}
func (h *CaptainCustomToolHandler) ensureCustomToolAdmin(c *gin.Context) bool {
if role, exists := c.Get("role"); exists {
if role == "administrator" || role == "super_admin" {
return true
}
c.JSON(http.StatusForbidden, gin.H{"error": "You are not authorized to do this action"})
return false
}
return true
}
func renderCaptainCustomToolValidationError(c *gin.Context, err error) bool {
var validationErr *service.CaptainCustomToolValidationError
if !errors.As(err, &validationErr) {
@@ -245,8 +268,8 @@ func renderCaptainCustomToolValidationError(c *gin.Context, err error) bool {
return true
}
func captainCustomToolPayload(tool *model.CaptainCustomTool) gin.H {
return gin.H{
func captainCustomToolPayload(c *gin.Context, tool *model.CaptainCustomTool) gin.H {
payload := gin.H{
"id": tool.ID,
"slug": tool.Slug,
"title": tool.Title,
@@ -256,11 +279,19 @@ func captainCustomToolPayload(tool *model.CaptainCustomTool) gin.H {
"request_template": tool.RequestTemplate,
"response_template": tool.ResponseTemplate,
"auth_type": tool.AuthType,
"auth_config": rawJSONValue(tool.AuthConfig),
"param_schema": rawJSONValue(tool.ParamSchema),
"enabled": tool.Enabled,
"account_id": tool.AccountID,
"created_at": tool.CreatedAt.Unix(),
"updated_at": tool.UpdatedAt.Unix(),
}
if captainCustomToolShowAuthConfig(c) {
payload["auth_config"] = rawJSONValue(tool.AuthConfig)
}
return payload
}
func captainCustomToolShowAuthConfig(c *gin.Context) bool {
role, exists := c.Get("role")
return exists && (role == "administrator" || role == "super_admin")
}