tests/identity_reauth_test.go
Rama master · réplica de lectura del repositorio original.
package main
import (
"context"
"testing"
"time"
"github.com/shellhub-io/shellhub/pkg/api/requests"
"github.com/shellhub-io/shellhub/pkg/models"
"github.com/shellhub-io/shellhub/tests/environment"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
)
// TestAccessPolicyReauth covers when a policy's require_reauth holds an enrolled key for a fresh
// re-authentication: every time without a period, only once the period lapses with one, never for
// an API key, and at the shortest period when several policies ask. What the re-authentication
// approval itself does is covered by [TestSSHApprovalReauth].
func TestAccessPolicyReauth(t *testing.T) {
t.Parallel()
ctx := context.Background()
compose := newSSHEnvironment(t, ctx, newNamespace)
_, device := startAcceptedAgent(t, ctx, compose)
sshid := deviceSSHID(device)
t.Run("a policy with no period asks on every login", func(t *testing.T) {
requireReauth(t, compose, "always", nil)
signer, fingerprint := enrollOwnerKey(t, compose)
loginThroughReauth(t, compose, sshid, signer)
requireRecent(t, identityByFingerprint(t, compose, fingerprint).LastReauthAt)
loginThroughReauth(t, compose, sshid, signer)
})
t.Run("a policy with a period skips a fresh re-authentication and asks for a stale one", func(t *testing.T) {
requireReauth(t, compose, "hourly", new(3600))
signer, fingerprint := enrollOwnerKey(t, compose)
loginThroughReauth(t, compose, sshid, signer)
requireStraightThrough(t, compose, sshid, signer)
compose.AgeSSHIdentityReauth(t, fingerprint, time.Hour+time.Second)
loginThroughReauth(t, compose, sshid, signer)
})
t.Run("the period's window closes when the period elapses", func(t *testing.T) {
const period = 60
requireReauth(t, compose, "minutely", new(period))
signer, fingerprint := enrollOwnerKey(t, compose)
compose.AgeSSHIdentityReauth(t, fingerprint, (period-5)*time.Second)
reauthedAt := identityByFingerprint(t, compose, fingerprint).LastReauthAt
require.NotNil(t, reauthedAt)
requireStraightThrough(t, compose, sshid, signer)
require.Equal(t, reauthedAt, identityByFingerprint(t, compose, fingerprint).LastReauthAt, "a login inside the window renewed it")
compose.AgeSSHIdentityReauth(t, fingerprint, (period+1)*time.Second)
loginThroughReauth(t, compose, sshid, signer)
})
t.Run("an API key is never asked to re-authenticate", func(t *testing.T) {
key, signer := newAPIKeyIdentity(t, compose, "unattended", false)
grant(t, compose, &requests.AccessPolicyCreate{
Name: "unattended",
Subject: apiKeySubject(key.ID),
Filter: requests.AccessPolicyFilter{All: true},
Logins: []string{"*"},
RequireReauth: true,
})
requireStraightThrough(t, compose, sshid, signer)
requireStraightThrough(t, compose, sshid, signer)
require.Nil(t, apiKeyIdentity(t, compose, "unattended").LastReauthAt)
})
t.Run("the shorter of two periods decides", func(t *testing.T) {
requireReauth(t, compose, "hourly", new(3600))
requireReauth(t, compose, "minutely", new(60))
signer, fingerprint := enrollOwnerKey(t, compose)
compose.AgeSSHIdentityReauth(t, fingerprint, 30*time.Second)
requireStraightThrough(t, compose, sshid, signer)
compose.AgeSSHIdentityReauth(t, fingerprint, 2*time.Minute)
loginThroughReauth(t, compose, sshid, signer)
})
t.Run("a policy with no period outweighs one with a period", func(t *testing.T) {
requireReauth(t, compose, "hourly", new(3600))
requireReauth(t, compose, "always", nil)
signer, fingerprint := enrollOwnerKey(t, compose)
compose.AgeSSHIdentityReauth(t, fingerprint, 30*time.Second)
loginThroughReauth(t, compose, sshid, signer)
})
}
func requireReauth(t *testing.T, compose *environment.DockerCompose, name string, period *int) {
t.Helper()
grant(t, compose, &requests.AccessPolicyCreate{
Name: name,
Subject: requests.AccessPolicySubject{Type: string(models.PolicySubjectAllMembers)},
Filter: requests.AccessPolicyFilter{All: true},
Logins: []string{"*"},
RequireReauth: true,
ReauthPeriod: period,
})
}
func loginThroughReauth(t *testing.T, compose *environment.DockerCompose, sshid string, signer ssh.Signer) {
t.Helper()
parked := startLogin(t, compose, sshid, signer)
prompt := parked.awaitApproval(t)
require.Equal(t, models.SSHApprovalReauth, prompt.kind, "the login was held for something other than a re-authentication")
parked.answer(reauthenticate(t, compose, ssh.FingerprintSHA256(signer.PublicKey()), prompt.code))
require.NoError(t, parked.result(t), "the login should resume once the re-authentication is confirmed")
}