- Add API server with WebSocket support and REST endpoints - Implement authentication system with API keys and permissions - Add task queue system with Redis backend and error handling - Include storage layer with database migrations and schemas - Add comprehensive logging, metrics, and telemetry - Implement security middleware and network utilities - Add experiment management and container orchestration - Include configuration management with smart defaults
129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/zalando/go-keyring"
|
|
)
|
|
|
|
type fakeKeyring struct {
|
|
secrets map[string]string
|
|
setErr error
|
|
getErr error
|
|
deleteErr error
|
|
}
|
|
|
|
func newFakeKeyring() *fakeKeyring {
|
|
return &fakeKeyring{secrets: make(map[string]string)}
|
|
}
|
|
|
|
func (f *fakeKeyring) Set(service, account, secret string) error {
|
|
if f.setErr != nil {
|
|
return f.setErr
|
|
}
|
|
f.secrets[key(service, account)] = secret
|
|
return nil
|
|
}
|
|
|
|
func (f *fakeKeyring) Get(service, account string) (string, error) {
|
|
if f.getErr != nil {
|
|
return "", f.getErr
|
|
}
|
|
if secret, ok := f.secrets[key(service, account)]; ok {
|
|
return secret, nil
|
|
}
|
|
return "", keyring.ErrNotFound
|
|
}
|
|
|
|
func (f *fakeKeyring) Delete(service, account string) error {
|
|
if f.deleteErr != nil {
|
|
return f.deleteErr
|
|
}
|
|
delete(f.secrets, key(service, account))
|
|
return nil
|
|
}
|
|
|
|
func key(service, account string) string {
|
|
return service + ":" + account
|
|
}
|
|
|
|
func newTestManager(t *testing.T, kr systemKeyring) (*KeychainManager, string) {
|
|
t.Helper()
|
|
baseDir := t.TempDir()
|
|
return newKeychainManagerWithKeyring(kr, baseDir), baseDir
|
|
}
|
|
|
|
func TestKeychainStoreAndGetPrimary(t *testing.T) {
|
|
kr := newFakeKeyring()
|
|
km, baseDir := newTestManager(t, kr)
|
|
|
|
if err := km.StoreAPIKey("fetch-ml", "alice", "super-secret"); err != nil {
|
|
t.Fatalf("StoreAPIKey failed: %v", err)
|
|
}
|
|
|
|
got, err := km.GetAPIKey("fetch-ml", "alice")
|
|
if err != nil {
|
|
t.Fatalf("GetAPIKey failed: %v", err)
|
|
}
|
|
if got != "super-secret" {
|
|
t.Fatalf("expected secret to be stored in primary keyring")
|
|
}
|
|
|
|
// Ensure fallback file was not created when primary succeeds
|
|
path := filepath.Join(baseDir, filepath.Base(km.fallback.path("fetch-ml", "alice")))
|
|
if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
|
|
t.Fatalf("expected no fallback file, got err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestKeychainFallbackWhenUnsupported(t *testing.T) {
|
|
kr := newFakeKeyring()
|
|
kr.setErr = keyring.ErrUnsupportedPlatform
|
|
kr.getErr = keyring.ErrUnsupportedPlatform
|
|
kr.deleteErr = keyring.ErrUnsupportedPlatform
|
|
km, _ := newTestManager(t, kr)
|
|
|
|
if err := km.StoreAPIKey("fetch-ml", "bob", "fallback-secret"); err != nil {
|
|
t.Fatalf("StoreAPIKey should fallback: %v", err)
|
|
}
|
|
|
|
got, err := km.GetAPIKey("fetch-ml", "bob")
|
|
if err != nil {
|
|
t.Fatalf("GetAPIKey should use fallback: %v", err)
|
|
}
|
|
if got != "fallback-secret" {
|
|
t.Fatalf("expected fallback secret, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestKeychainDeleteRemovesFallback(t *testing.T) {
|
|
kr := newFakeKeyring()
|
|
kr.deleteErr = keyring.ErrNotFound
|
|
km, _ := newTestManager(t, kr)
|
|
|
|
if err := km.fallback.store("fetch-ml", "carol", "temp"); err != nil {
|
|
t.Fatalf("failed to seed fallback store: %v", err)
|
|
}
|
|
|
|
if err := km.DeleteAPIKey("fetch-ml", "carol"); err != nil {
|
|
t.Fatalf("DeleteAPIKey failed: %v", err)
|
|
}
|
|
|
|
if _, err := km.fallback.get("fetch-ml", "carol"); !errors.Is(err, os.ErrNotExist) {
|
|
t.Fatalf("expected fallback secret removed, err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestListAvailableMethodsIncludesFallback(t *testing.T) {
|
|
kr := newFakeKeyring()
|
|
kr.getErr = keyring.ErrUnsupportedPlatform
|
|
km, _ := newTestManager(t, kr)
|
|
|
|
methods := km.ListAvailableMethods()
|
|
if len(methods) != 1 || methods[0] == "OS keyring" {
|
|
t.Fatalf("expected only fallback method, got %v", methods)
|
|
}
|
|
}
|