def _do_test_error_preauth_limit(self, management_api, device_api, tenant_token=""): auth = management_api.make_auth(tenant_token) devs = management_api.list_devices(**auth) assert len(devs) == 6 limit = 3 for i in range(limit): dev = devs[i] aid = dev.auth_sets[0].id with orchestrator.run_fake_for_device_id(dev.id): management_api.accept_device(dev.id, aid, **auth) try: d = Device(IDDATA) d.public_key = PUBKEY d.private_key = PRIVKEY da = DevAuthorizer(tenant_token) rsp = device_auth_req(device_api.auth_requests_url, da, d) except bravado.exception.HTTPError as e: assert e.response.status_code == 401 dev = management_api.find_device_by_identity(d.identity, **auth) assert dev.auth_sets[0].status == 'preauthorized'
def test_ok(self, api_client_int, clean_db, mongo, test_set): """ Happy path - correct link obtained from the service, leading to a successful download of a correct artifact. """ # set up deployment tenant_id = str(ObjectId()) _, r = api_client_int.create_tenant(tenant_id) assert r.status_code == 201 deployment_id = str(uuid4()) dev = Device() dev.device_type = test_set["dev_type"] configuration_deployment = { "name": test_set["name"], "configuration": test_set["config"], } make_deployment( api_client_int, tenant_id, deployment_id, dev.devid, configuration_deployment, ) # obtain + verify deployment instructions dc = SimpleDeviceClient() nextdep = dc.get_next_deployment( dev.fake_token_mt(tenant_id), artifact_name="dontcare", device_type=test_set["dev_type"], ) assert nextdep.artifact["artifact_name"] == test_set["name"] assert nextdep.artifact["source"]["uri"] is not None assert nextdep.artifact["source"]["expire"] is not None assert nextdep.artifact["device_types_compatible"] == [test_set["dev_type"]] # get/verify download contents r = requests.get( nextdep.artifact["source"]["uri"], verify=False, ) assert r.status_code == 200 with open("/testing/out.mender", "wb+") as f: f.write(r.content) self.verify_artifact( "/testing/out.mender", test_set["name"], test_set["dev_type"], test_set["config"], )
def test_auth_req_bad_key(self, device_api, management_api, clean_migrated_db): d = Device() da = DevAuthorizer() # corrupt the autogenerated public key d.public_key = 'invalid' rsp = device_auth_req(device_api.auth_requests_url, da, d) assert rsp.status_code == 400 assert rsp.json( )['error'] == 'invalid auth request: cannot decode public key'
def accepted_device(device_api, management_api, clean_migrated_db): """Fixture that sets up an accepted device. Yields a tuple: (device ID, instance of Device, instance of DevAuthorizer)""" d = Device() da = DevAuthorizer() url = device_api.auth_requests_url # poke devauth so that device appears rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 # try to find our devices in all devices listing dev = management_api.find_device_by_identity(d.identity) print('found matching device with ID', dev.id) devid = dev.id # extract authentication data set ID aid = dev.auth_sets[0].id try: with orchestrator.run_fake_for_device_id(devid) as server: management_api.accept_device(devid, aid) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 yield (devid, d, da)
def accept_device(device_api, management_api, tenant_token=None): d = Device() da = DevAuthorizer(tenant_token) url = device_api.auth_requests_url kwargs = {} if tenant_token is not None: kwargs["Authorization"] = "Bearer " + tenant_token try: with orchestrator.run_fake_for_device_id(1) as server: with mock_tenantadm_auth(): # poke devauth so that device appears rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 # try to find our devices in all devices listing dev = management_api.find_device_by_identity(d.identity, **kwargs) assert dev is not None print("found matching device with ID", dev.id) devid = dev.id # extract authentication data set ID aid = dev.auth_sets[0].id with orchestrator.run_fake_for_device_id(devid) as server: management_api.accept_device(devid, aid, **kwargs) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 return devid, d, da
def make_pending_device(utoken, num_auth_sets=1, tenant_token=''): id_data = rand_id_data() dev = None for i in range(num_auth_sets): priv, pub = util.crypto.rsa_get_keypair() new_set = create_authset(id_data, pub, priv, utoken, tenant_token=tenant_token) if dev is None: dev = Device(new_set.did, new_set.id_data, utoken, tenant_token) dev.authsets.append(new_set) dev.status = 'pending' return dev
def test_device_new(self, device_api, clean_migrated_db): d = Device() da = DevAuthorizer() with deviceadm.run_fake_for_device(d) as server: rsp = device_auth_req(device_api.auth_requests_url, da, d) assert rsp.status_code == 401
def test_device_new(self): d = Device() da = DevAuthorizer() url = self.devapi.make_api_url("auth_requests") self.log.error("device URL: %s", url) rsp = device_auth_req(self.devapi.make_api_url("auth_requests"), da, d) assert rsp.status_code == 401
def test_deployments_new_no_artifact(self): """Add deployment without an artifact, verify that it's finished and device deployment has `noartifact` status""" dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) artifact_name = 'no-artifact ' + str(uuid4()) # come up with an artifact newdep = self.make_new_deployment(name='fake deployment', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dep = self.client.deployments.get_deployments_id( Authorization='foo', id=depid).result()[0] self.log.debug('deployment dep: %s', dep) assert dep.artifact_name == artifact_name assert dep.id == depid assert dep.status == 'finished' # fetch device status depdevs = self.client.deployments.get_deployments_deployment_id_devices( Authorization='foo', deployment_id=depid).result()[0] self.log.debug('deployment devices: %s', depdevs) assert len(depdevs) == 1 depdev = depdevs[0] assert depdev.status == 'noartifact' assert depdev.id == dev.devid
def test_get_devices(self): url = self.devapi.make_api_url("auth_requests") mc = SimpleManagementClient() devs = mc.list_devices() self.log.debug('devices; %s', devs) devcount = 50 for _ in range(devcount): dev = Device() da = DevAuthorizer() # poke devauth so that device appears rsp = device_auth_req(url, da, dev) assert rsp.status_code == 401 # try to get a maximum number of devices devs = mc.list_devices(page=1, per_page=500) self.log.debug('got %d devices', len(devs)) assert 500 >= len(devs) >= devcount # we have added at least `devcount` devices, so listing some lower # number of device should return exactly that number of entries plimit = devcount // 2 devs = mc.list_devices(page=1, per_page=plimit) self.log.debug('got %d devices', len(devs)) assert len(devs) == plimit
def test_auth_req_fake_tenantadm_tenant_suspended(self, management_api, device_api, tenant_foobar, clean_migrated_db): d = Device() da = DevAuthorizer(tenant_token=tenant_foobar) url = device_api.auth_requests_url handlers = [ ('POST', '/api/internal/v1/tenantadm/tenants/verify', lambda _: (401, {}, { 'request_id': 'test', 'error': 'account suspended' })), ] with mockserver.run_fake(get_fake_tenantadm_addr(), handlers=handlers) as fake: rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 assert rsp.json()['error'] == 'account suspended' # request failed, so device should not even be listed as known for the # default tenant TestEnterprise.verify_tenant_dev_present(management_api, d.identity, '', present=False)
def test_auth_req_fake_tenantadm_valid_tenant_token( self, management_api, device_api, tenant_foobar): d = Device() da = DevAuthorizer(tenant_token=tenant_foobar) url = device_api.auth_requests_url handlers = [ ('POST', '/api/internal/v1/tenantadm/tenants/verify', lambda _: (200, {}, { 'id': '507f191e810c19729de860ea', 'name': 'Acme', })), ] try: with orchestrator.run_fake_for_device_id(1) as server: with mockserver.run_fake(get_fake_tenantadm_addr(), handlers=handlers) as fake: rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 except bravado.exception.HTTPError as e: assert e.response.status_code == 204 # device should be appear in devices listing TestEnterprise.verify_tenant_dev_present(management_api, d.identity, tenant_foobar, present=True)
def test_device_deployments_already_installed(self): """Check case with already installed artifact """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update-' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) # pretend we have the same artifact installed already # NOTE: asking for a deployment while having it already # installed is special in the sense that the status of # deployment for this device will be marked as 'already-installed' nextdep = dc.get_next_deployment(dev.fake_token, artifact_name=artifact_name, device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep == None # verify that device status was properly recorded self.verify_deployment_stats(depid, expected={ 'already-installed': 1, })
def test_deplyments_get_devices(self): """Create deployments, get devices with pagination""" devices = [] devices_qty = 30 device_ids = [] default_per_page = 20 device_type = "test-hammer-type" # create devices for i in range(0, devices_qty): device = Device(device_type) self.inventory_add_dev(device) devices.append(device) device_ids.append(device.devid) data = b"foo_bar" artifact_name = "pagination-test-" + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=device_type) as art: ac = SimpleArtifactsClient() ac.add_artifact(description="some description", size=art.size, data=art) new_dep = self.d.make_new_deployment(name="pagination deployment", artifact_name=artifact_name, devices=device_ids) dep_id = self.d.add_deployment(new_dep) for dev in devices: dc = SimpleDeviceClient() dc.get_next_deployment( dev.fake_token, artifact_name="different {}".format(artifact_name), device_type=dev.device_type, ) # check default 'page' and 'per_page' values res = self.d.client.Management_API.List_Devices_in_Deployment_with_pagination( Authorization="foo", deployment_id=dep_id).result()[0] assert len(res) == default_per_page # check custom 'per_page' res = self.d.client.Management_API.List_Devices_in_Deployment_with_pagination( Authorization="foo", deployment_id=dep_id, per_page=devices_qty).result()[0] assert len(res) == devices_qty # check 2nd page devices_qty_on_second_page = devices_qty - default_per_page res = self.d.client.Management_API.List_Devices_in_Deployment_with_pagination( Authorization="foo", deployment_id=dep_id, page=2, per_page=default_per_page).result()[0] assert len(res) == devices_qty_on_second_page
def test_id_data_formatting(self, device_api, management_api, clean_migrated_db): reference_id_data = [ '{"attribute_foo":"foo"}', '{"attribute_bar":2, "attribute_foo":1}', '{"attribute_foo":"foo","mac": "00:00:00:01","sn": "0001"}', ] reformatted_id_data = [ '{"attribute_foo": "foo"}', ' { "attribute_foo": "foo" }', '{"attribute_foo":1, "attribute_bar":2}', '{ "attribute_foo":1, "attribute_bar":2}', '{"sn": "0001","mac": "00:00:00:01","attribute_foo":"foo"}', ] # submit first auth req with 'reference data', second with 'reformatted' data # must result in only 3 devices with deviceadm.run_fake_for_device(deviceadm.ANY_DEVICE) as server: for reference in reference_id_data: dev = Device(reference) da = DevAuthorizer() rsp = device_auth_req(device_api.auth_requests_url, da, dev) assert rsp.status_code == 401 devs = management_api.list_devices() assert len(devs) == 3 with deviceadm.run_fake_for_device(deviceadm.ANY_DEVICE) as server: for reformatted in reformatted_id_data: dev = Device(reformatted) da = DevAuthorizer() rsp = device_auth_req(device_api.auth_requests_url, da, dev) assert rsp.status_code == 401 devs = management_api.list_devices() assert len(devs) == 3 # verify we have the correct id data json_reference_id_data = [json.loads(i) for i in reference_id_data] for d in devs: api_id_data = json.loads(d.id_data) found_in_reference = [ x for x in json_reference_id_data if x == api_id_data ] assert len(found_in_reference) == 1
def test_auth_req_fake_tenantadm_no_tenant_token(self): d = Device() # use empty tenant token da = DevAuthorizer(tenant_token="") url = self.devapi.make_api_url("/auth_requests") rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 self.verify_tenant_dev_present(d.identity, False, tenant='')
def test_device_deployments_simple(self): """Check that device can get next deployment, simple cases: - bogus token - valid update - device type incompatible with artifact """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update-' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) # try with some bogus token try: dc.get_next_deployment('foo-bar-baz', artifact_name=artifact_name, device_type=dev.device_type) except bravado.exception.HTTPError as err: assert 400 <= err.response.status_code < 500 else: raise AssertionError('expected to fail') # pretend we have another artifact installed nextdep = dc.get_next_deployment(dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep assert dev.device_type in nextdep.artifact['device_types_compatible'] # pretend our device type is different than expected nextdep = dc.get_next_deployment(dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type='other {}'.format(dev.device_type)) self.log.info('device next: %s', nextdep) assert nextdep == None # verify that device status was properly recorded self.verify_deployment_stats(depid, expected={ 'noartifact': 1, })
def test_auth_req_fake_tenantadm_no_tenant_token(self, management_api, device_api, clean_migrated_db): d = Device() # use empty tenant token da = DevAuthorizer(tenant_token="") url = device_api.auth_requests_url rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 TestMultiTenant.verify_tenant_dev_present(management_api, d.identity, '', present=False)
def test_device_deployments_logs(self): """Check that device can get next deployment, full cycle """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update ' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) # pretend we have another artifact installed nextdep = dc.get_next_deployment( dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep dc.upload_logs(dev.fake_token, nextdep.id, logs=[ 'foo bar baz', 'lorem ipsum', ]) rsp = self.client.deployments.get_deployments_deployment_id_devices_device_id_log( Authorization='foo', deployment_id=depid, device_id=dev.devid).result()[1] logs = rsp.text self.log.info('device logs\n%s', logs) assert 'lorem ipsum' in logs assert 'foo bar baz' in logs
def test_auth_req_no_tenantadm(self): d = Device() da = DevAuthorizer(tenant_token=make_fake_tenant_token( tenant='foobar')) url = self.devapi.make_api_url("/auth_requests") # poke devauth so that device appears, but since tenantadm service is # unavailable we'll get 500 in return rsp = device_auth_req(url, da, d) assert rsp.status_code == 500 # request failed, so device should not even be listed as known self.verify_tenant_dev_present(d.identity, False, tenant='foobar')
def make_preauthd_device(utoken): devauthm = ApiClient(deviceauth_v2.URL_MGMT) priv, pub = util.crypto.rsa_get_keypair() id_data = rand_id_data() body = deviceauth_v2.preauth_req(id_data, pub) r = devauthm.with_auth(utoken).call('POST', deviceauth_v2.URL_DEVICES, body) assert r.status_code == 201 api_dev = get_device_by_id_data(id_data, utoken) assert len(api_dev['auth_sets']) == 1 aset = api_dev['auth_sets'][0] dev = Device(api_dev['id'], id_data, pub) dev.authsets.append( Authset(aset['id'], dev.id, id_data, pub, priv, 'preauthorized')) dev.status = 'preauthorized' return dev
def test_auth_req_no_tenantadm(self, management_api, device_api, tenant_foobar): d = Device() da = DevAuthorizer(tenant_token=tenant_foobar) url = device_api.auth_requests_url # poke devauth so that device appears, but since tenantadm service is # unavailable we'll get 500 in return rsp = device_auth_req(url, da, d) assert rsp.status_code == 500 # request failed, so device should not even be listed as known TestMultiTenant.verify_tenant_dev_present(management_api, d.identity, tenant_foobar, present=False)
def get_device_from_url(self, url): # type: (unicode) -> Device """ :param url: :return: """ response = urllib2.urlopen(url) encoding = response.headers['content-type'].split('charset=')[-1] json_text = unicode(response.read(), encoding) return Device.from_dict(json.loads(json_text))
def test_delete_device(self): # try delete a nonexistent device try: self.delete_device('some-devid-foo') except bravado.exception.HTTPError as e: assert e.response.status_code == 404 # try delete an existing device, verify decommissioning workflow was started # setup single device and poke devauth dev = Device() da = DevAuthorizer() # poke devauth so that device appears with deviceadm.run_fake_for_device(dev) as server: rsp = device_auth_req(self.devapi.make_api_url("auth_requests"), da, dev) assert rsp.status_code == 401 mc = SimpleManagementClient() ourdev = mc.find_device_by_identity(dev.identity) assert ourdev # handler for orchestrator's job endpoint def decommission_device_handler(request): dreq = json.loads(request.body.decode()) self.log.info('decommision request %s', dreq) # verify that devauth tries to decommision correct device assert dreq.get('device_id', None) == ourdev.id # test is enforcing particular request ID assert dreq.get('request_id', None) == 'delete_device' # test is enforcing particular request ID assert dreq.get('authorization', None) == 'Bearer foobar' return (200, {}, '') handlers = [ ('POST', '/api/workflow/decommission_device', decommission_device_handler), ] with mockserver.run_fake(get_fake_orchestrator_addr(), handlers=handlers) as server: rsp = self.delete_device( ourdev.id, { 'X-MEN-RequestID': 'delete_device', 'Authorization': 'Bearer foobar', }) self.log.info('decommission request finished with status: %s', rsp.status_code) assert rsp.status_code == 204 found = mc.find_device_by_identity(dev.identity) assert not found
def test_device_deployments_logs(self): """Check that device can get next deployment, full cycle""" dev = Device() self.d.log.info("fake device with ID: %s", dev.devid) self.inventory_add_dev(dev) data = b"foo_bar" artifact_name = "hammer-update-" + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description="desc", size=art.size, data=art) as artid: newdep = self.d.make_new_deployment( name="foo", artifact_name=artifact_name, devices=[dev.devid]) with self.d.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.d.log.debug("device token %s", dev.fake_token) # pretend we have another artifact installed nextdep = dc.get_next_deployment( dev.fake_token, artifact_name="different {}".format(artifact_name), device_type=dev.device_type, ) self.d.log.info("device next: %s", nextdep) assert nextdep dc.upload_logs(dev.fake_token, nextdep.id, logs=["foo bar baz", "lorem ipsum"]) rsp = self.d.client.Management_API.Get_Deployment_Log_for_Device( Authorization="foo", deployment_id=depid, device_id=dev.devid).result()[1] logs = rsp.text self.d.log.info("device logs\n%s", logs) assert "lorem ipsum" in logs assert "foo bar baz" in logs
def _do_test_ok_preauth(self, management_api, device_api, tenant_token=""): d = Device(IDDATA) d.public_key = PUBKEY d.private_key = PRIVKEY da = DevAuthorizer(tenant_token=tenant_token) # get the authset id - need it for the url auth = management_api.make_auth(tenant_token) dbg = management_api.list_devices() print(dbg) dev = management_api.find_device_by_identity(d.identity, **auth) assert dev with devadm_fake_status_update(AID), \ orchestrator.run_fake_for_device_id(DEVID): rsp = device_auth_req(device_api.auth_requests_url, da, d) assert rsp.status_code == 200 dev = management_api.get_device(id=dev.id, **auth) assert dev.auth_sets[0].status == 'accepted'
def devices(clean_migrated_mongo, user): uc = ApiClient(useradm.URL_MGMT) r = uc.call('POST', useradm.URL_LOGIN, auth=(user.name, user.pwd)) assert r.status_code == 200 utoken = r.text devices = [] for _ in range(5): aset = create_random_authset(utoken) dev = Device(aset.did, aset.id_data, aset.pubkey) devices.append(dev) yield devices
def tenants_users_devices(tenants_users, mongo): uc = ApiClient(useradm.URL_MGMT) for t in tenants_users: user = t.users[0] r = uc.call('POST', useradm.URL_LOGIN, auth=(user.name, user.pwd)) assert r.status_code == 200 utoken = r.text for _ in range(2): aset = create_random_authset(utoken, t.tenant_token) dev = Device(aset.did, aset.id_data, aset.pubkey, t.tenant_token) dev.authsets.append(aset) t.devices.append(dev) yield tenants_users
def test_auth_req_fake_tenantadm_invalid_tenant_token(self): d = Device() da = DevAuthorizer(tenant_token="bad-token") url = self.devapi.make_api_url("/auth_requests") handlers = [ ('POST', '/api/internal/v1/tenantadm/tenants/verify', lambda _: (401, {}, '')), ] with mockserver.run_fake(get_fake_tenantadm_addr(), handlers=handlers) as fake: rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 # request failed, so device should not even be listed as known self.verify_tenant_dev_present(d.identity, False, tenant='')
def test_device_new(self, device_api, management_api, clean_migrated_db): d = Device() da = DevAuthorizer() rsp = device_auth_req(device_api.auth_requests_url, da, d) assert rsp.status_code == 401 devs = management_api.list_devices() assert len(devs) == 1 dev = devs[0] assert len(dev.auth_sets) == 1 aset = dev.auth_sets[0] assert compare_keys(aset.pubkey, d.public_key)