fix(14): WR-04 require the AI key again when the endpoint changes
An admin who cannot read the write-only key could point base_url at their own host, leave the key redacted and have it sent there on the next call. An update that keeps the stored key may no longer change the adapter or base URL; changing either needs the key re-entered.
This commit is contained in:
@@ -3,6 +3,7 @@ package controllers
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"git.golem15.com/golem15/sm-golem-plugin/models"
|
||||
"git.golem15.com/golem15/summercms/modules/cabana"
|
||||
@@ -15,7 +16,7 @@ const PermissionAccessSettings = "golem15.golem.access_settings"
|
||||
// ModelsAdminController is the AI models list and form
|
||||
// (golem15.golem.models). The API key is write-only: the form shows it
|
||||
// redacted, and an update that sends it back empty or redacted keeps the
|
||||
// stored key.
|
||||
// stored key, unless the same update changes the adapter or base URL.
|
||||
type ModelsAdminController struct{}
|
||||
|
||||
var (
|
||||
@@ -64,8 +65,19 @@ func (ModelsAdminController) FormBeforeCreate(_ context.Context, model any) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// baseURLOf is a model's base URL with nil and blank alike.
|
||||
func baseURLOf(m *models.AiModel) string {
|
||||
if m.BaseURL == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(*m.BaseURL)
|
||||
}
|
||||
|
||||
// FormBeforeUpdate keeps the stored key when the form sent it back empty
|
||||
// or redacted.
|
||||
// or redacted. A kept key never follows a changed endpoint: when the
|
||||
// adapter or base URL differs from the stored one, the key must be entered
|
||||
// again, so an operator who cannot read the key cannot redirect it to a
|
||||
// host they control either.
|
||||
func (ModelsAdminController) FormBeforeUpdate(ctx context.Context, model any) error {
|
||||
m, ok := model.(*models.AiModel)
|
||||
if !ok {
|
||||
@@ -79,9 +91,12 @@ func (ModelsAdminController) FormBeforeUpdate(ctx context.Context, model any) er
|
||||
return errors.New("golem: no transaction for the model update")
|
||||
}
|
||||
var stored models.AiModel
|
||||
if err := tx.Select("api_key").Where("id = ?", m.ID).Take(&stored).Error; err != nil {
|
||||
if err := tx.Select("api_key", "adapter", "base_url").Where("id = ?", m.ID).Take(&stored).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if stored.Adapter != m.Adapter || baseURLOf(&stored) != baseURLOf(m) {
|
||||
return &cabana.ValidationError{Details: map[string]any{"api_key": []string{"Re-enter the API key when changing the adapter or base URL."}}}
|
||||
}
|
||||
m.APIKey = stored.APIKey
|
||||
if keyMissing(m) {
|
||||
return &cabana.ValidationError{Details: map[string]any{"api_key": []string{"The api key field is required."}}}
|
||||
|
||||
Reference in New Issue
Block a user