Ctrl K
ComunidadGuías…

tests/identity_reauth_test.go

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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")
}