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_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_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 verify_tenant_dev_present(self, identity, present=True, tenant='foobar'): """Assert that device with `identity` is present (or not)""" # request was rejected, device should not be listed mc = SimpleManagementClient() if tenant: token = make_fake_tenant_token(tenant=tenant) dev = mc.find_device_by_identity(identity, Authorization='Bearer ' + token) else: # use default auth dev = mc.find_device_by_identity(identity) if present: assert dev else: assert not dev
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
def test_get_single_device(self): mc = SimpleManagementClient() try: mc.get_device(id='some-devid-foo') except bravado.exception.HTTPError as e: assert e.response.status_code == 404 # 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 # try to find our devices in all devices listing mc = SimpleManagementClient() ourdev = mc.find_device_by_identity(dev.identity) authdev = mc.get_device(id=ourdev.id) assert authdev == ourdev
def management_api(): yield SimpleManagementClient()
def test_token(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 # try to find our devices in all devices listing mc = SimpleManagementClient() dev = mc.find_device_by_identity(d.identity) self.log.debug('found matching device with ID: %s', dev.id) devid = dev.id # extract authentication data set 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 with deviceadm.run_fake_for_device(d) 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 self.log.info("device token: %s", d.token) thdr, tclaims, tsign = explode_jwt(d.token) assert 'typ' in thdr and thdr['typ'] == 'JWT' assert 'jti' in tclaims assert 'exp' in tclaims assert 'sub' in tclaims and tclaims['sub'] == devid assert 'iss' in tclaims and tclaims['iss'] == 'Mender' assert 'mender.device' in tclaims and tclaims['mender.device'] == True # TODO: signature verification? # verify token; the token is to be placed in the Authorization header # and it looks like bravado cannot handle a POST request with no data # in body, hence we fall back to sending request directly verify_url = self.intclient.make_api_url("/tokens/verify") self.log.info("verify URL: %s", verify_url) auth_hdr = 'Bearer {}'.format(d.token) # no auth header should raise an error rsp = requests.post(verify_url, data='') assert rsp.status_code == 401 # successful verification rsp = requests.post(verify_url, data='', headers={'Authorization': auth_hdr}) assert rsp.status_code == 200 # use a bogus token that is not a valid JWT rsp = requests.post(verify_url, data='', headers={'Authorization': 'bogus'}) assert rsp.status_code == 401 # or a correct token with data appended at the end rsp = requests.post(verify_url, data='', headers={'Authorization': auth_hdr + "==foo"}) assert rsp.status_code == 401 # bravado cannot handle DELETE requests either # self.client.tokens.delete_tokens_id(id=tclaims['jti']) # use requests instead rsp = requests.delete( self.make_api_url('/tokens/{}'.format(tclaims['jti']))) assert rsp.status_code == 204 # unsuccessful verification rsp = requests.post(verify_url, data='', headers={'Authorization': auth_hdr}) assert rsp.status_code == 401
def management_api(request): yield SimpleManagementClient( request.config.getoption("--host"), request.config.getoption("--management-spec"), )