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 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 try: with orchestrator.run_fake_for_device_id(1) as server: # 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 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_token_addons( self, test_case, clean_migrated_db, device_api, management_api, internal_api ): tenant_token = make_fake_tenant_token( "123456789012345678901234", ) dev_auth = DevAuthorizer(tenant_token=tenant_token) jwt = None dev = accept_device(device_api, management_api, tenant_token)[1] with orchestrator.run_fake_for_device_id(1) as server: jwt = request_token( dev, dev_auth, device_api.auth_requests_url, test_case["addons"] ) assert len(jwt) > 0 rsp = requests.post( internal_api.api_url + "tokens/verify", data="", headers={ "Authorization": "Bearer " + jwt, "X-Forwarded-Uri": test_case.get("forwarded_uri"), "X-Forwarded-Method": test_case.get("method"), }, ) assert rsp.status_code == test_case.get("status_code", 200)
def test_device_count_simple(self, devices, management_api): """We have 15 devices, each with a single auth set, verify that accepting/rejecting affects the count""" count = management_api.count_devices() assert count == 15 pending_count = management_api.count_devices(status='pending') assert pending_count == 15 # accept device[0] and reject device[1] for idx, (d, da) in enumerate(devices[0:2]): 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: if idx == 0: management_api.accept_device(devid, aid) elif idx == 1: management_api.reject_device(devid, aid) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 TestDevice.verify_device_count(management_api, 'pending', 13) TestDevice.verify_device_count(management_api, 'accepted', 1) TestDevice.verify_device_count(management_api, 'rejected', 1)
def test_device_limit_applied(self, management_api, internal_api, tenant_foobar_devices, tenant_foobar): """Verify that max accepted devices limit is indeed applied. Since device limits can only be set on per-tenant basis, use fixtures that setup tenant 'foobar' with devices and a token """ expected = 2 internal_api.put_max_devices_limit('foobar', expected) accepted = 0 try: with orchestrator.run_fake_for_device_id(orchestrator.ANY_DEVICE): for dev, dev_auth in tenant_foobar_devices: auth = 'Bearer ' + tenant_foobar fdev = management_api.find_device_by_identity( dev.identity, Authorization=auth) aid = fdev.auth_sets[0].id management_api.accept_device(fdev.id, aid, Authorization=auth) accepted += 1 except bravado.exception.HTTPError as e: assert e.response.status_code == 422 finally: if accepted > expected: pytest.fail( "expected only {} devices to be accepted".format(expected))
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_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 change_status(self, id, device_id, expected_initial, expected_final, expected_error_code=None, auth=None): if auth is None: auth = self.uauth Status = self.client.get_model('Status') s = Status(status=expected_final) try: actual_initial = self.client.devices.get_devices_id( id=id, _request_options={ "headers": auth }).result()[0].status assert actual_initial == expected_initial with orchestrator.run_fake_for_device_id(device_id) as server: self.client.devices.put_devices_id_status(id=id, status=s, _request_options={ "headers": auth }).result() except bravado.exception.HTTPError as e: assert e.response.status_code == expected_error_code return else: if expected_error_code is not None: pytest.fail("Expected an exception, but didnt get any!") return assert self.client.devices.get_devices_id_status( id=id, _request_options={ "headers": auth }).result()[0].status == expected_final
def test_delete_device(self, management_api, devices): # try delete an existing device, verify decommissioning workflow was started # setup single device and poke devauth dev, _ = devices[0] ourdev = management_api.get_single_device() assert ourdev with orchestrator.run_fake_for_device_id(ourdev.id) as server: rsp = management_api.delete_device( ourdev.id, { "X-MEN-RequestID": "delete_device", "Authorization": "Bearer foobar", }, ) print("decommission request finished with status:", rsp.status_code) assert rsp.status_code == 204 found = None status_code = None try: found = management_api.get_device(id=ourdev.id) except bravado.exception.HTTPError as e: status_code = e.response.status_code assert status_code == 404 assert not found
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_delete_tokens_by_non_existent_tenant_ok(self, accepted_tenants_devices, internal_api, management_api, device_api): try: td = accepted_tenants_devices tenant_foo_token = make_fake_tenant_token("foo") da_foo = DevAuthorizer(tenant_token=tenant_foo_token) d1_foo = td["foo"][0] with orchestrator.run_fake_for_device_id(1) as server: token1 = request_token(d1_foo, da_foo, device_api.auth_requests_url) assert len(token1) > 0 d2_foo = td["foo"][1] with orchestrator.run_fake_for_device_id(2) as server: token2 = request_token(d2_foo, da_foo, device_api.auth_requests_url) assert len(token2) > 0 tenant_bar_token = make_fake_tenant_token("bar") da_bar = DevAuthorizer(tenant_token=tenant_bar_token) d1_bar = td["bar"][0] with orchestrator.run_fake_for_device_id(1) as server: token3 = request_token(d1_bar, da_bar, device_api.auth_requests_url) assert len(token2) > 0 verify_url = internal_api.make_api_url("/tokens/verify") verify_token(token1, 200, verify_url) verify_token(token2, 200, verify_url) verify_token(token3, 200, verify_url) dev1 = management_api.find_device_by_identity( d1_foo.identity, Authorization="Bearer " + tenant_foo_token) payload = {"tenant_id": "baz"} rsp = requests.delete(internal_api.make_api_url("/tokens"), params=payload) assert rsp.status_code == 204 verify_token(token1, 200, verify_url) verify_token(token2, 200, verify_url) verify_token(token3, 200, verify_url) except bravado.exception.HTTPError as e: assert e.response.status_code == 204
def test_delete_tokens_by_non_existent_device_ok(self, accepted_tenants_devices, internal_api, management_api, device_api): try: td = accepted_tenants_devices tenant_foo_token = make_fake_tenant_token('foo') da_foo = DevAuthorizer(tenant_token=tenant_foo_token) d1_foo = td['foo'][0] with orchestrator.run_fake_for_device_id(1) as server: token1 = request_token(d1_foo, da_foo, device_api.auth_requests_url) assert len(token1) > 0 d2_foo = td['foo'][1] with orchestrator.run_fake_for_device_id(2) as server: token2 = request_token(d2_foo, da_foo, device_api.auth_requests_url) assert len(token2) > 0 tenant_bar_token = make_fake_tenant_token('bar') da_bar = DevAuthorizer(tenant_token=tenant_bar_token) d1_bar = td['bar'][0] with orchestrator.run_fake_for_device_id(1) as server: token3 = request_token(d1_bar, da_bar, device_api.auth_requests_url) assert len(token2) > 0 verify_url = internal_api.make_api_url("/tokens/verify") verify_token(token1, 200, verify_url) verify_token(token2, 200, verify_url) verify_token(token3, 200, verify_url) payload = {'device_id': 'foo', 'tenant_id': 'foo'} rsp = requests.delete(internal_api.make_api_url("/tokens"), params=payload) assert rsp.status_code == 204 verify_token(token1, 200, verify_url) verify_token(token2, 200, verify_url) verify_token(token3, 200, verify_url) except bravado.exception.HTTPError as e: assert e.response.status_code == 204
def device_token(accepted_device, device_api): devid, d, da = accepted_device try: with orchestrator.run_fake_for_device_id(devid) as server: token = request_token(d, da, device_api.auth_requests_url) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 print("device token:", token) assert token yield token
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_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 test_delete_device_ochestrator_failure(self, management_api, devices): # try delete an existing device, verify it is failing when orchestrator # is failing dev, _ = devices[0] ourdev = management_api.get_single_device() assert ourdev with orchestrator.run_fake_for_device_id(ourdev.id, 500) as server: rsp = management_api.delete_device( ourdev.id, { 'X-MEN-RequestID': 'delete_device', 'Authorization': 'Bearer foobar', }) print('decommission request finished with status:', rsp.status_code) assert rsp.status_code == 500
def make_devices(device_api, devcount=1, tenant_token=""): url = device_api.auth_requests_url out_devices = [] with orchestrator.run_fake_for_device_id(1) as server: for _ in range(devcount): dev = Device() da = DevAuthorizer(tenant_token=tenant_token) # poke devauth so that device appears rsp = device_auth_req(url, da, dev) assert rsp.status_code == 401 out_devices.append((dev, da)) return out_devices
def _test_delete_authset_OK(self, management_api, devices, **kwargs): d, da = devices[0] dev = management_api.find_device_by_identity(d.identity, **kwargs) assert dev print("found matching device with ID:", dev.id) aid = dev.auth_sets[0].id with orchestrator.run_fake_for_device_id(dev.id) as server: rsp = management_api.delete_authset(dev.id, aid, **kwargs) assert rsp.status_code == 204 found = management_api.get_device(id=dev.id, **kwargs) assert found assert len(found.auth_sets) == 0
def test_token_claims(self, accepted_device, management_api, device_api): devid, d, da = accepted_device try: with orchestrator.run_fake_for_device_id(devid) as server: token = request_token(d, da, device_api.auth_requests_url) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 assert len(token) > 0 print("device token:", 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
def test_token_claims(self, accepted_device, management_api, device_api): devid, d, da = accepted_device try: with orchestrator.run_fake_for_device_id(devid) as server: token = request_token(d, da, device_api.auth_requests_url) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 assert len(token) > 0 print("device token:", 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
def test_device_accept_orchestrator_failure(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 status = None try: with orchestrator.run_fake_for_device_id(devid, 500) as server: management_api.accept_device(devid, aid) except bravado.exception.HTTPError as e: status = e.response.status_code assert status == 500
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_delete_device(self, management_api, devices): # try delete an existing device, verify decommissioning workflow was started # setup single device and poke devauth dev, _ = devices[0] ourdev = management_api.find_device_by_identity(dev.identity) assert ourdev try: with orchestrator.run_fake_for_device_id(ourdev.id) as server: rsp = management_api.delete_device( ourdev.id, { 'X-MEN-RequestID': 'delete_device', 'Authorization': 'Bearer foobar', }) print('decommission request finished with status:', rsp.status_code) except bravado.exception.HTTPError as e: assert e.response.status_code == 204 found = management_api.find_device_by_identity(dev.identity) assert not found
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 accepted_tenants_devices(device_api, management_api, clean_migrated_db, cli, request): """Fixture that sets up an accepted devices for tenants. The fixture can be parametrized with a tenants, number of devices and number of authentication sets. Yields a dict: [tenant ID: [device object, ...], ]""" requested = request.param tenants_devices = dict() url = device_api.auth_requests_url for (tenant, dev_count, auth_count) in requested: tenant_devices = [] cli.migrate(tenant=tenant) tenant_token = make_fake_tenant_token(tenant) for _ in range(int(dev_count)): d = Device() for i in range(int(auth_count)): d.rotate_key() da = DevAuthorizer(tenant_token=tenant_token) # poke devauth so that device appears 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 # try to find our devices in all devices listing dev = management_api.find_device_by_identity( d.identity, Authorization="Bearer " + tenant_token) devid = dev.id for a in dev.auth_sets: if compare_keys(a.pubkey, d.public_key): aid = a.id break try: with orchestrator.run_fake_for_device_id(devid) as server: management_api.accept_device(devid, aid, Authorization="Bearer " + tenant_token) token = request_token(d, da, device_api.auth_requests_url) assert len(token) > 0 except bravado.exception.HTTPError as e: assert e.response.status_code == 204 assert dev tenant_devices.append(d) tenants_devices[tenant] = tenant_devices yield tenants_devices