示例#1
0
 def test_token_failure(self, dep: DEP, expected_status: int,
                        expected_text: str):
     try:
         dep.fetch_token()
     except DEPServiceError as e:
         assert e.response.status_code == expected_status
         assert e.text == expected_text
示例#2
0
 def test_fetch_devices(self, dep_live: DEP):
     dep_live.fetch_token()
     devices = dep_live.fetch_devices()
     assert 'cursor' in devices
     assert 'devices' in devices
     assert 'fetched_until' in devices
     assert 'more_to_follow' in devices
示例#3
0
def dep_thread_callback(app: Flask):
    """Runner thread main procedure

    Todo:
        * Catch everything so we don't interrupt the thread (and it never reschedules)
        * Certificate expiration warnings/emails
    """
    threadlocals = threading.local()

    with app.app_context():
        try:
            dep_account: DEPAccount = db.session.query(DEPAccount).one()
            app.logger.info('Checking DEP state')

            dep = DEP(
                consumer_key=dep_account.consumer_key,
                consumer_secret=dep_account.consumer_secret,
                access_token=dep_account.access_token,
                access_secret=dep_account.access_secret,
            )

            dep_sync_organization(app, dep)

            try:
                dep_fetch_devices(app, dep, dep_account.id)
            except DEPServiceError as dse:
                print(dse)
                if dse.text == 'EXPIRED_CURSOR':
                    app.logger.info("Sync cursor had expired, clearing for next run...")
                    dep_account.cursor = None
                    db.session.add(dep_account)
                    db.session.commit()

        except sqlalchemy.orm.exc.NoResultFound:
            app.logger.info('Not attempting a DEP sync, no account configured.')
示例#4
0
    def test_account(self, dep_live: DEP):
        dep_live.fetch_token()
        account = dep_live.account()
        assert account is not None
        assert 'server_name' in account
        assert 'server_uuid' in account
        assert 'facilitator_id' in account
        assert 'admin_id' in account
        assert 'org_name' in account
        assert 'org_email' in account
        assert 'org_phone' in account
        assert 'org_address' in account

        # X-Server-Protocol 3
        assert 'org_id' in account
        assert 'org_id_hash' in account
        assert 'org_type' in account
        assert 'org_version' in account
示例#5
0
def dep(simulator_token: dict) -> DEP:
    d = DEP(
        consumer_key=simulator_token['consumer_key'],
        consumer_secret=simulator_token['consumer_secret'],
        access_token=simulator_token['access_token'],
        access_secret=simulator_token['access_secret'],
        url=SIMULATOR_URL,
    )

    return d
示例#6
0
def dep_sync_organization(app: Flask, dep: DEP):
    """Synchronise information from the DEP service to the local database.
    """
    with app.app_context():
        try:
            app.logger.debug(
                'Querying for DEP Account information from the database')
            dep_account: DEPAccount = db.session.query(DEPAccount).one()

            # Refresh organisation information if there is none
            if dep_account.server_name is None or dep_account.server_uuid is None:
                app.logger.debug(
                    'Refreshing information about organization from the DEP service'
                )
                account = dep.account()

                if account is not None:
                    dep_account.server_uuid = account.get('server_uuid', None)
                    dep_account.server_name = account.get('server_name', None)
                    dep_account.facilitator_id = account.get(
                        'facilitator_id', None)
                    dep_account.admin_id = account.get('admin_id', None)
                    dep_account.org_name = account.get('org_name', None)
                    dep_account.org_email = account.get('org_email', None)
                    dep_account.org_phone = account.get('org_phone', None)
                    dep_account.org_address = account.get('org_address', None)
                    dep_account.org_id = account.get('org_id', None)
                    dep_account.org_id_hash = account.get('org_id_hash', None)
                    if 'org_type' in account:
                        dep_account.org_type = DEPOrgType(account['org_type'])

                    if 'org_version' in account:
                        dep_account.org_version = DEPOrgVersion(
                            account['org_version'])

                    db.session.commit()
                    app.logger.info(
                        'Successfully fetched DEP Organization: %s',
                        dep_account.org_name)
                else:
                    app.logger.warn('Failed to fetch DEP Organization')
            else:
                app.logger.info('DEP Organization already fetched: %s',
                                dep_account.org_name)

        except sqlalchemy.orm.exc.NoResultFound:
            app.logger.info(
                'Not attempting to fetch DEP account information. No DEP account is configured.'
            )
示例#7
0
def dep_define_profiles(app: Flask, dep: DEP):
    """Create DEP profiles which have not yet been synced with Apple."""
    thread_session = db.create_scoped_session()

    dep_profiles_pending = thread_session.query(DEPProfile).filter(
        DEPProfile.uuid.is_(None), DEPProfile.last_upload_at.is_(None)).all()
    app.logger.debug('There are %d pending DEP profile(s) to upload',
                     len(dep_profiles_pending))

    for dep_profile in dep_profiles_pending:
        try:
            schema = AppleDEPProfileSchema()
            dep_profile_apple = schema.dump(dep_profile)
            print(dep_profile_apple.data)
            response = dep.define_profile(dep_profile_apple.data)
            assert 'profile_uuid' in response
            dep_profile.uuid = response['profile_uuid']
            dep_profile.last_uploaded_at = datetime.datetime.now()
        except Exception as e:
            app.logger.error(
                'Got an exception trying to define a profile: {}'.format(e))

    thread_session.commit()
示例#8
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()
示例#9
0
def dep_live(live_token: str):
    return DEP.from_token(live_token)
示例#10
0
 def test_get_profile(self, dep_live: DEP, live_dep_profile: str):
     dep_live.fetch_token()
     profiles = dep_live.profile(live_dep_profile)
     print(profiles)
示例#11
0
 def test_device_details(self, dep_live: DEP, live_device: str):
     dep_live.fetch_token()
     device_details = dep_live.device_detail(live_device)
     print(device_details)
示例#12
0
async def dep_sync(consumer_key: str, consumer_secret: str, access_token: str, access_secret: str, url: str):
    dep = DEP(consumer_key, consumer_secret, access_token, access_secret, url)
    initial_fetch = await initial_dep_fetch(dep)
示例#13
0
async def initial_dep_fetch(dep: DEP):
    """Perform the initial DEP fetch, if required."""
    for page in dep.devices():
        for device in page:
            pass
示例#14
0
 def test_account(self, dep: DEP):
     dep.fetch_token()
     account = dep.account()
     assert account is not None
示例#15
0
 def test_fetch_cursor(self, dep: DEP):
     dep.fetch_token()
     for page in dep.devices():
         print(len(page))
         for d in page:
             print(d)
示例#16
0
 def test_fetch_devices(self, dep: DEP):
     dep.fetch_token()
     devices = dep.fetch_devices()
     assert len(devices) == 500