Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ export GOG_KEYRING_BACKEND=file

Precedence: `GOG_KEYRING_BACKEND` env var overrides `config.json`.

Override the OS keyring service/collection/wallet name via env:

```bash
export GOG_KEYRING_SERVICE_NAME=kdewallet
```

This changes the keyring service name passed to the OS backend. On KDE/KWallet this controls the wallet name used by the native KWallet backend.

## Configuration

### Account Selection
Expand Down
1 change: 1 addition & 0 deletions docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ Environment:
- `GOG_CLIENT=work` (select OAuth client bucket; see `--client`)
- `GOG_KEYRING_PASSWORD=...` (used when keyring falls back to encrypted file backend in non-interactive environments)
- `GOG_KEYRING_BACKEND={auto|keychain|file}` (force backend; use `file` to avoid Keychain prompts and pair with `GOG_KEYRING_PASSWORD` for non-interactive)
- `GOG_KEYRING_SERVICE_NAME=...` (override keyring `ServiceName`; on native KWallet this is the wallet name)
- `GOG_TIMEZONE=America/New_York` (default output timezone; IANA name or `UTC`; `local` forces local timezone)
- `GOG_ENABLE_COMMANDS=calendar,tasks` (optional allowlist of top-level commands)
- `config.json` can also set `keyring_backend` (JSON5; env vars take precedence)
Expand Down
11 changes: 10 additions & 1 deletion internal/secrets/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func keyringItem(key string, data []byte) keyring.Item {
const (
keyringPasswordEnv = "GOG_KEYRING_PASSWORD" //nolint:gosec // env var name, not a credential
keyringBackendEnv = "GOG_KEYRING_BACKEND" //nolint:gosec // env var name, not a credential
keyringServiceEnv = "GOG_KEYRING_SERVICE_NAME" //nolint:gosec // env var name, not a credential
)

var (
Expand Down Expand Up @@ -144,6 +145,14 @@ func normalizeKeyringBackend(value string) string {
return strings.ToLower(strings.TrimSpace(value))
}

func keyringServiceName() string {
if value := strings.TrimSpace(os.Getenv(keyringServiceEnv)); value != "" {
return value
}

return config.AppName
}

// keyringOpenTimeout is the maximum time to wait for keyring.Open() to complete.
// On headless Linux, D-Bus SecretService can hang indefinitely if gnome-keyring
// is installed but not running.
Expand Down Expand Up @@ -185,7 +194,7 @@ func openKeyring() (keyring.Keyring, error) {
}

cfg := keyring.Config{
ServiceName: config.AppName,
ServiceName: keyringServiceName(),
// KeychainTrustApplication is intentionally false to support Homebrew upgrades.
// When true, macOS Keychain ties access control to the specific binary hash.
// Homebrew upgrades install a new binary with a different hash, causing the
Expand Down
49 changes: 48 additions & 1 deletion internal/secrets/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,30 @@ var errKeyringOpenBlocked = errors.New("keyring open blocked")
// KeychainTrustApplication is false to match production config (see store.go).
func keyringConfig(keyringDir string) keyring.Config {
return keyring.Config{
ServiceName: config.AppName,
ServiceName: keyringServiceName(),
KeychainTrustApplication: false,
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
FileDir: keyringDir,
FilePasswordFunc: fileKeyringPasswordFunc(),
}
}

func TestKeyringServiceName_Default(t *testing.T) {
t.Setenv(keyringServiceEnv, "")

if got := keyringServiceName(); got != config.AppName {
t.Fatalf("expected default service name %q, got %q", config.AppName, got)
}
}

func TestKeyringServiceName_Env(t *testing.T) {
t.Setenv(keyringServiceEnv, "kdewallet")

if got := keyringServiceName(); got != "kdewallet" {
t.Fatalf("expected env service name %q, got %q", "kdewallet", got)
}
}

func TestResolveKeyringBackendInfo_Default(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
Expand Down Expand Up @@ -288,3 +304,34 @@ func TestOpenKeyring_ExplicitBackend_IgnoresDBusDetection(t *testing.T) {
t.Fatal("expected non-nil store")
}
}

func TestOpenKeyring_UsesEnvServiceName(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, "xdg-config"))
t.Setenv("GOG_KEYRING_BACKEND", "file")
t.Setenv("GOG_KEYRING_PASSWORD", "testpw")
t.Setenv(keyringServiceEnv, "kdewallet")

originalOpen := keyringOpenFunc
t.Cleanup(func() { keyringOpenFunc = originalOpen })

var got keyring.Config
keyringOpenFunc = func(cfg keyring.Config) (keyring.Keyring, error) {
got = cfg
return keyring.NewArrayKeyring(nil), nil
}

ring, err := openKeyring()
if err != nil {
t.Fatalf("openKeyring: %v", err)
}

if ring == nil {
t.Fatal("expected non-nil keyring")
}

if got.ServiceName != "kdewallet" {
t.Fatalf("expected service name %q, got %q", "kdewallet", got.ServiceName)
}
}