def test_device_accept_reject_cycle(self, devices, device_api, management_api): d, da = devices[0] url = device_api.auth_requests_url dev = management_api.get_single_device() assert dev devid = dev.id print("found device with ID:", dev.id) aid = dev.auth_sets[0].id with orchestrator.run_fake_for_device_id(devid) as server: _, rsp = management_api.accept_device(devid, aid) assert rsp.status_code == 204 # device is accepted, we should get a token now rsp = device_auth_req(url, da, d) assert rsp.status_code == 200 da.parse_rsp_payload(d, rsp.text) assert len(d.token) > 0 # reject it now _, rsp = management_api.reject_device(devid, aid) print("RSP:", rsp) assert rsp.status_code == 204 # device is rejected, should get unauthorized rsp = device_auth_req(url, da, d) assert rsp.status_code == 401
def test_device_count_multiple_auth_sets(self, devices, management_api, device_api): """"Verify that auth sets are properly counted. Take a device, make sure it has 2 auth sets, switch each auth sets between accepted/rejected/pending states """ dev, dauth = devices[0] # pretend device rotates its keys dev.rotate_key() with deviceadm.run_fake_for_device(deviceadm.ANY_DEVICE) as server: device_auth_req(device_api.auth_requests_url, dauth, dev) # should have 2 auth sets now found_dev = management_api.find_device_by_identity(dev.identity) assert len(found_dev.auth_sets) == 2 first_aid, second_aid = found_dev.auth_sets[0].id, found_dev.auth_sets[ 1].id # device [0] has 2 auth sets, but still counts as 1 device TestDevice.verify_device_count(management_api, 'pending', 5) devid = found_dev.id with orchestrator.run_fake_for_device_id( orchestrator.ANY_DEVICE) as server: # accept first auth set management_api.accept_device(devid, first_aid) TestDevice.verify_device_count(management_api, 'pending', 4) TestDevice.verify_device_count(management_api, 'accepted', 1) TestDevice.verify_device_count(management_api, 'rejected', 0) # reject the other management_api.reject_device(devid, second_aid) TestDevice.verify_device_count(management_api, 'pending', 4) TestDevice.verify_device_count(management_api, 'accepted', 1) TestDevice.verify_device_count(management_api, 'rejected', 0) # reject both management_api.reject_device(devid, first_aid) TestDevice.verify_device_count(management_api, 'pending', 4) TestDevice.verify_device_count(management_api, 'accepted', 0) TestDevice.verify_device_count(management_api, 'rejected', 1) # switch the first back to pending, 2nd remains rejected management_api.put_device_status(devid, first_aid, 'pending') TestDevice.verify_device_count(management_api, 'pending', 5) TestDevice.verify_device_count(management_api, 'accepted', 0) TestDevice.verify_device_count(management_api, 'rejected', 0)
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 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 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 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_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 _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 request_token(device, dev_auth, url): # device is accepted, we should get a token now rsp = device_auth_req(url, dev_auth, device) assert rsp.status_code == 200 dev_auth.parse_rsp_payload(device, rsp.text) return device.token
def request_token(device, dev_auth, url, tenant_addons=[]): with mock_tenantadm_auth(tenant_addons): rsp = device_auth_req(url, dev_auth, device) assert rsp.status_code == 200 dev_auth.parse_rsp_payload(device, rsp.text) return device.token
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_device_accept_reject(self): d = Device() da = DevAuthorizer() url = self.devapi.make_api_url("auth_requests") # poke devauth so that device appears with deviceadm.run_fake_for_device(d) as server: rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 # determine device ID by listing all devices and finding one with # matching public key mc = SimpleManagementClient() dev = mc.find_device_by_identity(d.identity) assert dev devid = dev.id self.log.debug('found matching device with ID: %s', dev.id) aid = dev.auth_sets[0].id try: with inventory.run_fake_for_device_id(devid) as server: self.accept_device(devid, aid) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 # device is accepted, we should get a token now rsp = device_auth_req(url, da, d) assert rsp.status_code == 200 da.parse_rsp_payload(d, rsp.text) assert len(d.token) > 0 self.log.info("device token: %s", d.token) # reject it now try: self.reject_device(devid, aid) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 # device is rejected, should get unauthorized with deviceadm.run_fake_for_device(d) as server: rsp = device_auth_req(url, da, d) assert rsp.status_code == 401
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_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_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 test_device_accept_reject_cycle(self, devices, device_api, management_api): d, da = devices[0] url = device_api.auth_requests_url dev = management_api.find_device_by_identity(d.identity) assert dev devid = dev.id print("found matching device with ID:", dev.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 # device is accepted, we should get a token now try: with orchestrator.run_fake_for_device_id(devid) as server: rsp = device_auth_req(url, da, d) assert rsp.status_code == 200 da.parse_rsp_payload(d, rsp.text) assert len(d.token) > 0 # reject it now try: management_api.reject_device(devid, aid) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 # device is rejected, should get unauthorized rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 except bravado.exception.HTTPError as e: assert e.response.status_code == 204
def test_token_seqnum(self): from itertools import repeat, count d = Device() da = DevAuthorizer(seqno=repeat(1)) # replace sequence number generator url = self.make_api_url("/auth_requests") # generate fake identity devid = make_devid(d.identity) # poke devauth so that device appears rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 try: self.accept_device(devid) except bravado.exception.HTTPError as e: assert e.response.status_code == 200 # device is accepted, but we're going to request a token using the same # sequence number as before rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 # try again with proper counter now da.seqno = count(next(da.seqno) + 1) rsp = device_auth_req(url, da, d) assert rsp.status_code == 200 da.parse_rsp_payload(d, rsp.text) assert len(d.token) > 0 self.log.info("device token: %s", d.token) # again, but fix seqno once more da.seqno = repeat(1) rsp = device_auth_req(url, da, d) assert rsp.status_code == 401
def test_device_accept_reject(self): d = Device() da = DevAuthorizer() url = self.make_api_url("auth_requests") # generate device ID from its identity devid = make_devid(d.identity) try: self.accept_device(devid) except bravado.exception.HTTPError as e: assert e.response.status_code == 404 # poke devauth so that device appears rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 try: self.accept_device(devid) except bravado.exception.HTTPError as e: assert e.response.status_code == 200 # device is accepted, we should get a token now rsp = device_auth_req(url, da, d) assert rsp.status_code == 200 da.parse_rsp_payload(d, rsp.text) assert len(d.token) > 0 self.log.info("device token: %s", d.token) # reject it now try: self.reject_device(devid) except bravado.exception.HTTPError as e: assert e.response.status_code == 200 # device is rejected, should get unauthorized rsp = device_auth_req(url, da, d) assert rsp.status_code == 401
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 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 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 request_token(device, dev_auth, url): handlers = [ ('POST', '/api/internal/v1/tenantadm/tenants/verify', lambda _: (200, {}, { 'id': '507f191e810c19729de860ea', 'name': 'Acme', })), ] with mockserver.run_fake(get_fake_tenantadm_addr(), handlers=handlers) as fake: rsp = device_auth_req(url, dev_auth, device) assert rsp.status_code == 200 dev_auth.parse_rsp_payload(device, rsp.text) return device.token
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)
def test_auth_req_fake_tenantadm_invalid_tenant_token(self, management_api, device_api, clean_migrated_db): d = Device() da = DevAuthorizer(tenant_token="bad-token") url = device_api.auth_requests_url 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 for the # default tenant TestMultiTenant.verify_tenant_dev_present(management_api, d.identity, '', present=False)
def test_device_new(self, device_api, management_api, clean_migrated_db): d = Device() da = DevAuthorizer() try: with orchestrator.run_fake_for_device_id(1) as server: rsp = device_auth_req(device_api.auth_requests_url, da, d) assert rsp.status_code == 401 except bravado.exception.HTTPError as e: assert e.response.status_code == 204 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)
def test_auth_req_fake_tenantadm_valid_tenant_token(self): d = Device() da = DevAuthorizer(tenant_token=make_fake_tenant_token( tenant='foobar')) url = self.devapi.make_api_url("/auth_requests") handlers = [ ('POST', '/api/internal/v1/tenantadm/tenants/verify', lambda _: (200, {}, { 'id': '507f191e810c19729de860ea', 'name': 'Acme', })), ] with mockserver.run_fake(get_fake_tenantadm_addr(), handlers=handlers) as fake: with deviceadm.run_fake_for_device(d) as fakedevadm: rsp = device_auth_req(url, da, d) assert rsp.status_code == 401 # device should be appear in devices listing self.verify_tenant_dev_present(d.identity, tenant='foobar')
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 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 # delete our device # use a predetermined request id to correlate with executed workflows self.delete_device(ourdev.id, {'X-MEN-RequestID': 'delete_device'}) found = mc.find_device_by_identity(dev.identity) assert not found # verify workflow was executed cc = ConductorClient() r = cc.get_workflows('decommission_device') assert r.status_code == 200 res = r.json() assert res['totalHits'] == 1 wf = [ x for x in res['results'] if x['input'] == '{device_id=' + ourdev.id + ', request_id=delete_device}' ] assert len(wf) == 1