def push_to_device(device: Device) -> apns2.Response: """Issue a `Blank Push` to a device. If the push token is invalid then it will be automatically set to None Args: device (Device): The device model to push to, must have a valid apns token and push magic Raises: ssl.SSLError [SSL: SSLV3_ALERT_CERTIFICATE_EXPIRED] sslv3 alert certificate expired (_ssl.c:777) if the push certificate has expired and the system attempts a push. Returns: APNS2Client Response object """ current_app.logger.debug( 'Sending a push notification to {} on topic {}, using push magic: {}'. format(device.hex_token, device.topic, device.push_magic)) client = get_apns() payload = MDMPayload(device.push_magic) notification = apns2.Notification(payload, priority=apns2.PRIORITY_LOW) response: apns2.response.Response = client.push(notification, device.hex_token, device.topic) # 410 means that the token is no longer valid for this device, so don't attempt to push any more if response.status_code == 410: device.token = None device.push_magic = None return response
def test_enroll_from_plist_finds_existing_device(self, app): plist = { 'UDID': '9b99e22c-c6b3-4768-8ab5-76c29f4021ac', 'SERIAL': '7b2c5e8e-4457-4a34-a942-05d5973c619e' } device = Device() device.serial_number = plist['SERIAL'] device.udid = plist['UDID'] cdatabase.db_session.add(device) cdatabase.db_session.commit() redis_client = app.redis_store._redis_client pubsub = {} for channel in ('enroll', 'serial'): pubsub_channel = redis_client.pubsub() pubsub_channel.subscribe('commandment.' + channel) pubsub_channel.get_message() pubsub[channel] = pubsub_channel new_device = enroll.enroll_from_plist(plist) message = pubsub['enroll'].get_message()['data'] assert pubsub['enroll'].get_message() is None payload = json.loads(message) assert payload == {'udid': plist['UDID']} message = pubsub['serial'].get_message()['data'] assert pubsub['serial'].get_message() is None payload = json.loads(message) assert payload == {'udid': plist['UDID'], 'serial_number': plist['SERIAL']} assert new_device.id == device.id
def push_to_device(device: Device) -> apns2.Response: """Issue a `Blank Push` to a device. If the push token is invalid then it will be automatically set to None Args: device (Device): The device model to push to, must have a valid apns token and push magic Returns: APNS2Client Response object """ current_app.logger.debug('Sending a push notification to {} on topic {}, using push magic: {}'.format( device.hex_token, device.topic, device.push_magic )) client = get_apns() payload = MDMPayload(device.push_magic) notification = apns2.Notification(payload, priority=apns2.PRIORITY_LOW) response = client.push(notification, device.hex_token, device.topic) # 410 means that the token is no longer valid for this device, so don't attempt to push any more if response.status_code == 410: device.token = None device.push_magic = None return response
def test_when_device_is_added_to_group_profiles_are_installed( self, client, device, profile, mdm_group): device = Device(**device) profile = Profile(**profile) profile_service = mock.MagicMock() # FIXME: there has to be a better way... original_profile_service = capi.ProfileService mdm_group = MDMGroup(**mdm_group) profile.status = ProfileStatus.ACTIVE mdm_group.profiles.append(profile) cdatabase.db_session.add(device) cdatabase.db_session.add(profile) cdatabase.db_session.add(mdm_group) cdatabase.db_session.commit() try: capi.ProfileService = mock.MagicMock() capi.ProfileService.return_value = profile_service res = client.put( url_for('api_app.mdmgroupresource_device', id=mdm_group.id, device_id=device.id)) finally: capi.ProfileService = original_profile_service assert self.assert_json(res.headers) assert self.assert_success(res) data = json.loads(res.data) assert data['message'] == "Success" profile_service.finalize_installation.assert_called_once_with( profile, device)
def test_deploy_changes_iff_status_is_correct_pending(self, profile, mdm_group, device): profile = Profile(**profile) mdm_group = MDMGroup(**mdm_group) device = Device(**device) profile_service = MagicMock() profile.mdm_groups = [mdm_group] mdm_group.devices = [device] tasks.ProfileService = MagicMock() tasks.ProfileService.return_value = profile_service tasks.db_session = MagicMock() query = MagicMock() tasks.db_session.query.return_value = query query.get.return_value = profile for status in list(ProfileStatus): profile.status = status tasks.push_to_device = MagicMock() tasks.db_session.add = MagicMock() tasks.process_profile_deployment_change(7) if status == ProfileStatus.PENDING_INSTALLATION: assert profile.status == ProfileStatus.ACTIVE tasks.push_to_device.assert_called_once_with(device) elif status == ProfileStatus.PENDING_DELETION: assert profile.status == ProfileStatus.PENDING_DELETION tasks.push_to_device.assert_called_once_with(device) elif status == ProfileStatus.PENDING_REMOVAL: assert profile.status == ProfileStatus.INACTIVE tasks.push_to_device.assert_called_once_with(device) else: assert profile.status == status assert not tasks.push_to_device.called
def test_enroll_from_plist_finds_existing_device(self, app): plist = { 'UDID': '9b99e22c-c6b3-4768-8ab5-76c29f4021ac', 'SERIAL': '7b2c5e8e-4457-4a34-a942-05d5973c619e' } device = Device() device.serial_number = plist['SERIAL'] device.udid = plist['UDID'] cdatabase.db_session.add(device) cdatabase.db_session.commit() redis_client = app.redis_store._redis_client pubsub = {} for channel in ('enroll', 'serial'): pubsub_channel = redis_client.pubsub() pubsub_channel.subscribe('commandment.' + channel) pubsub_channel.get_message() pubsub[channel] = pubsub_channel new_device = enroll.enroll_from_plist(plist) message = pubsub['enroll'].get_message()['data'] assert pubsub['enroll'].get_message() is None payload = json.loads(message) assert payload == {'udid': plist['UDID']} message = pubsub['serial'].get_message()['data'] assert pubsub['serial'].get_message() is None payload = json.loads(message) assert payload == { 'udid': plist['UDID'], 'serial_number': plist['SERIAL'] } assert new_device.id == device.id
def test_finalize_removal(self, profile, device): profile = Profile(**profile) device = Device(**device) profile_service = service.ProfileService() service.db_session = mock.MagicMock() query = mock.MagicMock() service.db_session.query.return_value = query query.get.return_value = profile removeqc = object() service.RemoveProfile = mock.MagicMock() service.RemoveProfile.new_queued_command.return_value = removeqc service.db_session.add = mock.MagicMock() profile_service.finalize_removal(profile, device) assert service.RemoveProfile.new_queued_command.call_args[0] == (device, {'Identifier': profile.identifier, 'UUID': profile.uuid}) service.db_session.add.assert_called_once_with(removeqc)
def test_finalize_installation(self, profile, device): profile = Profile(**profile) device = Device(**device) profile_service = service.ProfileService() profile.id = 7 service.db_session = mock.MagicMock() query = mock.MagicMock() service.db_session.query.return_value = query query.get.return_value = profile installqc = object() service.InstallProfile = mock.MagicMock() service.InstallProfile.new_queued_command.return_value = installqc service.db_session.add = mock.MagicMock() profile_service.finalize_installation(profile, device) assert service.InstallProfile.new_queued_command.call_args[0] == (device, {'id': 7}) service.db_session.add.assert_called_once_with(installqc)
def dep_fetch_devices(app: Flask, dep: DEP, dep_account_id: int): """Perform fetch or sync of devices. TODO: If default DEP Profile is nominated, it is queued for assignment here. But may want to check `profile_status` to see whether only devices with the `removed` status are considered unassigned. See: https://docs.sqlalchemy.org/en/latest/orm/contextual.html """ thread_session = db.create_scoped_session() dep_account: DEPAccount = thread_session.query(DEPAccount).one() if dep_account.cursor is not None: app.logger.info('Syncing using previous cursor: %s', dep_account.cursor) else: app.logger.info('No DEP cursor found, performing a full fetch') # TODO: if fetched_until is quite recent, there's no reason to fetch again for device_page in dep.devices(dep_account.cursor): print(device_page) for device in device_page['devices']: if 'op_type' in device: # its a sync, not a fetch optype = DEPOperationType(device['op_type']) if optype == DEPOperationType.Added: app.logger.debug('DEP Added: %s', device['serial_number']) elif optype == DEPOperationType.Modified: app.logger.debug('DEP Modified: %s', device['serial_number']) elif optype == DEPOperationType.Deleted: app.logger.debug('DEP Deleted: %s', device['serial_number']) else: app.logger.error('DEP op_type not recognised (%s), skipping', device['op_type']) continue else: pass try: d: Device = thread_session.query(Device).filter(Device.serial_number == device['serial_number']).one() d.description = device['description'] d.model = device['model'] d.os = device['os'] d.device_family = device['device_family'] d.color = device['color'] d.profile_status = device['profile_status'] if device['profile_status'] != 'empty': d.profile_uuid = device.get('profile_uuid', None) # Only exists in DEP Sync not Fetch? d.profile_assign_time = dateutil.parser.parse(device['profile_assign_time']) d.device_assigned_by = device['device_assigned_by'] d.device_assigned_date = dateutil.parser.parse(device['device_assigned_date']) d.is_dep = True except sqlalchemy.orm.exc.NoResultFound: app.logger.debug('No existing device record for serial: %s', device['serial_number']) if device['profile_status'] != 'empty': device['profile_assign_time'] = dateutil.parser.parse(device['profile_assign_time']) device['device_assigned_date'] = dateutil.parser.parse(device['device_assigned_date']) if 'op_type' in device: del device['op_type'] del device['op_date'] del device['profile_assign_time'] del device['device_assigned_date'] d = Device(**device) d.is_dep = True thread_session.add(d) except sqlalchemy.exc.StatementError as e: app.logger.error('Got a statement error trying to insert a DEP device: {}'.format(e)) app.logger.debug('Last DEP Cursor was: %s', device_page['cursor']) dep_account.cursor = device_page.get('cursor', None) dep_account.more_to_follow = device_page.get('more_to_follow', None) dep_account.fetched_until = dateutil.parser.parse(device_page['fetched_until']) thread_session.commit()
def device(session): d = Device(udid='00000000-1111-2222-3333-444455556666', device_name='commandment-mdmclient') session.add(d) session.commit()
def device(session: Session): """Create a fixture device which is referenced in all of the fake MDM responses by its UDID.""" d = Device(udid='00000000-1111-2222-3333-444455556666', device_name='commandment-mdmclient') session.add(d) session.commit()