Files
tutti/services/tuttid/data/workspace/sqlite_managed_credentials.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

288 lines
9.8 KiB
Go

package workspace
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"database/sql"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"time"
managedcredentialsbiz "github.com/tutti-os/tutti/services/tuttid/biz/managedcredentials"
)
func (s *SQLiteStore) ListManagedModelProviderConfigs(ctx context.Context, workspaceID string) ([]managedcredentialsbiz.ProviderConfig, error) {
if s == nil || s.writeDB == nil {
return nil, errors.New("workspace database is not initialized")
}
rows, err := s.readDB.QueryContext(ctx, `
SELECT workspace_id, provider_id, enabled, api_key_ciphertext, base_url, models_json, updated_at_unix_ms
FROM managed_model_provider_credentials
WHERE workspace_id = ?
ORDER BY provider_id
`, workspaceID)
if err != nil {
return nil, fmt.Errorf("list managed model provider configs: %w", err)
}
defer rows.Close()
var configs []managedcredentialsbiz.ProviderConfig
for rows.Next() {
config, err := scanManagedModelProviderConfig(rows)
if err != nil {
return nil, err
}
configs = append(configs, config)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("list managed model provider config rows: %w", err)
}
return configs, nil
}
func (s *SQLiteStore) GetManagedModelProviderConfig(ctx context.Context, workspaceID string, providerID managedcredentialsbiz.ProviderID) (managedcredentialsbiz.ProviderConfig, error) {
if s == nil || s.writeDB == nil {
return managedcredentialsbiz.ProviderConfig{}, errors.New("workspace database is not initialized")
}
row := s.readDB.QueryRowContext(ctx, `
SELECT workspace_id, provider_id, enabled, api_key_ciphertext, base_url, models_json, updated_at_unix_ms
FROM managed_model_provider_credentials
WHERE workspace_id = ? AND provider_id = ?
`, workspaceID, providerID)
config, err := scanManagedModelProviderConfig(row)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return managedcredentialsbiz.ProviderConfig{}, ErrWorkspaceAppNotFound
}
return managedcredentialsbiz.ProviderConfig{}, err
}
return config, nil
}
func (s *SQLiteStore) PutManagedModelProviderConfig(ctx context.Context, config managedcredentialsbiz.ProviderConfig) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
modelsJSON, err := json.Marshal(config.Models)
if err != nil {
return fmt.Errorf("marshal managed provider models: %w", err)
}
ciphertext, err := encryptManagedCredential(config.APIKey)
if err != nil {
return err
}
now := unixMs(time.Now().UTC())
_, err = s.writeDB.ExecContext(ctx, `
INSERT INTO managed_model_provider_credentials (
workspace_id, provider_id, enabled, api_key_ciphertext, base_url, models_json, updated_at_unix_ms
) VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(workspace_id, provider_id) DO UPDATE SET
enabled = excluded.enabled,
api_key_ciphertext = excluded.api_key_ciphertext,
base_url = excluded.base_url,
models_json = excluded.models_json,
updated_at_unix_ms = excluded.updated_at_unix_ms
`, config.WorkspaceID, string(config.Provider), boolInt(config.Enabled), ciphertext, config.BaseURL, string(modelsJSON), now)
if err != nil {
return fmt.Errorf("put managed model provider config: %w", err)
}
return nil
}
func (s *SQLiteStore) DeleteManagedModelProviderConfig(ctx context.Context, workspaceID string, providerID managedcredentialsbiz.ProviderID) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
_, err := s.writeDB.ExecContext(ctx, `
DELETE FROM managed_model_provider_credentials
WHERE workspace_id = ? AND provider_id = ?
`, workspaceID, providerID)
if err != nil {
return fmt.Errorf("delete managed model provider config: %w", err)
}
return nil
}
func (s *SQLiteStore) PutManagedModelGrant(ctx context.Context, grant managedcredentialsbiz.Grant) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
providersJSON, err := json.Marshal(grant.ProviderIDs)
if err != nil {
return fmt.Errorf("marshal managed grant providers: %w", err)
}
scopesJSON, err := json.Marshal(grant.Scopes)
if err != nil {
return fmt.Errorf("marshal managed grant scopes: %w", err)
}
_, err = s.writeDB.ExecContext(ctx, `
INSERT INTO managed_model_app_grants (
workspace_id, app_id, grant_ref, provider_ids_json, scopes_json, created_at_unix_ms, expires_at_unix_ms, revoked_at_unix_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, NULL)
ON CONFLICT(workspace_id, app_id, grant_ref) DO UPDATE SET
provider_ids_json = excluded.provider_ids_json,
scopes_json = excluded.scopes_json,
expires_at_unix_ms = excluded.expires_at_unix_ms,
revoked_at_unix_ms = NULL
`, grant.WorkspaceID, grant.AppID, grant.GrantRef, string(providersJSON), string(scopesJSON), unixMs(grant.CreatedAt), unixMs(grant.ExpiresAt))
if err != nil {
return fmt.Errorf("put managed model grant: %w", err)
}
return nil
}
func (s *SQLiteStore) GetManagedModelGrant(ctx context.Context, workspaceID string, appID string, grantRef string) (managedcredentialsbiz.Grant, error) {
if s == nil || s.writeDB == nil {
return managedcredentialsbiz.Grant{}, errors.New("workspace database is not initialized")
}
row := s.readDB.QueryRowContext(ctx, `
SELECT workspace_id, app_id, grant_ref, provider_ids_json, scopes_json, created_at_unix_ms, expires_at_unix_ms, revoked_at_unix_ms
FROM managed_model_app_grants
WHERE workspace_id = ? AND app_id = ? AND grant_ref = ?
`, workspaceID, appID, grantRef)
var grant managedcredentialsbiz.Grant
var providersJSON string
var scopesJSON string
var createdAtUnixMS int64
var expiresAtUnixMS int64
var revokedAt sql.NullInt64
if err := row.Scan(&grant.WorkspaceID, &grant.AppID, &grant.GrantRef, &providersJSON, &scopesJSON, &createdAtUnixMS, &expiresAtUnixMS, &revokedAt); err != nil {
return managedcredentialsbiz.Grant{}, err
}
_ = json.Unmarshal([]byte(providersJSON), &grant.ProviderIDs)
_ = json.Unmarshal([]byte(scopesJSON), &grant.Scopes)
grant.CreatedAt = time.UnixMilli(createdAtUnixMS).UTC()
grant.ExpiresAt = time.UnixMilli(expiresAtUnixMS).UTC()
grant.RevokedAt = nullableUnixMs(revokedAt)
return grant, nil
}
func (s *SQLiteStore) RevokeManagedModelGrant(ctx context.Context, workspaceID string, appID string, grantRef string) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
_, err := s.writeDB.ExecContext(ctx, `
UPDATE managed_model_app_grants
SET revoked_at_unix_ms = ?
WHERE workspace_id = ? AND app_id = ? AND grant_ref = ?
`, unixMs(time.Now().UTC()), workspaceID, appID, grantRef)
if err != nil {
return fmt.Errorf("revoke managed model grant: %w", err)
}
return nil
}
func (s *SQLiteStore) DeleteManagedModelGrant(ctx context.Context, workspaceID string, appID string, grantRef string) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
_, err := s.writeDB.ExecContext(ctx, `
DELETE FROM managed_model_app_grants
WHERE workspace_id = ? AND app_id = ? AND grant_ref = ?
`, workspaceID, appID, grantRef)
if err != nil {
return fmt.Errorf("delete managed model grant: %w", err)
}
return nil
}
type managedProviderScanner interface {
Scan(dest ...any) error
}
func scanManagedModelProviderConfig(row managedProviderScanner) (managedcredentialsbiz.ProviderConfig, error) {
var config managedcredentialsbiz.ProviderConfig
var providerID string
var enabled int
var ciphertext string
var modelsJSON string
var updatedAtUnixMS int64
if err := row.Scan(&config.WorkspaceID, &providerID, &enabled, &ciphertext, &config.BaseURL, &modelsJSON, &updatedAtUnixMS); err != nil {
return managedcredentialsbiz.ProviderConfig{}, err
}
apiKey, err := decryptManagedCredential(ciphertext)
if err != nil {
return managedcredentialsbiz.ProviderConfig{}, err
}
config.Provider = managedcredentialsbiz.ProviderID(providerID)
config.Enabled = enabled != 0
config.APIKey = apiKey
_ = json.Unmarshal([]byte(modelsJSON), &config.Models)
config.UpdatedAt = time.UnixMilli(updatedAtUnixMS).UTC()
return config, nil
}
func boolInt(value bool) int {
if value {
return 1
}
return 0
}
func managedCredentialKey() []byte {
seed := strings.TrimSpace(os.Getenv("TUTTI_MANAGED_CREDENTIAL_SECRET"))
if seed == "" {
hostname, _ := os.Hostname()
userConfigDir, _ := os.UserConfigDir()
seed = "tutti-managed-credentials:" + hostname + ":" + userConfigDir
}
sum := sha256.Sum256([]byte(seed))
return sum[:]
}
func encryptManagedCredential(value string) (string, error) {
if value == "" {
return "", nil
}
block, err := aes.NewCipher(managedCredentialKey())
if err != nil {
return "", fmt.Errorf("create managed credential cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("create managed credential gcm: %w", err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", fmt.Errorf("create managed credential nonce: %w", err)
}
ciphertext := gcm.Seal(nonce, nonce, []byte(value), nil)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
func decryptManagedCredential(value string) (string, error) {
if value == "" {
return "", nil
}
raw, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return "", fmt.Errorf("decode managed credential: %w", err)
}
block, err := aes.NewCipher(managedCredentialKey())
if err != nil {
return "", fmt.Errorf("create managed credential cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("create managed credential gcm: %w", err)
}
if len(raw) < gcm.NonceSize() {
return "", errors.New("managed credential ciphertext is malformed")
}
nonce := raw[:gcm.NonceSize()]
ciphertext := raw[gcm.NonceSize():]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", fmt.Errorf("decrypt managed credential: %w", err)
}
return string(plaintext), nil
}