feat(security): 暴露连接配置与代理的密钥存储 API

This commit is contained in:
tianqijiuyun-latiao
2026-04-03 01:04:42 +08:00
parent b5e8f5c022
commit 263db6bf30
3 changed files with 155 additions and 1 deletions

View File

@@ -123,11 +123,26 @@ func proxyConfigEqual(a, b connection.ProxyConfig) bool {
a.Password == b.Password
}
func currentGlobalProxyView() connection.GlobalProxyView {
snapshot := currentGlobalProxyConfig()
if !snapshot.Enabled {
return connection.GlobalProxyView{Enabled: false}
}
return connection.GlobalProxyView{
Enabled: true,
Type: snapshot.Proxy.Type,
Host: snapshot.Proxy.Host,
Port: snapshot.Proxy.Port,
User: snapshot.Proxy.User,
HasPassword: strings.TrimSpace(snapshot.Proxy.Password) != "",
}
}
func (a *App) GetGlobalProxyConfig() connection.QueryResult {
return connection.QueryResult{
Success: true,
Message: "OK",
Data: currentGlobalProxyConfig(),
Data: currentGlobalProxyView(),
}
}
@@ -312,3 +327,4 @@ func buildProxyURLFromConfig(proxyConfig connection.ProxyConfig) (*url.URL, erro
}
return proxyURL, nil
}

View File

@@ -0,0 +1,44 @@
package app
import "GoNavi-Wails/internal/connection"
func (a *App) savedConnectionRepository() *savedConnectionRepository {
return newSavedConnectionRepository(a.configDir, a.secretStore)
}
func (a *App) GetSavedConnections() ([]connection.SavedConnectionView, error) {
return a.savedConnectionRepository().List()
}
func (a *App) SaveConnection(input connection.SavedConnectionInput) (connection.SavedConnectionView, error) {
return a.savedConnectionRepository().Save(input)
}
func (a *App) DeleteConnection(id string) error {
return a.savedConnectionRepository().Delete(id)
}
func (a *App) DuplicateConnection(id string) (connection.SavedConnectionView, error) {
return a.savedConnectionRepository().Duplicate(id)
}
func (a *App) ImportLegacyConnections(items []connection.LegacySavedConnection) ([]connection.SavedConnectionView, error) {
result := make([]connection.SavedConnectionView, 0, len(items))
repo := a.savedConnectionRepository()
for _, item := range items {
view, err := repo.Save(connection.SavedConnectionInput(item))
if err != nil {
return nil, err
}
result = append(result, view)
}
return result, nil
}
func (a *App) SaveGlobalProxy(input connection.SaveGlobalProxyInput) (connection.GlobalProxyView, error) {
return a.saveGlobalProxy(input)
}
func (a *App) ImportLegacyGlobalProxy(input connection.LegacyGlobalProxyInput) (connection.GlobalProxyView, error) {
return a.saveGlobalProxy(connection.SaveGlobalProxyInput(input))
}

View File

@@ -0,0 +1,94 @@
package app
import (
"testing"
"GoNavi-Wails/internal/connection"
)
func TestSaveConnectionMethodReturnsSecretlessView(t *testing.T) {
app := NewAppWithSecretStore(newFakeAppSecretStore())
app.configDir = t.TempDir()
result, err := app.SaveConnection(connection.SavedConnectionInput{
ID: "conn-1",
Name: "Primary",
Config: connection.ConnectionConfig{
ID: "conn-1",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
Password: "postgres-secret",
},
})
if err != nil {
t.Fatal(err)
}
if result.Config.Password != "" {
t.Fatal("SaveConnection must not return plaintext password")
}
if !result.HasPrimaryPassword {
t.Fatal("expected HasPrimaryPassword=true")
}
}
func TestDuplicateConnectionClonesSecretBundle(t *testing.T) {
app := NewAppWithSecretStore(newFakeAppSecretStore())
app.configDir = t.TempDir()
_, err := app.SaveConnection(connection.SavedConnectionInput{
ID: "conn-1",
Name: "Primary",
Config: connection.ConnectionConfig{
ID: "conn-1",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
Password: "postgres-secret",
},
})
if err != nil {
t.Fatal(err)
}
duplicate, err := app.DuplicateConnection("conn-1")
if err != nil {
t.Fatal(err)
}
if duplicate.ID == "conn-1" {
t.Fatal("duplicate should have a new id")
}
resolved, err := app.resolveConnectionSecrets(duplicate.Config)
if err != nil {
t.Fatal(err)
}
if resolved.Password != "postgres-secret" {
t.Fatalf("expected duplicated secret bundle, got %q", resolved.Password)
}
}
func TestSaveGlobalProxyReturnsSecretlessView(t *testing.T) {
app := NewAppWithSecretStore(newFakeAppSecretStore())
app.configDir = t.TempDir()
view, err := app.SaveGlobalProxy(connection.SaveGlobalProxyInput{
Enabled: true,
Type: "http",
Host: "127.0.0.1",
Port: 8080,
User: "ops",
Password: "proxy-secret",
})
if err != nil {
t.Fatal(err)
}
if view.Password != "" {
t.Fatal("global proxy view must not expose plaintext password")
}
if !view.HasPassword {
t.Fatal("expected hasPassword=true")
}
}