示例#1
0
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
示例#2
0
    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
示例#3
0
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)
示例#5
0
    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
示例#7
0
    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)
示例#8
0
    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)
示例#9
0
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()
示例#10
0
def device(session):
    d = Device(udid='00000000-1111-2222-3333-444455556666',
               device_name='commandment-mdmclient')
    session.add(d)
    session.commit()
示例#11
0
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()