gopass/tests/config_test.go
google-labs-jules[bot] 8c60b17c24
feat(age): Add unlock command to age agent (#3244)
* feat(age): Add unlock command to age agent

This change introduces a proper lock/unlock mechanism for the age agent.

The issue was that after locking the agent with `gopass age lock`, there was no way to unlock it without restarting the agent. This made the lock command mostly useless.

This change introduces a new `unlock` command for the agent and a `locked` state.

- The `lock` command now sets a `locked` flag to `true` in addition to clearing identities.
- The `decrypt` function in the agent now checks this `locked` flag and returns an error if the agent is locked.
- When the gopass client receives the "agent is locked" error, it will ask the user for their passphrase, reload the identities, and send them to the agent.
- A new `gopass age agent unlock` CLI command is added to trigger this new functionality.
- The `gopass age agent status` command is enhanced to report whether the agent is locked.
- The old top-level `gopass age lock` command is hidden, and a new `gopass age agent lock` command is introduced for consistency.

Fixes #3242

* feat(age): Add unlock command to age agent

This change introduces a proper lock/unlock mechanism for the age agent.

The issue was that after locking the agent with `gopass age lock`, there was no way to unlock it without restarting the agent. This made the lock command mostly useless.

This change introduces a new `unlock` command for the agent and a `locked` state.

- The `lock` command now sets a `locked` flag to `true` in addition to clearing identities.
- The `decrypt` function in the agent now checks this `locked` flag and returns an error if the agent is locked.
- When the gopass client receives the "agent is locked" error, it will ask the user for their passphrase, reload the identities, and send them to the agent.
- A new `gopass age agent unlock` CLI command is added to trigger this new functionality.
- The `gopass age agent status` command is enhanced to report whether the agent is locked.
- The old top-level `gopass age lock` command is hidden, and a new `gopass age agent lock` command is introduced for consistency.

I have also addressed the PR comment about the import alias. I have removed the alias and used a dot import instead to avoid the name collision.

Fixes #3242

* feat(age): Add unlock command to age agent

This change introduces a proper lock/unlock mechanism for the age agent.

The issue was that after locking the agent with `gopass age lock`, there was no way to unlock it without restarting the agent. This made the lock command mostly useless.

This change introduces a new `unlock` command for the agent and a `locked` state.

- The `lock` command now sets a `locked` flag to `true` in addition to clearing identities.
- The `decrypt` function in the agent now checks this `locked` flag and returns an error if the agent is locked.
- When the gopass client receives the "agent is locked" error, it will ask the user for their passphrase, reload the identities, and send them to the agent.
- A new `gopass age agent unlock` CLI command is added to trigger this new functionality.
- The `gopass age agent status` command is enhanced to report whether the agent is locked.
- The old top-level `gopass age lock` command is hidden, and a new `gopass age agent lock` command is introduced for consistency.

To avoid name collisions with the imported `filippo.io/age` package, the local `age` package has been renamed to `agecrypto`.

Fixes #3242

* feat(age): Add auto-lock feature to age agent

This change introduces an auto-lock feature for the age agent. The agent will now automatically lock itself after a configurable period of inactivity.

This change also includes the initial fix for issue #3242, which introduced a proper lock/unlock mechanism for the age agent.

- A new config option `age.agent-timeout` is added to specify the inactivity timeout in seconds.
- The agent now has a timer that is reset on every successful decryption operation.
- If the timer expires, the agent locks itself.
- A new `set-timeout` command is added to the agent protocol to configure the timeout.
- The gopass client sends the timeout to the agent when it starts or when it unlocks the agent.
- A new test `TestAgentAutoLock` is added to verify the new functionality.

To avoid name collisions with the imported `filippo.io/age` package, the local `age` package has been renamed to `agecrypto`.

Fixes #3242

* [fix] Fix lint issues

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

---------

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Dominik Schulz <dominik.schulz@gauner.org>
2025-09-20 17:09:12 +02:00

91 lines
2.1 KiB
Go

package tests
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBaseConfig(t *testing.T) {
ts := newTester(t)
defer ts.teardown()
out, err := ts.run("config")
require.NoError(t, err)
wanted := `age.agent-enabled = false
age.agent-timeout = 0
core.autopush = true
core.autosync = true
core.cliptimeout = 45
core.exportkeys = false
core.follow-references = false
core.notifications = true
`
wanted += "mounts.path = " + ts.storeDir("root") + "\n" +
"pwgen.xkcd-lang = en"
assert.Equal(t, wanted, out)
invertables := []string{
"core.autoimport",
"show.safecontent",
}
for _, invert := range invertables {
t.Run("invert "+invert, func(t *testing.T) {
arg := "config " + invert + " false"
out, err = ts.run(arg)
require.NoError(t, err, "Running gopass "+arg)
assert.Equal(t, "false", out, "Output of gopass "+arg)
arg = "config " + invert
out, err = ts.run(arg)
require.NoError(t, err, "Running gopass "+arg)
assert.Equal(t, "false", out, "Output of gopass "+arg)
})
}
t.Run("cliptimeout", func(t *testing.T) {
out, err = ts.run("config core.cliptimeout 120")
require.NoError(t, err)
assert.Equal(t, "120", out)
out, err = ts.run("config core.cliptimeout")
require.NoError(t, err)
assert.Equal(t, "120", out)
})
}
func TestMountConfig(t *testing.T) {
ts := newTester(t)
defer ts.teardown()
// we add a mount:
_, err := ts.run("init --store mnt/m1 --path " + ts.storeDir("m1") + " --storage=fs " + keyID)
require.NoError(t, err)
_, err = ts.run("config")
require.NoError(t, err)
wanted := `age.agent-enabled = false
age.agent-timeout = 0
core.autopush = true
core.autosync = true
core.cliptimeout = 45
core.exportkeys = false
core.follow-references = false
core.notifications = true
`
wanted += "mounts.mnt/m1.path = " + ts.storeDir("m1") + "\n"
wanted += "mounts.path = " + ts.storeDir("root") + "\n"
wanted += "pwgen.xkcd-lang = en\n"
wanted += "recipients.mnt/m1.hash = 9a4c4b1e0eb9ade2e692ff948f43d9668145eca3df88ffff67e0e21426252907\n"
out, err := ts.run("config")
require.NoError(t, err)
assert.Equal(t, strings.TrimSpace(wanted), out)
}