github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/device_test.go (about) 1 // Copyright 2023 Gravitational, Inc 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package types 16 17 import ( 18 "crypto" 19 "testing" 20 "time" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 "google.golang.org/protobuf/testing/protocmp" 26 "google.golang.org/protobuf/types/known/timestamppb" 27 28 devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" 29 ) 30 31 func TestDeviceConversions_toAndFrom(t *testing.T) { 32 t1 := time.UnixMilli(1680276526972000) // Fri Mar 31 2023 15:28:46 UTC 33 t11 := t1.Add(100 * time.Millisecond) 34 t2 := t1.Add(1 * time.Second) 35 t22 := t1.Add(100 * time.Millisecond) 36 37 const osType = devicepb.OSType_OS_TYPE_MACOS 38 const assetTag = "llama14" 39 dev := &devicepb.Device{ 40 ApiVersion: "v1", 41 Id: "0af7c335-5f2c-4756-8266-9965a47ccbd3", 42 OsType: osType, 43 AssetTag: assetTag, 44 CreateTime: timestamppb.New(t1), 45 UpdateTime: timestamppb.New(t2), 46 EnrollStatus: devicepb.DeviceEnrollStatus_DEVICE_ENROLL_STATUS_ENROLLED, 47 Credential: &devicepb.DeviceCredential{ 48 Id: "557762f0-4cd4-4b75-aaee-575c57237c0b", 49 PublicKeyDer: []byte("insert public key here"), 50 DeviceAttestationType: devicepb.DeviceAttestationType_DEVICE_ATTESTATION_TYPE_UNSPECIFIED, 51 TpmEkcertSerial: "00:00:00:00:00:00:00:00:00:00:00:DE:AD:BE:EF:CA:FE", 52 TpmAkPublic: []byte("a TPMT_PUBLIC encoded blob"), 53 }, 54 CollectedData: []*devicepb.DeviceCollectedData{ 55 { 56 CollectTime: timestamppb.New(t1), 57 RecordTime: timestamppb.New(t11), 58 OsType: osType, 59 SerialNumber: assetTag, 60 }, 61 { 62 CollectTime: timestamppb.New(t2), 63 RecordTime: timestamppb.New(t22), 64 OsType: osType, 65 SerialNumber: assetTag, 66 ModelIdentifier: "MacBookPro9,2", 67 OsVersion: "13.1.2", 68 OsBuild: "22D68", 69 OsUsername: "llama", 70 JamfBinaryVersion: "9.27", 71 MacosEnrollmentProfiles: "Enrolled via DEP: No\nMDM enrollment: Yes (User Approved)\nMDM server: ...", 72 ReportedAssetTag: assetTag + "-reported", 73 SystemSerialNumber: assetTag + "-system", 74 BaseBoardSerialNumber: assetTag + "-board", 75 TpmPlatformAttestation: &devicepb.TPMPlatformAttestation{ 76 Nonce: []byte("foo-bar-bizz"), 77 PlatformParameters: &devicepb.TPMPlatformParameters{ 78 EventLog: []byte("dummy-event-log"), 79 Quotes: []*devicepb.TPMQuote{ 80 { 81 Quote: []byte("fake-quote-1"), 82 Signature: []byte("fake-signature-1"), 83 }, 84 { 85 Quote: []byte("fake-quote-2"), 86 Signature: []byte("fake-signature-2"), 87 }, 88 }, 89 Pcrs: []*devicepb.TPMPCR{ 90 { 91 Index: 0, 92 Digest: []byte("fake-sha1-digest"), 93 DigestAlg: uint64(crypto.SHA1), 94 }, 95 { 96 Index: 1, 97 Digest: []byte("fake-sha256-digest"), 98 DigestAlg: uint64(crypto.SHA256), 99 }, 100 }, 101 }, 102 }, 103 OsId: "macOS", // Made up, only set for Linux. 104 }, 105 }, 106 Source: &devicepb.DeviceSource{ 107 Name: "myscript", 108 Origin: devicepb.DeviceOrigin_DEVICE_ORIGIN_API, 109 }, 110 Profile: &devicepb.DeviceProfile{ 111 UpdateTime: timestamppb.New(t1), 112 ModelIdentifier: "MacBookPro9,2", 113 OsVersion: "13.1.2", 114 OsBuild: "22F82", 115 OsBuildSupplemental: "22F770820d", 116 OsUsernames: []string{"admin", "llama"}, 117 JamfBinaryVersion: "9.27", 118 ExternalId: "99", 119 OsId: "macOS", // Made up, only set for Linux. 120 }, 121 Owner: "llama", 122 } 123 124 gotRes := DeviceToResource(dev) 125 // Assert some of the more "unusual" or missing fields. 126 // We know other information isn't lost because of the conversion below, 127 // therefore it must be present in the resource. 128 assert.Equal(t, dev.ApiVersion, gotRes.Version, "resource.Version is not the ApiVersion") 129 assert.Equal(t, dev.Id, gotRes.Metadata.Name, "resource.Metadata.Name is not the Id") 130 assert.NotEmpty(t, gotRes.Metadata.Namespace, "resource.Metadata.Namespace") 131 132 gotDev, err := DeviceFromResource(gotRes) 133 require.NoError(t, err, "DeviceFromResource failed") 134 if diff := cmp.Diff(dev, gotDev, protocmp.Transform()); diff != "" { 135 t.Errorf("DeviceFromResource mismatch (-want +got)\n%s", diff) 136 } 137 } 138 139 func TestResourceAttestationType_toAndFrom(t *testing.T) { 140 t.Parallel() 141 tests := []struct { 142 attestationType string 143 wantEmpty bool 144 wantErr string 145 }{ 146 { 147 attestationType: "unspecified", 148 wantEmpty: true, 149 }, 150 { 151 attestationType: "tpm_ekpub", 152 }, 153 { 154 attestationType: "tpm_ekcert", 155 }, 156 { 157 attestationType: "tpm_ekcert_trusted", 158 }, 159 { 160 attestationType: "quantum_entanglement", 161 wantErr: "unknown attestation type", 162 }, 163 } 164 for _, tt := range tests { 165 t.Run(tt.attestationType, func(t *testing.T) { 166 asEnum, err := ResourceDeviceAttestationTypeFromString(tt.attestationType) 167 if tt.wantErr != "" { 168 require.ErrorContains(t, err, tt.wantErr, "ResourceDeviceAttestationTypeFromString error mismatch") 169 return 170 } 171 172 got := ResourceDeviceAttestationTypeToString(asEnum) 173 want := tt.attestationType 174 if tt.wantEmpty { 175 want = "" 176 } 177 require.Equal(t, want, got, "ResourceDeviceAttestationTypeToString mismatch") 178 }) 179 } 180 } 181 182 func TestAllDeviceEnumsMapped(t *testing.T) { 183 tests := []struct { 184 name string 185 nameMap map[int32]string // a proto enum "name" map, like MyEnum_name. 186 toString func(i int32) string 187 fromString func(s string) (int32, error) 188 }{ 189 { 190 name: "OSType", 191 nameMap: devicepb.OSType_name, 192 toString: func(i int32) string { 193 return ResourceOSTypeToString(devicepb.OSType(i)) 194 }, 195 fromString: func(s string) (int32, error) { 196 val, err := ResourceOSTypeFromString(s) 197 return int32(val), err 198 }, 199 }, 200 { 201 name: "DeviceEnrollStatus", 202 nameMap: devicepb.DeviceEnrollStatus_name, 203 toString: func(i int32) string { 204 return ResourceDeviceEnrollStatusToString(devicepb.DeviceEnrollStatus(i)) 205 }, 206 fromString: func(s string) (int32, error) { 207 val, err := ResourceDeviceEnrollStatusFromString(s) 208 return int32(val), err 209 }, 210 }, 211 { 212 name: "DeviceAttestationType", 213 nameMap: devicepb.DeviceAttestationType_name, 214 toString: func(i int32) string { 215 return ResourceDeviceAttestationTypeToString(devicepb.DeviceAttestationType(i)) 216 }, 217 fromString: func(s string) (int32, error) { 218 val, err := ResourceDeviceAttestationTypeFromString(s) 219 return int32(val), err 220 }, 221 }, 222 { 223 name: "DeviceOrigin", 224 nameMap: devicepb.DeviceOrigin_name, 225 toString: func(i int32) string { 226 return ResourceDeviceOriginToString(devicepb.DeviceOrigin(i)) 227 }, 228 fromString: func(s string) (int32, error) { 229 val, err := ResourceDeviceOriginFromString(s) 230 return int32(val), err 231 }, 232 }, 233 } 234 for _, test := range tests { 235 t.Run(test.name, func(t *testing.T) { 236 for num, name := range test.nameMap { 237 t.Run(name, func(t *testing.T) { 238 s := test.toString(num) 239 gotNum, err := test.fromString(s) 240 require.NoError(t, err, "to/from enum conversion failed") 241 require.Equal(t, num, gotNum, "to/from enum conversion changed the enum value") 242 }) 243 } 244 245 t.Run(`from "" (empty string)`, func(t *testing.T) { 246 got, err := test.fromString("") 247 require.NoError(t, err, `conversion from "" failed`) 248 require.Equal(t, int32(0), got, `conversion from "" returned a non-zero value`) 249 }) 250 }) 251 } 252 }