Files
probo/e2e/console/device_enrollment_test.go
Ludovic Vielle 238c19d509 Add pre-assume enrolled device status query
The /enroll wait UI polled device state via node(), which
requires an assumed org session, so confirmation never
succeeded for unassumed viewers. Expose viewer.enrolledDevice
behind itam:employee-device:get (own-device, skip assumption)
and point the poller at it.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-07-24 15:08:34 +02:00

867 lines
23 KiB
Go

// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package console_test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"testing"
"github.com/stretchr/testify/require"
"go.probo.inc/probo/e2e/internal/testutil"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
)
const (
enrollDeviceMutation = `
mutation EnrollDevice($input: EnrollDeviceInput!) {
enrollDevice(input: $input) {
enrollmentToken
serverUrl
enrollmentUrl
device { id }
}
}`
createDeviceMutation = `
mutation CreateDevice($input: CreateDeviceInput!) {
createDevice(input: $input) {
enrollmentToken
serverUrl
enrollmentUrl
device { id }
}
}`
revokeDeviceMutation = `
mutation RevokeDevice($input: RevokeDeviceInput!) {
revokeDevice(input: $input) {
device { id state }
}
}`
devicePermissionQuery = `
query DevicePermission($orgId: ID!) {
node(id: $orgId) {
... on Organization {
canEnrollDevice: permission(action: "itam:device:enroll")
}
}
}`
getDeviceQuery = `
query GetDevice($id: ID!) {
node(id: $id) {
... on Device {
id
state
owner {
id
fullName
}
}
}
}`
listDevicesQuery = `
query ListDevices($orgId: ID!) {
node(id: $orgId) {
... on Organization {
devices(first: 1) {
totalCount
}
}
}
}`
listEnrolledDevicesQuery = `
query ListEnrolledDevices($orgId: ID!) {
viewer {
enrolledDevices(organizationId: $orgId, first: 100) {
edges {
node {
id
state
}
}
}
}
}`
getEnrolledDeviceQuery = `
query GetEnrolledDevice($id: ID!) {
viewer {
enrolledDevice(id: $id) {
id
state
hostname
}
}
}`
)
type enrollDeviceResult struct {
EnrollDevice struct {
EnrollmentToken string `json:"enrollmentToken"`
ServerURL string `json:"serverUrl"`
EnrollmentURL string `json:"enrollmentUrl"`
Device struct {
ID string `json:"id"`
} `json:"device"`
} `json:"enrollDevice"`
}
type createDeviceResult struct {
CreateDevice struct {
EnrollmentToken string `json:"enrollmentToken"`
ServerURL string `json:"serverUrl"`
EnrollmentURL string `json:"enrollmentUrl"`
Device struct {
ID string `json:"id"`
} `json:"device"`
} `json:"createDevice"`
}
type enrollAPIResponse struct {
APIKey string `json:"api_key"`
}
func exchangeEnrollmentToken(t *testing.T, token string) (int, enrollAPIResponse) {
t.Helper()
body, err := json.Marshal(map[string]string{"token": token})
require.NoError(t, err)
req, err := http.NewRequest(
http.MethodPost,
testutil.GetBaseURL()+"/api/agent/v1/enroll",
bytes.NewReader(body),
)
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
raw, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var payload enrollAPIResponse
if resp.StatusCode == http.StatusOK {
require.NoError(t, json.Unmarshal(raw, &payload))
}
return resp.StatusCode, payload
}
func assertEnrollmentURLs(t *testing.T, serverURL, enrollmentURL, enrollmentToken string) {
t.Helper()
require.Equal(t, testutil.GetBaseURL(), serverURL)
parsed, err := url.Parse(enrollmentURL)
require.NoError(t, err)
require.Equal(t, "probo", parsed.Scheme)
require.Equal(t, "enroll", parsed.Host)
require.Equal(t, serverURL, parsed.Query().Get("server"))
require.Equal(t, enrollmentToken, parsed.Query().Get("token"))
}
func enrollDevice(t *testing.T, client *testutil.Client, organizationID string) enrollDeviceResult {
t.Helper()
var result enrollDeviceResult
client.MustExecute(enrollDeviceMutation, map[string]any{
"input": map[string]any{
"organizationId": organizationID,
},
}, &result)
require.NotEmpty(t, result.EnrollDevice.EnrollmentToken)
require.NotEmpty(t, result.EnrollDevice.Device.ID)
assertEnrollmentURLs(
t,
result.EnrollDevice.ServerURL,
result.EnrollDevice.EnrollmentURL,
result.EnrollDevice.EnrollmentToken,
)
return result
}
func activateEnrolledDevice(t *testing.T, enrollmentToken, hardwareUUID string) {
t.Helper()
status, payload := exchangeEnrollmentToken(t, enrollmentToken)
require.Equal(t, http.StatusOK, status)
require.NotEmpty(t, payload.APIKey)
body, err := json.Marshal(map[string]any{
"hardware_uuid": hardwareUUID,
"hostname": "e2e-host",
"platform": "DARWIN",
"os_version": "14.0",
"agent_version": "1.0.0",
})
require.NoError(t, err)
req, err := http.NewRequest(
http.MethodPost,
testutil.GetBaseURL()+"/api/agent/v1/heartbeat",
bytes.NewReader(body),
)
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", payload.APIKey))
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
require.Equal(t, http.StatusOK, resp.StatusCode)
}
func sendHeartbeat(t *testing.T, apiKey, hardwareUUID string) int {
t.Helper()
body, err := json.Marshal(map[string]any{
"hardware_uuid": hardwareUUID,
"hostname": "e2e-host",
"platform": "DARWIN",
"os_version": "14.0",
"agent_version": "1.0.0",
})
require.NoError(t, err)
req, err := http.NewRequest(
http.MethodPost,
testutil.GetBaseURL()+"/api/agent/v1/heartbeat",
bytes.NewReader(body),
)
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
return resp.StatusCode
}
func enrollAndActivateDevice(
t *testing.T,
client *testutil.Client,
organizationID string,
) enrollDeviceResult {
t.Helper()
enrolled := enrollDevice(t, client, organizationID)
activateEnrolledDevice(
t,
enrolled.EnrollDevice.EnrollmentToken,
enrolled.EnrollDevice.Device.ID+"-hw",
)
return enrolled
}
func createDevice(
t *testing.T,
client *testutil.Client,
organizationID string,
ownerProfileID *string,
) createDeviceResult {
t.Helper()
input := map[string]any{
"organizationId": organizationID,
}
if ownerProfileID != nil {
input["ownerId"] = *ownerProfileID
}
var result createDeviceResult
client.MustExecute(createDeviceMutation, map[string]any{"input": input}, &result)
require.NotEmpty(t, result.CreateDevice.EnrollmentToken)
require.NotEmpty(t, result.CreateDevice.Device.ID)
assertEnrollmentURLs(
t,
result.CreateDevice.ServerURL,
result.CreateDevice.EnrollmentURL,
result.CreateDevice.EnrollmentToken,
)
return result
}
func listEnrolledDeviceIDs(
t *testing.T,
client *testutil.Client,
organizationID string,
) []string {
t.Helper()
var result struct {
Viewer struct {
EnrolledDevices struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"enrolledDevices"`
} `json:"viewer"`
}
client.MustExecute(listEnrolledDevicesQuery, map[string]any{"orgId": organizationID}, &result)
ids := make([]string, len(result.Viewer.EnrolledDevices.Edges))
for i, edge := range result.Viewer.EnrolledDevices.Edges {
ids[i] = edge.Node.ID
}
return ids
}
func setupDeviceEnrollmentClients(t *testing.T) (
owner, admin, employee, viewer *testutil.Client,
orgID, ownerProfileID string,
) {
t.Helper()
owner = testutil.NewClient(t, testutil.RoleOwner)
admin = testutil.NewClientInOrg(t, testutil.RoleAdmin, owner)
employee = testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
viewer = testutil.NewClientInOrg(t, testutil.RoleViewer, owner)
orgID = owner.GetOrganizationID().String()
ownerProfileID = owner.GetProfileID().String()
return owner, admin, employee, viewer, orgID, ownerProfileID
}
func TestDeviceEnrollment(t *testing.T) {
t.Parallel()
t.Run("enrollment token can be exchanged once", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollDevice(t, employee, orgID)
status, payload := exchangeEnrollmentToken(t, enrolled.EnrollDevice.EnrollmentToken)
require.Equal(t, http.StatusOK, status)
require.NotEmpty(t, payload.APIKey)
replayStatus, _ := exchangeEnrollmentToken(t, enrolled.EnrollDevice.EnrollmentToken)
require.Equal(t, http.StatusUnauthorized, replayStatus)
})
t.Run("revoked device enrollment token returns unauthorized", func(t *testing.T) {
t.Parallel()
owner, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollDevice(t, employee, orgID)
owner.MustExecute(revokeDeviceMutation, map[string]any{
"input": map[string]any{
"deviceId": enrolled.EnrollDevice.Device.ID,
},
}, &struct {
RevokeDevice struct {
Device struct {
State string `json:"state"`
} `json:"device"`
} `json:"revokeDevice"`
}{})
status, _ := exchangeEnrollmentToken(t, enrolled.EnrollDevice.EnrollmentToken)
require.Equal(t, http.StatusUnauthorized, status)
})
t.Run("revoked device API key is rejected on heartbeat", func(t *testing.T) {
t.Parallel()
owner, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollDevice(t, employee, orgID)
status, payload := exchangeEnrollmentToken(t, enrolled.EnrollDevice.EnrollmentToken)
require.Equal(t, http.StatusOK, status)
require.NotEmpty(t, payload.APIKey)
deviceID := enrolled.EnrollDevice.Device.ID
require.Equal(t, http.StatusOK, sendHeartbeat(t, payload.APIKey, deviceID+"-hw"))
owner.MustExecute(revokeDeviceMutation, map[string]any{
"input": map[string]any{
"deviceId": deviceID,
},
}, &struct {
RevokeDevice struct {
Device struct {
State string `json:"state"`
} `json:"device"`
} `json:"revokeDevice"`
}{})
require.Equal(
t,
http.StatusUnauthorized,
sendHeartbeat(t, payload.APIKey, deviceID+"-hw"),
)
})
t.Run("re-enrollment succeeds after revoke with same hardware UUID", func(t *testing.T) {
t.Parallel()
owner, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollAndActivateDevice(t, employee, orgID)
hardwareUUID := enrolled.EnrollDevice.Device.ID + "-hw"
owner.MustExecute(revokeDeviceMutation, map[string]any{
"input": map[string]any{
"deviceId": enrolled.EnrollDevice.Device.ID,
},
}, &struct {
RevokeDevice struct {
Device struct {
State string `json:"state"`
} `json:"device"`
} `json:"revokeDevice"`
}{})
reEnrolled := enrollDevice(t, employee, orgID)
activateEnrolledDevice(t, reEnrolled.EnrollDevice.EnrollmentToken, hardwareUUID)
var result struct {
Node struct {
ID string `json:"id"`
State string `json:"state"`
} `json:"node"`
}
employee.MustExecute(getDeviceQuery, map[string]any{"id": reEnrolled.EnrollDevice.Device.ID}, &result)
require.Equal(t, reEnrolled.EnrollDevice.Device.ID, result.Node.ID)
require.Equal(t, "ACTIVE", result.Node.State)
})
t.Run("owner can enroll device", func(t *testing.T) {
t.Parallel()
owner, _, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrollDevice(t, owner, orgID)
})
t.Run("admin can enroll device", func(t *testing.T) {
t.Parallel()
_, admin, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrollDevice(t, admin, orgID)
})
t.Run("employee can enroll device", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrollDevice(t, employee, orgID)
})
t.Run("employee permission gate", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
var result struct {
Node struct {
CanEnrollDevice bool `json:"canEnrollDevice"`
} `json:"node"`
}
employee.MustExecute(devicePermissionQuery, map[string]any{"orgId": orgID}, &result)
require.True(t, result.Node.CanEnrollDevice)
})
t.Run("employee can read own device", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollDevice(t, employee, orgID)
var result struct {
Node struct {
ID string `json:"id"`
State string `json:"state"`
} `json:"node"`
}
employee.MustExecute(getDeviceQuery, map[string]any{"id": enrolled.EnrollDevice.Device.ID}, &result)
require.Equal(t, enrolled.EnrollDevice.Device.ID, result.Node.ID)
require.Equal(t, "PENDING", result.Node.State)
})
t.Run("employee cannot list org devices", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
_, err := employee.Do(listDevicesQuery, map[string]any{"orgId": orgID})
testutil.RequireForbiddenError(t, err, "employee should not list org devices")
})
t.Run("employee can list own enrolled devices", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollAndActivateDevice(t, employee, orgID)
var result struct {
Viewer struct {
EnrolledDevices struct {
Edges []struct {
Node struct {
ID string `json:"id"`
State string `json:"state"`
} `json:"node"`
} `json:"edges"`
} `json:"enrolledDevices"`
} `json:"viewer"`
}
employee.MustExecute(listEnrolledDevicesQuery, map[string]any{"orgId": orgID}, &result)
require.Len(t, result.Viewer.EnrolledDevices.Edges, 1)
require.Equal(t, enrolled.EnrollDevice.Device.ID, result.Viewer.EnrolledDevices.Edges[0].Node.ID)
require.Equal(t, "ACTIVE", result.Viewer.EnrolledDevices.Edges[0].Node.State)
})
t.Run("employee enrolled devices exclude pending devices", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollDevice(t, employee, orgID)
ids := listEnrolledDeviceIDs(t, employee, orgID)
require.NotContains(t, ids, enrolled.EnrollDevice.Device.ID)
})
t.Run("employee only sees own enrolled devices", func(t *testing.T) {
t.Parallel()
owner, _, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
employeeA := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
employeeB := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
employeeBID := employeeB.GetProfileID().String()
enrolledA := enrollAndActivateDevice(t, employeeA, orgID)
createdB := createDevice(t, owner, orgID, &employeeBID)
activateEnrolledDevice(
t,
createdB.CreateDevice.EnrollmentToken,
createdB.CreateDevice.Device.ID+"-hw",
)
var result struct {
Viewer struct {
EnrolledDevices struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"enrolledDevices"`
} `json:"viewer"`
}
employeeA.MustExecute(listEnrolledDevicesQuery, map[string]any{"orgId": orgID}, &result)
require.Len(t, result.Viewer.EnrolledDevices.Edges, 1)
require.Equal(t, enrolledA.EnrollDevice.Device.ID, result.Viewer.EnrolledDevices.Edges[0].Node.ID)
})
t.Run("owner can list own enrolled devices", func(t *testing.T) {
t.Parallel()
owner, _, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollAndActivateDevice(t, owner, orgID)
ids := listEnrolledDeviceIDs(t, owner, orgID)
require.Contains(t, ids, enrolled.EnrollDevice.Device.ID)
})
t.Run("admin can list own enrolled devices", func(t *testing.T) {
t.Parallel()
_, admin, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollAndActivateDevice(t, admin, orgID)
ids := listEnrolledDeviceIDs(t, admin, orgID)
require.Contains(t, ids, enrolled.EnrollDevice.Device.ID)
})
t.Run("owner only sees own enrolled devices", func(t *testing.T) {
t.Parallel()
owner, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolledOwner := enrollAndActivateDevice(t, owner, orgID)
enrollDevice(t, employee, orgID)
ids := listEnrolledDeviceIDs(t, owner, orgID)
require.Len(t, ids, 1)
require.Equal(t, enrolledOwner.EnrollDevice.Device.ID, ids[0])
})
t.Run("employee sees device when owner was set with profile id", func(t *testing.T) {
t.Parallel()
owner, _, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
employeeA := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
profileID := employeeA.GetProfileID().String()
created := createDevice(t, owner, orgID, &profileID)
activateEnrolledDevice(
t,
created.CreateDevice.EnrollmentToken,
created.CreateDevice.Device.ID+"-hw",
)
ids := listEnrolledDeviceIDs(t, employeeA, orgID)
require.Contains(t, ids, created.CreateDevice.Device.ID)
var deviceResult struct {
Node struct {
Owner *struct {
ID string `json:"id"`
FullName string `json:"fullName"`
} `json:"owner"`
} `json:"node"`
}
owner.MustExecute(
getDeviceQuery,
map[string]any{"id": created.CreateDevice.Device.ID},
&deviceResult,
)
require.NotNil(t, deviceResult.Node.Owner)
require.Equal(t, profileID, deviceResult.Node.Owner.ID)
require.NotEmpty(t, deviceResult.Node.Owner.FullName)
})
t.Run("employee cannot revoke device", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollDevice(t, employee, orgID)
_, err := employee.Do(revokeDeviceMutation, map[string]any{
"input": map[string]any{
"deviceId": enrolled.EnrollDevice.Device.ID,
},
})
testutil.RequireForbiddenError(t, err, "employee should not revoke devices")
})
t.Run("employee cannot create device for another user", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, ownerProfileID := setupDeviceEnrollmentClients(t)
_, err := employee.Do(createDeviceMutation, map[string]any{
"input": map[string]any{
"organizationId": orgID,
"ownerId": ownerProfileID,
},
})
testutil.RequireForbiddenError(t, err, "employee should not create device for another user")
})
t.Run("viewer cannot enroll device", func(t *testing.T) {
t.Parallel()
_, _, _, viewer, orgID, _ := setupDeviceEnrollmentClients(t)
_, err := viewer.Do(enrollDeviceMutation, map[string]any{
"input": map[string]any{
"organizationId": orgID,
},
})
testutil.RequireForbiddenError(t, err, "viewer should not enroll devices")
})
t.Run("unassumed session can poll own enrolledDevice", func(t *testing.T) {
t.Parallel()
_, _, employee, _, orgID, _ := setupDeviceEnrollmentClients(t)
enrolled := enrollAndActivateDevice(t, employee, orgID)
deviceID := enrolled.EnrollDevice.Device.ID
unassumed := testutil.NewClientWithNewSession(t, employee)
_, err := unassumed.Do(getDeviceQuery, map[string]any{"id": deviceID})
testutil.RequireErrorCode(t, err, "ASSUMPTION_REQUIRED", "node get requires assumption")
var result struct {
Viewer struct {
EnrolledDevice struct {
ID string `json:"id"`
State string `json:"state"`
} `json:"enrolledDevice"`
} `json:"viewer"`
}
unassumed.MustExecute(getEnrolledDeviceQuery, map[string]any{"id": deviceID}, &result)
require.Equal(t, deviceID, result.Viewer.EnrolledDevice.ID)
require.Equal(t, "ACTIVE", result.Viewer.EnrolledDevice.State)
})
t.Run("unassumed session cannot read another users enrolledDevice", func(t *testing.T) {
t.Parallel()
owner, _, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
employeeA := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
employeeB := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
enrolledB := enrollDevice(t, employeeB, orgID)
unassumedA := testutil.NewClientWithNewSession(t, employeeA)
_, err := unassumedA.Do(getEnrolledDeviceQuery, map[string]any{
"id": enrolledB.EnrollDevice.Device.ID,
})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "employee cannot read another users enrolledDevice")
})
t.Run("enrolledDevice does not disclose foreign org device existence", func(t *testing.T) {
t.Parallel()
_, _, employeeA, _, _, _ := setupDeviceEnrollmentClients(t)
_, _, employeeB, _, orgBID, _ := setupDeviceEnrollmentClients(t)
enrolledB := enrollDevice(t, employeeB, orgBID)
unassumedA := testutil.NewClientWithNewSession(t, employeeA)
_, err := unassumedA.Do(getEnrolledDeviceQuery, map[string]any{
"id": enrolledB.EnrollDevice.Device.ID,
})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "foreign org enrolledDevice must look like not found")
unknownID := gid.New(employeeA.GetOrganizationID().TenantID(), coredata.DeviceEntityType).String()
_, err = unassumedA.Do(getEnrolledDeviceQuery, map[string]any{
"id": unknownID,
})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "unknown enrolledDevice must look like not found")
})
t.Run("owner retains admin access", func(t *testing.T) {
t.Parallel()
owner, _, _, _, orgID, _ := setupDeviceEnrollmentClients(t)
created := createDevice(t, owner, orgID, nil)
var deviceResult struct {
Node struct {
Owner *struct {
FullName string `json:"fullName"`
} `json:"owner"`
} `json:"node"`
}
owner.MustExecute(
getDeviceQuery,
map[string]any{"id": created.CreateDevice.Device.ID},
&deviceResult,
)
require.Nil(t, deviceResult.Node.Owner)
var listResult struct {
Node struct {
Devices struct {
TotalCount int `json:"totalCount"`
} `json:"devices"`
} `json:"node"`
}
owner.MustExecute(listDevicesQuery, map[string]any{"orgId": orgID}, &listResult)
require.GreaterOrEqual(t, listResult.Node.Devices.TotalCount, 1)
var revokeResult struct {
RevokeDevice struct {
Device struct {
State string `json:"state"`
} `json:"device"`
} `json:"revokeDevice"`
}
owner.MustExecute(revokeDeviceMutation, map[string]any{
"input": map[string]any{
"deviceId": created.CreateDevice.Device.ID,
},
}, &revokeResult)
require.Equal(t, "REVOKED", revokeResult.RevokeDevice.Device.State)
})
}
func TestDeviceEnrollmentPermissionQueryShape(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
admin := testutil.NewClientInOrg(t, testutil.RoleAdmin, owner)
orgID := owner.GetOrganizationID().String()
for _, tc := range []struct {
name string
client *testutil.Client
}{
{name: "owner", client: owner},
{name: "admin", client: admin},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
resp, err := tc.client.Do(devicePermissionQuery, map[string]any{"orgId": orgID})
require.NoError(t, err)
var result struct {
Node struct {
CanEnrollDevice bool `json:"canEnrollDevice"`
} `json:"node"`
}
require.NoError(t, json.Unmarshal(resp.Data, &result))
require.True(t, result.Node.CanEnrollDevice)
})
}
}