Ctrl K
ComunidadGuías…

tests/enrollment_test.go

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

Archivo original ↗Preguntar a KiBot
package main

import (
	"context"
	"net/http"
	"testing"

	"github.com/go-resty/resty/v2"
	"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/assert"
	"github.com/stretchr/testify/require"
)

// TestEnrollmentPolicy enrolls devices with requests built by hand against the endpoint an agent
// enrolls through, and reads back what each provisioning key's policy made of them. The cases
// share one stack, so every device carries a hostname and a MAC address no other case uses:
// accepting a device merges it into any accepted device with the same MAC. What an agent does with
// a refusal is covered by [TestProvisioningKeyEnrollment].
func TestEnrollmentPolicy(t *testing.T) {
	t.Parallel()

	compose := newSSHEnvironment(t, t.Context(), newNamespace)

	t.Run("automatic", func(t *testing.T) { testAutomaticEnrollment(t, compose) })
	t.Run("manual", func(t *testing.T) { testManualEnrollment(t, compose) })
	t.Run("allowlist", func(t *testing.T) { testAllowlistEnrollment(t, compose) })
	t.Run("keyless", func(t *testing.T) { testKeylessEnrollment(t, compose) })
	t.Run("uid", func(t *testing.T) { testEnrollmentUID(t, compose) })
	t.Run("system keys", func(t *testing.T) { testSystemKeyPresented(t, compose, http.StatusBadRequest) })
	t.Run("re-registration", func(t *testing.T) { testReRegistration(t, compose) })
	t.Run("limits", func(t *testing.T) { testEnrollmentLimits(t, compose) })
	t.Run("events", func(t *testing.T) { testEnrollmentEvents(t, compose) })
	t.Run("search", func(t *testing.T) { testRegistrationSearch(t, compose) })
}

func TestLegacyEnrollment(t *testing.T) {
	t.Parallel()

	compose := newSSHEnvironment(t, t.Context(), legacyNamespace)

	t.Run("keyless", func(t *testing.T) { testKeylessEnrollment(t, compose) })
	t.Run("login code", func(t *testing.T) { testDeviceLoginCode(t, compose) })
	t.Run("agent login", func(t *testing.T) { testAgentLoginWithTenant(t, compose) })
	t.Run("provisioning key", func(t *testing.T) { testProvisioningKeyRefusedInLegacy(t, compose) })
	t.Run("system keys", func(t *testing.T) { testSystemKeyPresented(t, compose, http.StatusForbidden) })
	t.Run("pairing", func(t *testing.T) { testPairingRefusedInLegacy(t, compose) })
	t.Run("provisioning key API", func(t *testing.T) { testProvisioningKeyAPIInLegacy(t, compose) })
}

func newKeyedDeviceAuthRequest(t *testing.T, key, hostname, mac string) requests.DeviceAuth {
	t.Helper()

	req := newDeviceAuthRequest(t, hostname, mac)
	req.TenantID = ""
	req.ProvisioningKey = key

	return req
}

func enroll(t *testing.T, compose *environment.DockerCompose, req requests.DeviceAuth) models.Device {
	t.Helper()

	return requireDevice(t, compose, authDevice(t, compose, req).UID)
}

func requireDevice(t *testing.T, compose *environment.DockerCompose, uid string) models.Device {
	t.Helper()

	device, resp, err := compose.GetDevice(t.Context(), uid)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resp.StatusCode(), resp.String())

	return *device
}

func postDeviceAuth(ctx context.Context, compose *environment.DockerCompose, req requests.DeviceAuth) (*resty.Response, error) {
	req, err := signDeviceAuth(ctx, compose, req)
	if err != nil {
		return nil, err
	}

	return compose.Anonymous(ctx).SetBody(req).Post("/api/devices/auth")
}

func requireAuthRefused(t *testing.T, compose *environment.DockerCompose, req requests.DeviceAuth) {
	t.Helper()

	requireAuthAnswers(t, compose, req, http.StatusBadRequest)
}

func requireAuthForbidden(t *testing.T, compose *environment.DockerCompose, req requests.DeviceAuth) {
	t.Helper()

	requireAuthAnswers(t, compose, req, http.StatusForbidden)
}

func requireAuthAnswers(t *testing.T, compose *environment.DockerCompose, req requests.DeviceAuth, status int) {
	t.Helper()

	resp, err := postDeviceAuth(t.Context(), compose, req)
	require.NoError(t, err)
	require.Equal(t, status, resp.StatusCode(), resp.String())

	for _, device := range compose.ListDevices(t, models.DeviceStatusEmpty) {
		assert.NotEqual(t, req.Hostname, device.Name, "a refused enrollment left a device behind")
	}
}

func requireStatusOnReauth(t *testing.T, compose *environment.DockerCompose, req requests.DeviceAuth, uid string, status models.DeviceStatus) {
	t.Helper()

	reauthDevice(t, compose, req, uid)

	require.Equal(t, status, requireDevice(t, compose, uid).Status)
}

func reauthDevice(t *testing.T, compose *environment.DockerCompose, req requests.DeviceAuth, uid string) {
	t.Helper()

	compose.DropDeviceAuthCache(t, uid)

	resp, err := postDeviceAuth(t.Context(), compose, req)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resp.StatusCode(), resp.String())
}

func tagNames(tags []models.Tag) []string {
	names := make([]string, 0, len(tags))
	for _, tag := range tags {
		names = append(names, tag.Name)
	}

	return names
}

func eventOf(events []models.ProvisioningKeyEvent, uid string) *models.ProvisioningKeyEvent {
	for i := range events {
		if events[i].DeviceUID == uid {
			return &events[i]
		}
	}

	return nil
}