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
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
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.')
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
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
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.' )
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()
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 dep_live(live_token: str): return DEP.from_token(live_token)
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)
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)
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)
async def initial_dep_fetch(dep: DEP): """Perform the initial DEP fetch, if required.""" for page in dep.devices(): for device in page: pass
def test_account(self, dep: DEP): dep.fetch_token() account = dep.account() assert account is not None
def test_fetch_cursor(self, dep: DEP): dep.fetch_token() for page in dep.devices(): print(len(page)) for d in page: print(d)
def test_fetch_devices(self, dep: DEP): dep.fetch_token() devices = dep.fetch_devices() assert len(devices) == 500