From 1b3e0e090d7a4faa4b2e432a84d2eca04dbe2e95 Mon Sep 17 00:00:00 2001 From: mosen Date: Thu, 6 Oct 2016 02:20:50 +1100 Subject: [PATCH] Complete listing of MDM Commands as structs (#6) * Add struct for CertificateList response. * Add structs for all possible MDM command request types. Add definitions for each setting which may be changed by the Settings command. Add some error enums. * Move response fixtures to their own file. Add very basic tests for commands. Add prefix to each error type to stop name collision Add more response types. Fix response tests to use new fixture location. * Further simple tests for MDM commands. * Add plist responses based on real world responses to use as test fixtures. Support InstalledApplicationList command. Add struct for Error response. * Add InstalledApplicationList response item struct. * Remove duplicate definition of InstalledApplicationListItem * Add simple test for error chain. Add ErrorChain to response struct. * Add example response for InstalledApplicationList * Add test for InstalledApplicationListResponse * int field did not have enough storage for some installed apps, used uint32. --- command.go | 175 ++- command_test.go | 116 ++ errors.go | 177 +++ response.go | 76 +- response_test.go | 1092 ++--------------- settings.go | 52 + test/responses.go | 1018 +++++++++++++++ test/responses/authenticate.plist | 20 + test/responses/certificatelist.plist | 45 + test/responses/error_invalidreq.plist | 25 + .../installed_application_list.plist | 47 + test/responses/securityinfo.plist | 27 + test/responses/tokenupdate.plist | 24 + 13 files changed, 1850 insertions(+), 1044 deletions(-) create mode 100644 command_test.go create mode 100644 errors.go create mode 100644 settings.go create mode 100644 test/responses.go create mode 100644 test/responses/authenticate.plist create mode 100644 test/responses/certificatelist.plist create mode 100644 test/responses/error_invalidreq.plist create mode 100644 test/responses/installed_application_list.plist create mode 100644 test/responses/securityinfo.plist create mode 100644 test/responses/tokenupdate.plist diff --git a/command.go b/command.go index 6e93f90..ab085c3 100644 --- a/command.go +++ b/command.go @@ -17,6 +17,7 @@ type CommandRequest struct { AccountConfiguration ScheduleOSUpdateScan InstallProfile + InstalledApplicationList } // Payload is an MDM payload @@ -30,30 +31,154 @@ type command struct { DeviceInformation InstallApplication InstallProfile + InstalledApplicationList AccountConfiguration ScheduleOSUpdateScan } -// InstallApplication is an InstallApplication MDM Command -type InstallApplication struct { - ITunesStoreID int `plist:"iTunesStoreID,omitempty" json:"itunes_store_id,omitempty"` - Identifier string `plist:",omitempty" json:"identifier,omitempty"` - ManifestURL string `plist:",omitempty" json:"manifest_url,omitempty"` - ManagementFlags int `plist:",omitempty" json:"management_flags,omitempty"` - NotManaged bool `plist:",omitempty" json:"not_managed,omitempty"` - // TODO: add remaining optional fields -} +// The following commands are in the order provided by the apple documentation. // InstallProfile is an InstallProfile MDM Command type InstallProfile struct { Payload []byte `plist:",omitempty" json:"payload,omitempty"` } +type RemoveProfile struct { + Identifier string `plist:",omitempty" json:"identifier,omitempty"` +} + +type InstallProvisioningProfile struct { + ProvisioningProfile []byte `plist:",omitempty" json:"provisioning_profile,omitempty"` +} + +type RemoveProvisioningProfile struct { + UUID string `plist:",omitempty" json:"uuid,omitempty"` +} + +type InstalledApplicationList struct { + Identifiers []string `plist:",omitempty" json:"identifiers,omitempty"` + ManagedAppsOnly bool `plist:",omitempty" json:"managed_apps_only,omitempty"` +} + // DeviceInformation is a DeviceInformation MDM Command type DeviceInformation struct { Queries []string `plist:",omitempty" json:"queries,omitempty"` } +type DeviceLock struct { + PIN string `json:"pin,omitempty"` + Message string `plist:",omitempty" json:"message,omitempty"` + PhoneNumber string `plist:",omitempty" json:"phone_number,omitempty"` +} + +type ClearPasscode struct { + UnlockToken []byte `plist:",omitempty" json:"unlock_token,omitempty"` +} + +type EraseDevice struct { + PIN string `plist:",omitempty" json:"pin,omitempty"` +} + +type RequestMirroring struct { + DestinationName string `plist:",omitempty" json:"destination_name,omitempty"` + DestinationDeviceID string `plist:",omitempty" json:"destination_device_id,omitempty"` + ScanTime string `plist:",omitempty" json:"scan_time,omitempty"` + Password string `plist:",omitempty" json:"password,omitempty"` +} + +type Restrictions struct { + ProfileRestrictions bool `plist:",omitempty" json:"restrictions,omitempty"` +} + +type DeleteUser struct { + UserName string `plist:",omitempty" json:"user_name,omitempty"` + ForceDeletion bool `plist:",omitempty" json:"force_deletion,omitempty"` +} + +type EnableLostMode struct { + Message string `plist:",omitempty" json:"message,omitempty"` + PhoneNumber string `plist:",omitempty" json:"phone_number,omitempty"` + Footnote string `plist:",omitempty" json:"footnote,omitempty"` +} + +// InstallApplication is an InstallApplication MDM Command +type InstallApplication struct { + ITunesStoreID int `plist:"iTunesStoreID,omitempty" json:"itunes_store_id,omitempty"` + Identifier string `plist:",omitempty" json:"identifier,omitempty"` + ManifestURL string `plist:",omitempty" json:"manifest_url,omitempty"` + ManagementFlags int `plist:",omitempty" json:"management_flags,omitempty"` + NotManaged bool `plist:",omitempty" json:"not_managed,omitempty"` + ChangeManagementState string `plist:",omitempty" json:"change_management_state,omitempty"` + Options InstallApplicationOptions `plist:",omitempty" json:"options,omitempty"` + // TODO: add remaining optional fields +} + +type InstallApplicationConfiguration struct { + // TODO: managed app config +} + +type InstallApplicationOptions struct { + NotManaged bool `plist:",omitempty" json:"not_managed,omitempty"` + PurchaseMethod int `plist:",omitempty" json:"purchase_method,omitempty"` +} + +type ApplyRedemptionCode struct { + Identifier string `plist:",omitempty" json:"identifier,omitempty"` + RedemptionCode string `plist:",omitempty" json:"redemption_code,omitempty"` +} + +type ManagedApplicationList struct { + Identifiers []string `plist:",omitempty" json:"identifiers,omitempty"` +} + +type RemoveApplication struct { + Identifier string `json:"identifier,omitempty"` +} + +type InviteToProgram struct { + ProgramID string `json:"program_id,omitempty"` + InvitationURL string `json:"invitation_url,omitempty"` +} + +type ValidateApplications struct { + Identifiers []string `plist:",omitempty" json:"identifiers,omitempty"` +} + +type InstallMedia struct { + ITunesStoreID int `plist:"iTunesStoreID,omitempty" json:"itunes_store_id,omitempty"` + MediaURL string `plist:",omitempty" json:"media_url,omitempty"` + MediaType string `json:"media_type"` + // TODO: media url fields +} + +type RemoveMedia struct { + MediaType string `json:"media_type"` + ITunesStoreID int `plist:"iTunesStoreID,omitempty" json:"itunes_store_id,omitempty"` + PersistentID string `plist:",omitempty" json:"persistent_id,omitempty"` +} + +type Settings struct { + Settings []Setting `json:"settings"` +} + +type ManagedApplicationConfiguration struct { + Identifiers []string `plist:",omitempty" json:"identifiers,omitempty"` +} + +type ApplicationConfiguration struct { + Identifier string `json:"identifier,omitempty"` + Configuration map[string]string `plist:",omitempty" json:"configuration,omitempty"` // TODO: string map is temporary +} + +type ManagedApplicationAttributes struct { + Identifiers []string `plist:",omitempty" json:"identifiers,omitempty"` +} + +type ManagedApplicationFeedback struct { + Identifiers []string `plist:",omitempty" json:"identifiers,omitempty"` + DeleteFeedback bool `plist:",omitempty" json:"delete_feedback,omitempty"` +} + // AccountConfiguration is an MDM command to create a primary user on OS X // It allows skipping the UI to set up a user. type AccountConfiguration struct { @@ -62,11 +187,6 @@ type AccountConfiguration struct { AutoSetupAdminAccounts []AdminAccount `plist:",omitempty" json:"auto_setup_admin_accounts,omitempty"` } -// ScheduleOSUpdateScan schedules an OS SoftwareUpdate check -type ScheduleOSUpdateScan struct { - Force bool `plist:",omitempty" json:"force,omitempty"` -} - // AdminAccount is the configuration for the // Admin account created during Setup Assistant type AdminAccount struct { @@ -76,6 +196,21 @@ type AdminAccount struct { Hidden bool `plist:"hidden,omitempty" json:"hidden,omitempty"` } +type OSUpdate struct { + ProductKey string `json:"product_key"` + InstallAction string `json:"install_action"` +} + +// ScheduleOSUpdate runs update(s) immediately +type ScheduleOSUpdate struct { + Updates []OSUpdate `plist:",omitempty" json:"updates,omitempty"` +} + +// ScheduleOSUpdateScan schedules a (background) OS SoftwareUpdate check +type ScheduleOSUpdateScan struct { + Force bool `plist:",omitempty" json:"force,omitempty"` +} + type data []byte func newPayload(requestType string) *Payload { @@ -94,8 +229,16 @@ func NewPayload(request *CommandRequest) (*Payload, error) { case "ScheduleOSUpdateScan": payload.Command.ScheduleOSUpdateScan = request.ScheduleOSUpdateScan case "ProfileList", - "SecurityInfo", + "ProvisioningProfileList", "CertificateList", + "SecurityInfo", + "StopMirroring", + "ClearRestrictionsPassword", + "UsersList", + "LogOutUser", + "DisableLostMode", + "DeviceLocation", + "ManagedMediaList", "OSUpdateStatus", "DeviceConfigured", "AvailableOSUpdates": @@ -106,6 +249,8 @@ func NewPayload(request *CommandRequest) (*Payload, error) { payload.Command.InstallProfile = request.InstallProfile case "AccountConfiguration": payload.Command.AccountConfiguration = request.AccountConfiguration + case "InstalledApplicationList": + payload.Command.InstalledApplicationList = request.InstalledApplicationList default: return nil, fmt.Errorf("Unsupported MDM RequestType %v", requestType) } diff --git a/command_test.go b/command_test.go new file mode 100644 index 0000000..23c784e --- /dev/null +++ b/command_test.go @@ -0,0 +1,116 @@ +package mdm + +import ( + "encoding/json" + "fmt" + "github.com/groob/plist" + "testing" +) + +// Basic tests will attempt to marshal and unmarshal mdm command structures to identify any naming or tag errors. + +// Make sure a command can be marshalled to json +func testMarshalJSON(t *testing.T, cmd interface{}) { + jsonCmd, err := json.Marshal(cmd) + if err != nil { + t.Error(err) + } + fmt.Println(string(jsonCmd)) +} + +// Make sure a command can be marshalled to plist +func testMarshalPlist(t *testing.T, cmd interface{}) { + plistCmd, err := plist.MarshalIndent(cmd, "\t") + if err != nil { + t.Error(err) + } + fmt.Println(string(plistCmd)) +} + +func TestInstallProfile(t *testing.T) { + cmd := InstallProfile{Payload: []byte{00}} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestRemoveProfile(t *testing.T) { + cmd := RemoveProfile{Identifier: "io.micromdm.test.profile"} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestInstallProvisioningProfile(t *testing.T) { + cmd := InstallProvisioningProfile{ProvisioningProfile: []byte{00}} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestRemoveProvisioningProfile(t *testing.T) { + cmd := RemoveProvisioningProfile{UUID: "1111-2222-3333"} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestInstalledApplicationList(t *testing.T) { + cmd := InstalledApplicationList{Identifiers: []string{"io.micromdm.application"}, ManagedAppsOnly: true} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestDeviceInformation(t *testing.T) { + cmd := DeviceInformation{Queries: []string{"SerialNumber"}} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestDeviceLock(t *testing.T) { + cmd := DeviceLock{PIN: "123456", Message: "Locked", PhoneNumber: "123-4567"} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestClearPasscode(t *testing.T) { + cmd := ClearPasscode{UnlockToken: []byte{00}} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestEraseDevice(t *testing.T) { + cmd := EraseDevice{PIN: "123456"} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestRequestMirroring(t *testing.T) { + cmd := RequestMirroring{DestinationName: "Apple TV", ScanTime: "30", Password: "sekret"} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +//func TestRestrictions(t *testing.T) { +// cmd := Restrictions{} +//} + +func TestDeleteUser(t *testing.T) { + cmd := DeleteUser{UserName: "joe", ForceDeletion: false} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestEnableLostMode(t *testing.T) { + cmd := EnableLostMode{Message: "Lost!", PhoneNumber: "123-4567", Footnote: "This is lost"} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestInstallApplication(t *testing.T) { + cmd := InstallApplication{ITunesStoreID: 1234567} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} + +func TestApplyRedemptionCode(t *testing.T) { + cmd := ApplyRedemptionCode{Identifier: "id", RedemptionCode: "abcdefg"} + testMarshalJSON(t, cmd) + testMarshalPlist(t, cmd) +} diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..32af566 --- /dev/null +++ b/errors.go @@ -0,0 +1,177 @@ +package mdm + +type MCProfileErrorDomain int + +const ( + prMalformedProfile MCProfileErrorDomain = iota + 1000 + prUnsupportedProfileVersion + prMissingRequiredField + prBadDataTypeInField + prBadSignature + prEmptyProfile + prCannotDecrypt + prNonUniqueUUIDs + prNonUniquePayloadIdentifiers + prProfileInstallationFailure + prUnsupportedFieldValue +) + +type MCPayloadErrorDomain int + +const ( + plMalformedPayload MCPayloadErrorDomain = iota + 2000 + plUnsupportedPayloadVersion + plMissingRequiredField + plBadDataTypeInField + plUnsupportedFieldValue + plInternalError +) + +type MCRestrictionsErrorDomain int + +const ( + rsInconsistentRestrictionSense MCRestrictionsErrorDomain = iota + 3000 + rsInconsistentValueComparisonSense +) + +type MCInstallationErrorDomain int + +const ( + inCannotParseProfile MCInstallationErrorDomain = iota + 4000 + inInstallationFailure + inDuplicateUUID + inProfileNotQueued + inUserCancelled + inPasscodeNotCompliant + inProfileRemovalDateInPast + inUnrecognisedFileFormat + inMismatchedCertificates + inDeviceLocked + inUpdatedProfileWrongIdentifier + inFinalProfileNotConfiguration + inProfileNotUpdatable + inUpdateFailed + inNoDeviceIdentity + inReplacementNoMDMPayload + inInternalError + inMultipleHTTPProxyPayloads + inMultipleCellularPayloads + inMultipleApplockPayloads + inUIInstallProhibited + inProfileMustBeNonInteractive + inProfileMustBeInstalledByMDM + inUnnacceptablePayload + inProfileNotFound + inInvalidSupervision + inRemovalDateInPast + inProfileRequiresPasscodeChange + inMultipleHomeScreenPayloads + inMultipleNotificationPayloads + inUnacceptablePayloadMultiuser + inPayloadContainsSensitiveInfo +) + +type MCPasscodeErrorDomain int + +const ( + pcPasscodeTooShort MCPasscodeErrorDomain = iota + 5000 + pcTooFewUniqueChars + pcTooFewComplexChars + pcRepeatingChars + pcAscendingDescendingChars + pcRequiresNumber + pcRequiresAlpha + pcPasscodeExpired + pcPasscodeTooRecent + _ + pcDeviceLocked + pcWrongPasscode + _ + pcCannotClearPasscode + pcCannotSetPasscode + pcCannotSetGracePeriod + pcCannotSetFingerprintUnlock + pcCannotSetFingerprintPurchase + pcCannotSetMaxAttempts +) + +type MCKeychainErrorDomain int + +const ( + kcKeychainSystemError MCKeychainErrorDomain = iota + 6000 + kcEmptyString + kcCannotCreateQuery +) + +type MCEmailErrorDomain int + +const ( + emHostUnreachable MCEmailErrorDomain = iota + 7000 + emInvalidCredentials + emUnknownValidationError + emSMIMECertificateNotFound + emSMIMECertificateBad + emIMAPMisconfigured + emPOPMisconfigured + emSMTPMisconfigured +) + +type MCWebClipErrorDomain int + +const ( + wcCannotInstallWebClip MCWebClipErrorDomain = iota + 8000 +) + +type MCCertificateErrorDomain int + +const ( + ceInvalidPassword MCCertificateErrorDomain = iota + 9000 + ceTooManyCertificatesInPayload + ceCannotStoreCertificate + ceCannotStoreWAPIData + ceCannotStoreRootCertificate + ceCertificateMalformed + ceCertificateNotIdentity +) + +type MCDefaultsErrorDomain int + +const ( + deCannotInstallDefaults MCDefaultsErrorDomain = iota + 10000 + deInvalidSigner +) + +type MCAPNErrorDomain int + +const ( + apnCannotInstallAPN MCAPNErrorDomain = iota + 11000 + apnCustomAPNAlreadyInstalled +) + +type MCMDMErrorDomain int + +const ( + mdmInvalidAccessRights MCMDMErrorDomain = iota + 12000 + mdmMultipleMDMInstances + mdmCannotCheckIn + mdmInvalidChallengeResponse + mdmInvalidPushCertificate + mdmCannotFindCertificate + mdmRedirectRefused + mdmNotAuthorized + mdmMalformedRequest + mdmInvalidReplacementProfile + mdmInternalConsistencyError + mdmInvalidMDMConfiguration + mdmMDMReplacementMismatch + mdmProfileNotManaged + mdmProvisioningProfileNotManaged + mdmCannotGetPushToken + mdmMissingIdentity + mdmCannotCreateEscrowKeybag + mdmCannotCopyEscrowKeybagData + mdmCannotCopyEscrowSecret + mdmUnauthorizedByServer + mdmInvalidRequestType + mdmInvalidTopic +) diff --git a/response.go b/response.go index a16ceb5..a3124b7 100644 --- a/response.go +++ b/response.go @@ -4,15 +4,47 @@ import "time" // Response is an MDM Command Response type Response struct { - UDID string - UserID *string `json:"user_id,omitempty" plist:"UserID,omitempty"` - Status string - CommandUUID string - RequestType string `json:"request_type,omitempty" plist:",omitempty"` - QueryResponses QueryResponses `json:"query_responses,omitempty" plist:",omitempty"` - SecurityInfo SecurityInfo `json:"security_info,omitempty" plist:",omitempty"` + UDID string + UserID *string `json:"user_id,omitempty" plist:"UserID,omitempty"` + Status string + CommandUUID string + RequestType string `json:"request_type,omitempty" plist:",omitempty"` + ErrorChain []ErrorChainItem `json:"error_chain" plist:",omitempty"` + QueryResponses QueryResponses `json:"query_responses,omitempty" plist:",omitempty"` + SecurityInfo SecurityInfo `json:"security_info,omitempty" plist:",omitempty"` + CertificateList CertificateList `json:"certificate_list,omitempty" plist:",omitempty"` + InstalledApplicationList InstalledApplicationListResponse `json:"installed_application_list,omitempty" plist:",omitempty"` } +type ProvisioningProfileListItem struct { + Name string `plist:",omitempty" json:"name,omitempty"` + UUID string `plist:",omitempty" json:"uuid,omitempty"` + ExpiryDate time.Time `plist:",omitempty" json:"expiry_date,omitempty"` +} + +type ProvisioningProfileListResponse []ProvisioningProfileListItem + +type CertificateListItem struct { + CommonName string `json:"common_name"` + Data []byte `json:"data"` + IsIdentity bool `json:"is_identity"` +} + +// CertificateList is the CertificateList MDM Command Response +type CertificateList []CertificateListItem + +type InstalledApplicationListItem struct { + Identifier string `plist:",omitempty" json:"identifier,omitempty"` + Version string `plist:",omitempty" json:"version,omitempty"` + ShortVersion string `plist:",omitempty" json:"short_version,omitempty"` + Name string `json:"name,omitempty"` + BundleSize uint32 `plist:",omitempty" json:"bundle_size,omitempty"` + DynamicSize uint32 `plist:",omitempty" json:"dynamic_size,omitempty"` + IsValidated bool `plist:",omitempty" json:"is_validated,omitempty"` +} + +type InstalledApplicationListResponse []InstalledApplicationListItem + // CommonQueryResponses has a list of query responses common to all device types type CommonQueryResponses struct { UDID string `json:"udid"` @@ -121,3 +153,33 @@ type SecurityInfo struct { PasscodeCompliantWithProfiles bool `json:"passcode_compliant_with_profiles,omitempty"` PasscodePresent bool `json:"passcode_present,omitempty"` } + +type RequestMirroringResponse struct { + MirroringResult string `json:"mirroring_result,omitempty"` +} + +//type GlobalRestrictions struct { +// RestrictedBool map[string]bool `plist:"restrictedBool,omitempty" json:"restricted_bool,omitempty"` +// RestrictedValue map[string]int `plist:"restrictedValue,omitempty" json:"restricted_value,omitempty"` +// Intersection map[string]string `plist:"intersection,omitempty" json:"intersection,omitempty"` // TODO: not actually string values +// Union map[string]string `plist:"union,omitempty" json:"union,omitempty"` // TODO: not actually string values +//} + +type UsersListItem struct { + UserName string `json:"user_name,omitempty"` + HasDataToSync bool `json:"has_data_to_sync,omitempty"` + DataQuota int `json:"data_quota,omitempty"` + DataUsed int `json:"data_used,omitempty"` +} + +type UsersListResponse []UsersListItem + +// Represents a single error in the error chain response +type ErrorChainItem struct { + ErrorCode int `json:"error_code,omitempty"` + ErrorDomain string `json:"error_domain,omitempty"` + LocalizedDescription string `json:"localized_description,omitempty"` + USEnglishDescription string `json:"us_english_description,omitempty"` +} + +type ErrorChain []ErrorChainItem diff --git a/response_test.go b/response_test.go index ae8be1b..a8aedb0 100644 --- a/response_test.go +++ b/response_test.go @@ -1,1030 +1,16 @@ package mdm import ( + "fmt" "github.com/groob/plist" + "github.com/micromdm/mdm/test" + "io/ioutil" "testing" ) -var ( - macOSElCapQueryResponses = ` - - - - CommandUUID - bb8b2033-b81a-4927-8aa1-993d4ba2ade9 - QueryResponses - - ActiveManagedUsers - - 00000000-1111-2222-3333-444455556666 - - AvailableDeviceCapacity - 4.5032081604003906 - AwaitingConfiguration - - BluetoothMAC - de-ad-be-ef-ca-fe - BuildVersion - 15F34 - CurrentConsoleManagedUser - 00000000-1111-2222-3333-444455556666 - DeviceCapacity - 476.13906860351562 - DeviceName - micromdm-macos - HostName - micromdm-macos.local - Languages - - en - - LocalHostName - micromdm-macos - Locales - - en_AU - eu - hr_BA - en_CM - rw_RW - en_SZ - tk_Latn - he_IL - ar - uz_Arab - en_PN - as - en_NF - rwk_TZ - zh_Hant_TW - gsw_LI - th_TH - ta_IN - es_EA - fr_GF - ar_001 - en_RW - tr_TR - de_CH - ee_TG - en_NG - fr_TG - az - fr_SC - es_HN - en_AG - ru_KZ - gsw - dyo - so_ET - zh_Hant_MO - de_BE - km_KH - my_MM - mgh_MZ - ee_GH - es_EC - kw_GB - rm_CH - en_ME - nyn - mk_MK - bs_Cyrl_BA - ar_MR - en_BM - ms_Arab - en_AI - gl_ES - en_PR - ha_Latn_GH - ff_CM - ne_IN - or_IN - khq_ML - en_MG - pt_TL - en_LC - ta_SG - jmc_TZ - om_ET - lv_LV - es_US - en_PT - vai_Latn_LR - en_NL - iu_Cans_CA - cgg_UG - ta - en_MH - to_TO - zu_ZA - shi_Latn_MA - brx_IN - ar_KM - en_AL - te - chr_US - yo_BJ - fr_VU - pa - tg - ks_Arab - kea - ksh_DE - sw_CD - th - te_IN - fr_RE - ur_IN - yo_NG - ti - guz_KE - tk - kl_GL - ksf_CM - mua_CM - lag_TZ - lb - fr_TN - es_PA - pl_PL - to - hi_IN - dje_NE - es_GQ - kok_IN - pl - fr_GN - bem - ha - ckb - lg - tr - en_PW - en_NO - nyn_UG - sr_Latn_RS - gsw_FR - pa_Guru - he - sn_ZW - qu_BO - lu_CD - mgo_CM - ps_AF - en_BS - ug_Arab - da - ms_Latn_SG - ps - ln - pt - iu_Cans - hi - lo - ebu - de - gu_IN - seh - en_CX - en_ZM - tzm_Latn_MA - fr_HT - fr_GP - lt - lu - ln_CD - vai_Latn - el_GR - lv - en_KE - sbp - hr - en_CY - es_GT - twq_NE - zh_Hant_HK - kln_KE - fr_GQ - chr - hu - es_UY - fr_CA - en_NR - mer - shi - es_PE - fr_SN - bez - sw_TZ - wae_CH - kkj - hy - kk_Cyrl_KZ - en_CZ - teo_KE - teo - dz_BT - ar_JO - mer_KE - khq - ln_CF - nn_NO - en_MO - ar_TD - dz - ses - en_BW - en_AS - ar_IL - ms_Latn_BN - bo_CN - nnh - teo_UG - hy_AM - ln_CG - sr_Latn_BA - en_MP - ksb_TZ - ar_SA - smn_FI - ar_LY - en_AT - so_KE - fr_CD - af_NA - en_NU - es_PH - en_KI - en_JE - lkt - fa_IR - ky_Cyrl - uz_Latn_UZ - zh_Hans_CN - ewo_CM - fr_PF - ca_IT - en_BZ - ar_KW - pt_GW - fr_FR - am_ET - en_VC - fr_DJ - fr_CF - es_SV - en_MS - pt_ST - ar_SD - luy_KE - gd_GB - de_LI - fr_CG - ckb_IQ - zh_Hans_SG - en_MT - ewo - af_ZA - os_GE - om_KE - nl_SR - es_ES - es_DO - ar_IQ - fr_CH - nnh_CM - es_419 - en_MU - bm_Latn - en_US_POSIX - yav_CM - luo_KE - dua_CM - et_EE - en_IE - ak_GH - rwk - es_CL - kea_CV - fr_CI - ckb_IR - fr_BE - se - en_NZ - ky_Cyrl_KG - en_LR - en_KN - nb_SJ - sg - sr_Cyrl_RS - ru_RU - en_ZW - sv_AX - si - ga_IE - en_VG - ff_MR - sk - agq_CM - fr_BF - naq_NA - sl - en_MW - mr_IN - az_Latn - en_LS - de_AT - ka - sn - sr_Latn_ME - fr_NC - so - is_IS - twq - ig_NG - sq - fo_FO - sr - tzm - ga - om - en_LT - bas_CM - se_NO - ki - nl_BE - ar_QA - gd - sv - kk - sw - es_CO - az_Latn_AZ - rn_BI - or - kl - ca - en_VI - km - os - en_MY - kn - en_LU - fr_SY - ar_TN - en_JM - fr_PM - ko - fr_NE - fr_MA - gl - ru_MD - saq_KE - ks - fr_CM - lb_LU - gv_IM - fr_BI - en_LV - ks_Arab_IN - es_NI - en_GB - kw - nl_SX - dav_KE - tr_CY - ky - en_UG - nus_SD - en_TC - tzm_Latn - ar_EG - fr_BJ - gu - es_PR - fr_RW - sr_Cyrl_BA - gv - fr_MC - cs - bez_TZ - es_CR - asa_TZ - ar_EH - ms_Arab_BN - mn_Cyrl - sbp_TZ - en_IL - ha_Latn_NE - lt_LT - mfe - en_GD - cy - ca_FR - es_BO - fr_BL - bn_IN - uz_Cyrl_UZ - az_Cyrl - en_IM - sw_KE - en_SB - pa_Arab - ur_PK - haw_US - ar_SO - en_IN - ha_Latn - fil - fr_MF - en_WS - es_CU - ja_JP - fy_NL - en_SC - en_IO - pt_PT - en_HK - en_GG - fr_MG - de_LU - ms_Latn_MY - tg_Cyrl - en_SD - shi_Tfng - ln_AO - ug_Arab_CN - as_IN - en_GH - ro_RO - jgo_CM - dua - en_UM - en_SE - kn_IN - en_KY - vun_TZ - kln - en_GI - ca_ES - rof - pt_CV - kok - pt_BR - ar_DJ - yi_001 - fi_FI - tg_Cyrl_TJ - zh - es_PY - ar_SS - mua - sr_Cyrl_ME - vai_Vaii_LR - en_001 - nl_NL - en_TK - si_LK - en_SG - sv_SE - fr_DZ - ca_AD - pt_AO - vi - xog_UG - xog - en_IS - nb - seh_MZ - es_AR - sk_SK - en_SH - ti_ER - nd - az_Cyrl_AZ - zu - ne - nd_ZW - el_CY - en_IT - nl_BQ - da_GL - ja - rm - fr_ML - rn - en_VU - rof_TZ - ro - ebu_KE - ru_KG - en_SI - sg_CF - mfe_MU - nl - brx - bs_Latn - fa - zgh_MA - en_GM - shi_Latn - en_FI - nn - en_EE - ru - kam_KE - fur - vai_Vaii - ar_ER - ti_ET - rw - ff - luo - fa_AF - ha_Latn_NG - nl_CW - en_HR - en_FJ - fi - pt_MO - be - en_US - en_TO - en_SK - bg - ru_BY - it_IT - ml_IN - gsw_CH - qu_EC - fo - sv_FI - en_FK - nus - ta_LK - vun - sr_Latn - fr - en_SL - bm - ar_BH - guz - bn - bo - ar_SY - lo_LA - ne_NP - uz_Latn - be_BY - es_IC - sr_Latn_XK - ar_MA - pa_Guru_IN - br - luy - kde_TZ - bs - fy - fur_IT - hu_HU - ar_AE - en_HU - sah_RU - zh_Hans - en_FM - sq_AL - ko_KP - en_150 - en_DE - fr_MQ - en_CA - hsb_DE - en_TR - ro_MD - es_VE - fr_WF - mt_MT - kab - nmg_CM - en_GR - ru_UA - fr_MR - tk_Latn_TM - zh_Hans_MO - mn_Cyrl_MN - ff_GN - bs_Cyrl - sw_UG - ko_KR - en_DG - bo_IN - en_CC - shi_Tfng_MA - lag - it_SM - os_RU - en_TT - ms_Arab_MY - sq_MK - ms_Latn - bem_ZM - kde - ar_OM - cgg - bas - bm_Latn_ML - kam - es_MX - sah - wae - en_GU - zh_Hant - fr_MU - fr_KM - ar_LB - en_BA - en_TV - sr_Cyrl - dje - kab_DZ - fil_PH - se_SE - vai - hr_HR - bs_Latn_BA - nl_AW - dav - so_SO - ar_PS - en_FR - uz_Cyrl - ff_SN - en_BB - ki_KE - naq - en_SS - mg_MG - mas_KE - en_RO - en_PG - mgh - dyo_SN - mas - agq - bn_BD - haw - yi - nb_NO - da_DK - en_DK - saq - ug - cy_GB - fr_YT - jmc - ses_ML - en_PH - de_DE - ar_YE - yo - lkt_US - uz_Arab_AF - jgo - sl_SI - uk - en_CH - asa - lg_UG - qu_PE - mgo - id_ID - en_NA - en_GY - zgh - pt_MZ - fr_LU - kk_Cyrl - mas_TZ - en_DM - ta_MY - dsb - en_BE - mg - ur - fr_GA - ka_GE - nmg - en_TZ - eu_ES - ar_DZ - id - so_DJ - hsb - yav - mk - pa_Arab_PK - ml - en_ER - ig - se_FI - mn - ksb - uz - vi_VN - ii - qu - en_PK - ee - mr - ms - en_ES - sq_XK - it_CH - mt - en_CK - br_FR - sr_Cyrl_XK - ksf - en_SX - bg_BG - en_PL - af - el - cs_CZ - fr_TD - zh_Hans_HK - is - ksh - my - en - it - dsb_DE - ii_CN - smn - iu - eo - en_ZA - en_AD - ak - en_RU - kkj_CM - am - es - et - uk_UA - - Model - iMac17,1 - ModelName - iMac - OSUpdateSettings - - AutoCheckEnabled - - AutomaticAppInstallationEnabled - - AutomaticOSInstallationEnabled - - AutomaticSecurityUpdatesEnabled - - BackgroundDownloadEnabled - - CatalogURL - https://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz - IsDefaultCatalog - - PerformPeriodicCheck - - PreviousScanDate - 2016-06-08T03:50:31Z - PreviousScanResult - 0 - - OSVersion - 10.11.5 - ProductName - iMac17,1 - SerialNumber - C12A1234567L - UDID - 11111111-2222-3333-4444-555566667777 - WiFiMAC - dd:ee:aa:dd:be:ef - iTunesStoreAccountHash - aaaaaaaaAAAAAAAAaaaaAAAAAAA= - iTunesStoreAccountIsActive - - - RequestType - DeviceInformation - Status - Acknowledged - UDID - 11111111-2222-3333-4444-555566667777 - -` - - // IOS 8 Fixture is here because it contains different keys to an IOS 9 device. NOTE: WiFi no cellular. - ios8IpadQueryResponses = ` - - - - CommandUUID - f14b5d8b-e1ae-4a3a-b217-e060803db82b - QueryResponses - - AvailableDeviceCapacity - 3.9073715209960938 - BatteryLevel - 1 - BluetoothMAC - de-ad-be-ef-ca-fe - BuildVersion - 12H143 - CellularTechnology - 0 - DeviceCapacity - 26.629673004150391 - DeviceName - micromdm-ios-8-ipad - EASDeviceIdentifier - AAAAAAAAAAAAAAAAAAAAAAAAAA - IsActivationLockEnabled - - IsCloudBackupEnabled - - IsDeviceLocatorServiceEnabled - - IsDoNotDisturbInEffect - - IsSupervised - - Model - MD786X - ModelName - iPad - OSVersion - 8.4 - ProductName - iPad4,1 - SerialNumber - ABCDABCDAB00 - UDID - 1111111111111111111111111111111111111111 - WiFiMAC - dd:ee:aa:dd:be:e1 - iTunesStoreAccountHash - aaaaaaaaAAAAAAAAaaaaAAAAAAA= - iTunesStoreAccountIsActive - - - Status - Acknowledged - UDID - 1111111111111111111111111111111111111111 - -` - - ios8IphoneQueryResponses = ` - - - - CommandUUID - c3911e20-e407-4ef8-b919-e9e3bce35ac6 - QueryResponses - - AvailableDeviceCapacity - 24.264816284179688 - BatteryLevel - 0.54000002145767212 - BluetoothMAC - de-ad-be-ef-ca-fe - BuildVersion - 12F70 - CarrierSettingsVersion - 19.0 - CellularTechnology - 3 - CurrentMCC - 504 - CurrentMNC - 02 - DataRoamingEnabled - - DeviceCapacity - 55.89947509765625 - DeviceName - micromdm-iphone6-ios8 - EASDeviceIdentifier - AAAAAAAAAAAAAAAAAAAAAAAAAA - ICCID - 1111 2222 3333 4444 5555 - IMEI - 11 222222 333333 4 - IsActivationLockEnabled - - IsCloudBackupEnabled - - IsDeviceLocatorServiceEnabled - - IsDoNotDisturbInEffect - - IsRoaming - - IsSupervised - - MEID - 00000000000000 - Model - MG4F2X - ModelName - iPhone - ModemFirmwareVersion - 2.23.03 - OSVersion - 8.3 - PersonalHotspotEnabled - - PhoneNumber - +00111111111 - ProductName - iPhone7,2 - SIMCarrierNetwork - Tincan - SerialNumber - 000000000001 - SubscriberCarrierNetwork - Tincan - SubscriberMCC - 502 - SubscriberMNC - 02 - UDID - 1111111111111111111111111111111111111112 - WiFiMAC - dd:ee:aa:dd:be:e2 - iTunesStoreAccountHash - aaaaaaaaAAAAAAAAaaaaAAAAAAA= - iTunesStoreAccountIsActive - - - Status - Acknowledged - UDID - 1111111111111111111111111111111111111112 - -` - - macOSElCapSecurityInfoNoFDE = ` - - - - CommandUUID - 1111111111111111111111111111111111111111 - RequestType - SecurityInfo - SecurityInfo - - FDE_Enabled - - - Status - Acknowledged - UDID - 1111111111111111111111111111111111111111 - - - ` - - ios8IpadSecurityInfo = ` - - - - CommandUUID - 1111111111111111111111111111111111111112 - SecurityInfo - - HardwareEncryptionCaps - 3 - PasscodeCompliant - - PasscodeCompliantWithProfiles - - PasscodePresent - - - Status - Acknowledged - UDID - 1111111111111111111111111111111111111112 - -` -) - func TestQueryResponseMac(t *testing.T) { response := &Response{} - plistBuf := []byte(macOSElCapQueryResponses) + plistBuf := []byte(test.OSXElCapQueryResponses) err := plist.Unmarshal(plistBuf, response) if err != nil { @@ -1034,7 +20,7 @@ func TestQueryResponseMac(t *testing.T) { func TestQueryResponseIpadIOS8(t *testing.T) { response := &Response{} - plistBuf := []byte(ios8IpadQueryResponses) + plistBuf := []byte(test.IOS8IpadQueryResponses) err := plist.Unmarshal(plistBuf, response) if err != nil { @@ -1044,7 +30,7 @@ func TestQueryResponseIpadIOS8(t *testing.T) { func TestQueryResponseIphoneIOS8(t *testing.T) { response := &Response{} - plistBuf := []byte(ios8IphoneQueryResponses) + plistBuf := []byte(test.IOS8IphoneQueryResponses) err := plist.Unmarshal(plistBuf, response) if err != nil { @@ -1054,7 +40,7 @@ func TestQueryResponseIphoneIOS8(t *testing.T) { func TestSecurityInfoMac(t *testing.T) { response := &Response{} - plistBuf := []byte(macOSElCapSecurityInfoNoFDE) + plistBuf := []byte(test.OSXElCapSecurityInfoNoFDE) err := plist.Unmarshal(plistBuf, response) if err != nil { @@ -1064,10 +50,72 @@ func TestSecurityInfoMac(t *testing.T) { func TestSecurityInfoIpadIOS8(t *testing.T) { response := &Response{} - plistBuf := []byte(ios8IpadSecurityInfo) + plistBuf := []byte(test.IOS8IpadSecurityInfo) err := plist.Unmarshal(plistBuf, response) if err != nil { t.Fatal(err) } } + +func TestErrorChain(t *testing.T) { + errorResponseBody, err := ioutil.ReadFile("./test/responses/error_invalidreq.plist") + + if err != nil { + t.Fatal(err) + } + + response := &Response{} + if err := plist.Unmarshal(errorResponseBody, response); err != nil { + t.Fatal(err) + } + + if response.Status != "Error" { + t.Fatal("Response status was not `Error`.") + } + + if response.ErrorChain == nil { + t.Fatal("Response did not contain expected error chain struct") + } + + for _, v := range response.ErrorChain { + if v.ErrorCode == 0 { + t.Fatal("Error response did not contain ErrorCode") + } + + if v.ErrorDomain == "" { + t.Fatal("Error response did not contain ErrorDomain") + } + + if v.LocalizedDescription == "" { + t.Fatal("Error response did not contain localized description") + } + + if v.USEnglishDescription == "" { + t.Fatal("Error response did not contain english description") + } + } +} + +func TestInstalledApplicationListResponse(t *testing.T) { + appListResponseBody, err := ioutil.ReadFile("./test/responses/installed_application_list.plist") + + if err != nil { + t.Fatal(err) + } + + response := &Response{} + if err := plist.Unmarshal(appListResponseBody, response); err != nil { + t.Fatal(err) + } + + if response.Status != "Acknowledged" { + t.Fatal("Response status was not `Acknowledged`.") + } + + if response.InstalledApplicationList == nil { + t.Fatal("No installed application list in response") + } + + fmt.Printf("%v\n", response) +} diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..c6c9b2d --- /dev/null +++ b/settings.go @@ -0,0 +1,52 @@ +package mdm + +// All of these settings are changed by sending the `Settings` command. + +type Setting struct { + Item string `json:"item"` +} + +type VoiceRoamingSetting struct { + Setting + Enabled bool `json:"enabled"` +} + +type PersonalHotspotSetting struct { + Setting + Enabled bool `json:"enabled"` +} + +type WallpaperSetting struct { + Setting + Image []byte `json:"image"` + Where int `json:"where"` +} + +type DataRoamingSetting struct { + Setting + Enabled bool `json:"enabled"` +} + +type ApplicationAttributesSetting struct { + Setting + Identifier string `json:"identifier"` + Attributes map[string]string `plist:",omitempty" json:"attributes,omitempty"` +} + +type DeviceNameSetting struct { + Setting + DeviceName string `json:"device_name"` +} + +type MDMOptions struct { + ActivationLockAllowedWhileSupervised bool `json:"activation_lock_allowed_while_supervised"` +} + +type MDMOptionsSetting struct { + Setting + MDMOptions MDMOptions `json:"mdm_options"` +} + +type MaximumResidentUsersSetting struct { + MaximumResidentUsers int `json:"maximum_resident_users"` +} diff --git a/test/responses.go b/test/responses.go new file mode 100644 index 0000000..51dc6ed --- /dev/null +++ b/test/responses.go @@ -0,0 +1,1018 @@ +package test + +var ( + OSXElCapQueryResponses = ` + + + + CommandUUID + bb8b2033-b81a-4927-8aa1-993d4ba2ade9 + QueryResponses + + ActiveManagedUsers + + 00000000-1111-2222-3333-444455556666 + + AvailableDeviceCapacity + 4.5032081604003906 + AwaitingConfiguration + + BluetoothMAC + de-ad-be-ef-ca-fe + BuildVersion + 15F34 + CurrentConsoleManagedUser + 00000000-1111-2222-3333-444455556666 + DeviceCapacity + 476.13906860351562 + DeviceName + micromdm-macos + HostName + micromdm-macos.local + Languages + + en + + LocalHostName + micromdm-macos + Locales + + en_AU + eu + hr_BA + en_CM + rw_RW + en_SZ + tk_Latn + he_IL + ar + uz_Arab + en_PN + as + en_NF + rwk_TZ + zh_Hant_TW + gsw_LI + th_TH + ta_IN + es_EA + fr_GF + ar_001 + en_RW + tr_TR + de_CH + ee_TG + en_NG + fr_TG + az + fr_SC + es_HN + en_AG + ru_KZ + gsw + dyo + so_ET + zh_Hant_MO + de_BE + km_KH + my_MM + mgh_MZ + ee_GH + es_EC + kw_GB + rm_CH + en_ME + nyn + mk_MK + bs_Cyrl_BA + ar_MR + en_BM + ms_Arab + en_AI + gl_ES + en_PR + ha_Latn_GH + ff_CM + ne_IN + or_IN + khq_ML + en_MG + pt_TL + en_LC + ta_SG + jmc_TZ + om_ET + lv_LV + es_US + en_PT + vai_Latn_LR + en_NL + iu_Cans_CA + cgg_UG + ta + en_MH + to_TO + zu_ZA + shi_Latn_MA + brx_IN + ar_KM + en_AL + te + chr_US + yo_BJ + fr_VU + pa + tg + ks_Arab + kea + ksh_DE + sw_CD + th + te_IN + fr_RE + ur_IN + yo_NG + ti + guz_KE + tk + kl_GL + ksf_CM + mua_CM + lag_TZ + lb + fr_TN + es_PA + pl_PL + to + hi_IN + dje_NE + es_GQ + kok_IN + pl + fr_GN + bem + ha + ckb + lg + tr + en_PW + en_NO + nyn_UG + sr_Latn_RS + gsw_FR + pa_Guru + he + sn_ZW + qu_BO + lu_CD + mgo_CM + ps_AF + en_BS + ug_Arab + da + ms_Latn_SG + ps + ln + pt + iu_Cans + hi + lo + ebu + de + gu_IN + seh + en_CX + en_ZM + tzm_Latn_MA + fr_HT + fr_GP + lt + lu + ln_CD + vai_Latn + el_GR + lv + en_KE + sbp + hr + en_CY + es_GT + twq_NE + zh_Hant_HK + kln_KE + fr_GQ + chr + hu + es_UY + fr_CA + en_NR + mer + shi + es_PE + fr_SN + bez + sw_TZ + wae_CH + kkj + hy + kk_Cyrl_KZ + en_CZ + teo_KE + teo + dz_BT + ar_JO + mer_KE + khq + ln_CF + nn_NO + en_MO + ar_TD + dz + ses + en_BW + en_AS + ar_IL + ms_Latn_BN + bo_CN + nnh + teo_UG + hy_AM + ln_CG + sr_Latn_BA + en_MP + ksb_TZ + ar_SA + smn_FI + ar_LY + en_AT + so_KE + fr_CD + af_NA + en_NU + es_PH + en_KI + en_JE + lkt + fa_IR + ky_Cyrl + uz_Latn_UZ + zh_Hans_CN + ewo_CM + fr_PF + ca_IT + en_BZ + ar_KW + pt_GW + fr_FR + am_ET + en_VC + fr_DJ + fr_CF + es_SV + en_MS + pt_ST + ar_SD + luy_KE + gd_GB + de_LI + fr_CG + ckb_IQ + zh_Hans_SG + en_MT + ewo + af_ZA + os_GE + om_KE + nl_SR + es_ES + es_DO + ar_IQ + fr_CH + nnh_CM + es_419 + en_MU + bm_Latn + en_US_POSIX + yav_CM + luo_KE + dua_CM + et_EE + en_IE + ak_GH + rwk + es_CL + kea_CV + fr_CI + ckb_IR + fr_BE + se + en_NZ + ky_Cyrl_KG + en_LR + en_KN + nb_SJ + sg + sr_Cyrl_RS + ru_RU + en_ZW + sv_AX + si + ga_IE + en_VG + ff_MR + sk + agq_CM + fr_BF + naq_NA + sl + en_MW + mr_IN + az_Latn + en_LS + de_AT + ka + sn + sr_Latn_ME + fr_NC + so + is_IS + twq + ig_NG + sq + fo_FO + sr + tzm + ga + om + en_LT + bas_CM + se_NO + ki + nl_BE + ar_QA + gd + sv + kk + sw + es_CO + az_Latn_AZ + rn_BI + or + kl + ca + en_VI + km + os + en_MY + kn + en_LU + fr_SY + ar_TN + en_JM + fr_PM + ko + fr_NE + fr_MA + gl + ru_MD + saq_KE + ks + fr_CM + lb_LU + gv_IM + fr_BI + en_LV + ks_Arab_IN + es_NI + en_GB + kw + nl_SX + dav_KE + tr_CY + ky + en_UG + nus_SD + en_TC + tzm_Latn + ar_EG + fr_BJ + gu + es_PR + fr_RW + sr_Cyrl_BA + gv + fr_MC + cs + bez_TZ + es_CR + asa_TZ + ar_EH + ms_Arab_BN + mn_Cyrl + sbp_TZ + en_IL + ha_Latn_NE + lt_LT + mfe + en_GD + cy + ca_FR + es_BO + fr_BL + bn_IN + uz_Cyrl_UZ + az_Cyrl + en_IM + sw_KE + en_SB + pa_Arab + ur_PK + haw_US + ar_SO + en_IN + ha_Latn + fil + fr_MF + en_WS + es_CU + ja_JP + fy_NL + en_SC + en_IO + pt_PT + en_HK + en_GG + fr_MG + de_LU + ms_Latn_MY + tg_Cyrl + en_SD + shi_Tfng + ln_AO + ug_Arab_CN + as_IN + en_GH + ro_RO + jgo_CM + dua + en_UM + en_SE + kn_IN + en_KY + vun_TZ + kln + en_GI + ca_ES + rof + pt_CV + kok + pt_BR + ar_DJ + yi_001 + fi_FI + tg_Cyrl_TJ + zh + es_PY + ar_SS + mua + sr_Cyrl_ME + vai_Vaii_LR + en_001 + nl_NL + en_TK + si_LK + en_SG + sv_SE + fr_DZ + ca_AD + pt_AO + vi + xog_UG + xog + en_IS + nb + seh_MZ + es_AR + sk_SK + en_SH + ti_ER + nd + az_Cyrl_AZ + zu + ne + nd_ZW + el_CY + en_IT + nl_BQ + da_GL + ja + rm + fr_ML + rn + en_VU + rof_TZ + ro + ebu_KE + ru_KG + en_SI + sg_CF + mfe_MU + nl + brx + bs_Latn + fa + zgh_MA + en_GM + shi_Latn + en_FI + nn + en_EE + ru + kam_KE + fur + vai_Vaii + ar_ER + ti_ET + rw + ff + luo + fa_AF + ha_Latn_NG + nl_CW + en_HR + en_FJ + fi + pt_MO + be + en_US + en_TO + en_SK + bg + ru_BY + it_IT + ml_IN + gsw_CH + qu_EC + fo + sv_FI + en_FK + nus + ta_LK + vun + sr_Latn + fr + en_SL + bm + ar_BH + guz + bn + bo + ar_SY + lo_LA + ne_NP + uz_Latn + be_BY + es_IC + sr_Latn_XK + ar_MA + pa_Guru_IN + br + luy + kde_TZ + bs + fy + fur_IT + hu_HU + ar_AE + en_HU + sah_RU + zh_Hans + en_FM + sq_AL + ko_KP + en_150 + en_DE + fr_MQ + en_CA + hsb_DE + en_TR + ro_MD + es_VE + fr_WF + mt_MT + kab + nmg_CM + en_GR + ru_UA + fr_MR + tk_Latn_TM + zh_Hans_MO + mn_Cyrl_MN + ff_GN + bs_Cyrl + sw_UG + ko_KR + en_DG + bo_IN + en_CC + shi_Tfng_MA + lag + it_SM + os_RU + en_TT + ms_Arab_MY + sq_MK + ms_Latn + bem_ZM + kde + ar_OM + cgg + bas + bm_Latn_ML + kam + es_MX + sah + wae + en_GU + zh_Hant + fr_MU + fr_KM + ar_LB + en_BA + en_TV + sr_Cyrl + dje + kab_DZ + fil_PH + se_SE + vai + hr_HR + bs_Latn_BA + nl_AW + dav + so_SO + ar_PS + en_FR + uz_Cyrl + ff_SN + en_BB + ki_KE + naq + en_SS + mg_MG + mas_KE + en_RO + en_PG + mgh + dyo_SN + mas + agq + bn_BD + haw + yi + nb_NO + da_DK + en_DK + saq + ug + cy_GB + fr_YT + jmc + ses_ML + en_PH + de_DE + ar_YE + yo + lkt_US + uz_Arab_AF + jgo + sl_SI + uk + en_CH + asa + lg_UG + qu_PE + mgo + id_ID + en_NA + en_GY + zgh + pt_MZ + fr_LU + kk_Cyrl + mas_TZ + en_DM + ta_MY + dsb + en_BE + mg + ur + fr_GA + ka_GE + nmg + en_TZ + eu_ES + ar_DZ + id + so_DJ + hsb + yav + mk + pa_Arab_PK + ml + en_ER + ig + se_FI + mn + ksb + uz + vi_VN + ii + qu + en_PK + ee + mr + ms + en_ES + sq_XK + it_CH + mt + en_CK + br_FR + sr_Cyrl_XK + ksf + en_SX + bg_BG + en_PL + af + el + cs_CZ + fr_TD + zh_Hans_HK + is + ksh + my + en + it + dsb_DE + ii_CN + smn + iu + eo + en_ZA + en_AD + ak + en_RU + kkj_CM + am + es + et + uk_UA + + Model + iMac17,1 + ModelName + iMac + OSUpdateSettings + + AutoCheckEnabled + + AutomaticAppInstallationEnabled + + AutomaticOSInstallationEnabled + + AutomaticSecurityUpdatesEnabled + + BackgroundDownloadEnabled + + CatalogURL + https://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz + IsDefaultCatalog + + PerformPeriodicCheck + + PreviousScanDate + 2016-06-08T03:50:31Z + PreviousScanResult + 0 + + OSVersion + 10.11.5 + ProductName + iMac17,1 + SerialNumber + C12A1234567L + UDID + 11111111-2222-3333-4444-555566667777 + WiFiMAC + dd:ee:aa:dd:be:ef + iTunesStoreAccountHash + aaaaaaaaAAAAAAAAaaaaAAAAAAA= + iTunesStoreAccountIsActive + + + RequestType + DeviceInformation + Status + Acknowledged + UDID + 11111111-2222-3333-4444-555566667777 + +` + + // IOS 8 Fixture is here because it contains different keys to an IOS 9 device. NOTE: WiFi no cellular. + IOS8IpadQueryResponses = ` + + + + CommandUUID + f14b5d8b-e1ae-4a3a-b217-e060803db82b + QueryResponses + + AvailableDeviceCapacity + 3.9073715209960938 + BatteryLevel + 1 + BluetoothMAC + de-ad-be-ef-ca-fe + BuildVersion + 12H143 + CellularTechnology + 0 + DeviceCapacity + 26.629673004150391 + DeviceName + micromdm-ios-8-ipad + EASDeviceIdentifier + AAAAAAAAAAAAAAAAAAAAAAAAAA + IsActivationLockEnabled + + IsCloudBackupEnabled + + IsDeviceLocatorServiceEnabled + + IsDoNotDisturbInEffect + + IsSupervised + + Model + MD786X + ModelName + iPad + OSVersion + 8.4 + ProductName + iPad4,1 + SerialNumber + ABCDABCDAB00 + UDID + 1111111111111111111111111111111111111111 + WiFiMAC + dd:ee:aa:dd:be:e1 + iTunesStoreAccountHash + aaaaaaaaAAAAAAAAaaaaAAAAAAA= + iTunesStoreAccountIsActive + + + Status + Acknowledged + UDID + 1111111111111111111111111111111111111111 + +` + + IOS8IphoneQueryResponses = ` + + + + CommandUUID + c3911e20-e407-4ef8-b919-e9e3bce35ac6 + QueryResponses + + AvailableDeviceCapacity + 24.264816284179688 + BatteryLevel + 0.54000002145767212 + BluetoothMAC + de-ad-be-ef-ca-fe + BuildVersion + 12F70 + CarrierSettingsVersion + 19.0 + CellularTechnology + 3 + CurrentMCC + 504 + CurrentMNC + 02 + DataRoamingEnabled + + DeviceCapacity + 55.89947509765625 + DeviceName + micromdm-iphone6-ios8 + EASDeviceIdentifier + AAAAAAAAAAAAAAAAAAAAAAAAAA + ICCID + 1111 2222 3333 4444 5555 + IMEI + 11 222222 333333 4 + IsActivationLockEnabled + + IsCloudBackupEnabled + + IsDeviceLocatorServiceEnabled + + IsDoNotDisturbInEffect + + IsRoaming + + IsSupervised + + MEID + 00000000000000 + Model + MG4F2X + ModelName + iPhone + ModemFirmwareVersion + 2.23.03 + OSVersion + 8.3 + PersonalHotspotEnabled + + PhoneNumber + +00111111111 + ProductName + iPhone7,2 + SIMCarrierNetwork + Tincan + SerialNumber + 000000000001 + SubscriberCarrierNetwork + Tincan + SubscriberMCC + 502 + SubscriberMNC + 02 + UDID + 1111111111111111111111111111111111111112 + WiFiMAC + dd:ee:aa:dd:be:e2 + iTunesStoreAccountHash + aaaaaaaaAAAAAAAAaaaaAAAAAAA= + iTunesStoreAccountIsActive + + + Status + Acknowledged + UDID + 1111111111111111111111111111111111111112 + +` + + OSXElCapSecurityInfoNoFDE = ` + + + + CommandUUID + 1111111111111111111111111111111111111111 + RequestType + SecurityInfo + SecurityInfo + + FDE_Enabled + + + Status + Acknowledged + UDID + 1111111111111111111111111111111111111111 + + + ` + + IOS8IpadSecurityInfo = ` + + + + CommandUUID + 1111111111111111111111111111111111111112 + SecurityInfo + + HardwareEncryptionCaps + 3 + PasscodeCompliant + + PasscodeCompliantWithProfiles + + PasscodePresent + + + Status + Acknowledged + UDID + 1111111111111111111111111111111111111112 + +` +) diff --git a/test/responses/authenticate.plist b/test/responses/authenticate.plist new file mode 100644 index 0000000..917f497 --- /dev/null +++ b/test/responses/authenticate.plist @@ -0,0 +1,20 @@ + + + + + BuildVersion + 13F69 + MessageType + Authenticate + OSVersion + 9.3.2 + ProductName + iPad4,1 + SerialNumber + XXXXXXXXXXXX + Topic + io.micromdm.topic.00000000-1111-2222-3333-444455556666 + UDID + 1111111111111111111111111111111111111111 + + diff --git a/test/responses/certificatelist.plist b/test/responses/certificatelist.plist new file mode 100644 index 0000000..0b2ab25 --- /dev/null +++ b/test/responses/certificatelist.plist @@ -0,0 +1,45 @@ + + + + + CertificateList + + + CommonName + client.local + Data + + AAAA== + + IsIdentity + + + + CommonName + MicroMDM CA + Data + + AAAA== + + IsIdentity + + + + CommonName + MicroMDM Identity + Data + + AAAA== + + IsIdentity + + + + CommandUUID + 00000000-1111-2222-3333-444455556666 + Status + Acknowledged + UDID + 1111111111111111111111111111111111111111 + + \ No newline at end of file diff --git a/test/responses/error_invalidreq.plist b/test/responses/error_invalidreq.plist new file mode 100644 index 0000000..2e45d1c --- /dev/null +++ b/test/responses/error_invalidreq.plist @@ -0,0 +1,25 @@ + + + + + CommandUUID + 00000000-1111-2222-3333-444455556666 + ErrorChain + + + ErrorCode + 12021 + ErrorDomain + MCMDMErrorDomain + LocalizedDescription + “OSUpdateStatus” is not a valid request type. + USEnglishDescription + “OSUpdateStatus” is not a valid request type. + + + Status + Error + UDID + 1111111111111111111111111111111111111111 + + \ No newline at end of file diff --git a/test/responses/installed_application_list.plist b/test/responses/installed_application_list.plist new file mode 100644 index 0000000..0813bb9 --- /dev/null +++ b/test/responses/installed_application_list.plist @@ -0,0 +1,47 @@ + + + + + CommandUUID + 00000000-1111-2222-3333-444455556666 + InstalledApplicationList + + + BundleSize + 5855484 + Identifier + com.apple.systempreferences + Name + System Preferences + ShortVersion + 14.0 + Version + 14.0 + + + BundleSize + 0 + Name + Java Mission Control + + + BundleSize + 65092 + Name + Set Info + + + BundleSize + 0 + Name + Install OS X Yosemite + + + RequestType + InstalledApplicationList + Status + Acknowledged + UDID + 00000000-1111-2222-3333-444455556666 + + \ No newline at end of file diff --git a/test/responses/securityinfo.plist b/test/responses/securityinfo.plist new file mode 100644 index 0000000..c9a9022 --- /dev/null +++ b/test/responses/securityinfo.plist @@ -0,0 +1,27 @@ + + + + + CommandUUID + 00000000-1111-2222-3333-444455556666 + SecurityInfo + + HardwareEncryptionCaps + 3 + PasscodeCompliant + + PasscodeCompliantWithProfiles + + PasscodeLockGracePeriod + 0 + PasscodeLockGracePeriodEnforced + 0 + PasscodePresent + + + Status + Acknowledged + UDID + 1111111111111111111111111111111111111111 + + \ No newline at end of file diff --git a/test/responses/tokenupdate.plist b/test/responses/tokenupdate.plist new file mode 100644 index 0000000..6fdb260 --- /dev/null +++ b/test/responses/tokenupdate.plist @@ -0,0 +1,24 @@ + + + + + AwaitingConfiguration + + MessageType + TokenUpdate + PushMagic + 00000000-1111-2222-3333-444455556666 + Token + + AAAA= + + Topic + io.micromdm.topic.00000000-1111-2222-3333-444455556666 + UDID + 1111111111111111111111111111111111111111 + UnlockToken + + AAAA== + + + \ No newline at end of file