예제 #1
0
    def init_service_mocks(
        self,
        wflows_rsp_q: asyncio.Queue = None,
        tadm_rsp_q: asyncio.Queue = None,
    ) -> mockserver.MockServer:

        # Very simple tornado request handler for tenantadm and workflows that
        # generates responses from items pushed onto an asyncio.Queue object,
        # the object can either be a callable or a 3-tuple with (code, header,
        # body). Callables are passed 'self' (RequestHandler) as argument.
        class RequestHandler(tornado.web.RequestHandler):
            def initialize(self, rsp_q):
                self.rsp_q = rsp_q

            def prepare(self):
                rsp = self.rsp_q.get_nowait()
                if callable(rsp):
                    rsp(self)
                else:
                    (status, hdr, body) = rsp
                    self.set_status(status)

                    for key, val in hdr.items():
                        self.add_header(key, val)

                    if body:
                        self.write(body)

                self.finish()

        with mockserver.run_fake(get_fake_tenantadm_addr()) as tadm:
            tadm.app.add_handlers(
                r".*",
                [(
                    r".*",
                    RequestHandler,
                    {
                        "rsp_q": tadm_rsp_q
                    },
                )],
            )
            with mockserver.run_fake(get_fake_workflows_addr()) as wflows:
                wflows.app.add_handlers(
                    r".*",
                    [(
                        r".*",
                        RequestHandler,
                        {
                            "rsp_q": wflows_rsp_q
                        },
                    )],
                )
                yield tadm, wflows
예제 #2
0
    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)
예제 #3
0
    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)
예제 #4
0
def run_fake_for_device_id(devid):
    handlers = [
        ('POST', '/api/0.1.0/devices', device_add(devid)),
    ]
    with mockserver.run_fake(get_fake_inventory_addr(),
                             handlers=handlers) as server:
        yield server
예제 #5
0
def run_fake_preauth(id_data, pubkey, ret_status):
    handlers = [('POST', '/api/management/v1/devauth/devices',
                 handler_preauth(id_data, pubkey, ret_status))]

    with mockserver.run_fake(get_fake_deviceauth_addr(),
                             handlers=handlers) as server:
        yield server
예제 #6
0
def run_fake_user_tenants(tenant_users, status='active'):
    """
    Runs GET /tenants?username=<user>. Takes a dict of `{tenant: list of
    users}`, returns valid tenant with `tenant_id` for `user`, otherwise an
    empty list.
    Returned tenant will have the 'status' field set accordingly.

    """
    user_to_tenant = {}
    for tenant, users in tenant_users.items():
        for user in users:
            user_to_tenant[user] = tenant

    def fake_get_tenants_for_user(request):
        # extract username from query parameter
        user_args = request.arguments.get('username', '')
        req_user = user_args[0].decode() if len(user_args) > 0 else ''

        tenant = user_to_tenant.get(req_user, '')
        if tenant:
            return (200, {},
                    json.dumps([{"id": tenant, "status": status, "name": "foo", "tenant_token": "sometoken"}]))
        else:
            return (200, {}, '[]')


    handlers = [
            ('GET', '/api/internal/v1/tenantadm/tenants',
             fake_get_tenants_for_user),
        ]


    with mockserver.run_fake(get_fake_tenantadm_addr(),
                             handlers=handlers) as server:
        yield server
예제 #7
0
def run_fake_create_user(user, status=201):
    handlers = [
        ("POST", "/api/internal/v1/tenantadm/users", fake_create_user(user, status))
    ]

    with mockserver.run_fake(get_fake_tenantadm_addr(), handlers=handlers) as server:
        yield server
예제 #8
0
def run_fake_for_device(device):
    handlers = [
        ('PUT', '/api/0.1.0/devices/(.*)', auth_set_put_for_device(device)),
    ]
    with mockserver.run_fake(get_fake_deviceadm_addr(),
                             handlers=handlers) as server:
        yield server
예제 #9
0
def tenantadm_fake_tenant_verify():
    handlers = [
        ('POST', '/api/internal/v1/tenantadm/tenants/verify', lambda _:
         (200, {}, '')),
    ]
    with mockserver.run_fake(get_fake_tenantadm_addr(),
                             handlers=handlers) as fake:
        yield fake
예제 #10
0
def run_fake_delete_device(devid, aid, ret_status):
    handlers = [
        ('DELETE', '/api/management/v1/devauth/devices/(.*)/auth/(.*)',
         handler_delete_device(devid, aid, ret_status)),
    ]
    with mockserver.run_fake(get_fake_deviceauth_addr(),
                             handlers=handlers) as server:
        yield server
예제 #11
0
def run_fake_delete_user(expected_tenant_id=None, expected_user_id=None):
    handlers = [('DELETE',
                 '/api/internal/v1/tenantadm/tenants/(.*)/users/(.*)',
                 fake_delete_user(expected_tenant_id, expected_user_id))]

    with mockserver.run_fake(get_fake_tenantadm_addr(),
                             handlers=handlers) as server:
        yield server
예제 #12
0
def run_fake_update_authset_status(devid, aid, status, ret_status):
    handlers = [
        ('PUT', '/api/management/v1/devauth/devices/(.*)/auth/(.*)/status',
         handler_update_authset_status(devid, aid, status, ret_status)),
    ]
    with mockserver.run_fake(get_fake_deviceauth_addr(),
                             handlers=handlers) as server:
        yield server
예제 #13
0
def run_fake_get_tenants(tenant_id, status=200):
    """
    Runs just the GET /tenants endpoint for tests that don't need anything more.
    """
    handlers = [
        ("GET", "/api/internal/v1/tenantadm/tenants", fake_get_tenants(tenant_id, 200))
    ]

    with mockserver.run_fake(get_fake_tenantadm_addr(), handlers=handlers) as server:
        yield server
예제 #14
0
def run_fake_for_device_id(devid):
    handlers = [
        ('POST', '/api/workflow/provision_device',
         provision_device_handler(devid)),
        ('POST', '/api/workflow/decommission_device',
         decommission_device_handler(devid)),
    ]
    with mockserver.run_fake(get_fake_orchestrator_addr(),
                             handlers=handlers) as server:
        yield server
예제 #15
0
def run_fake_update_user(tenant_id, id, update, status=204):
    """
    Runs the update endpoint *and* the GET /tenants endpoint - it's packaged
    together because some tests will verify login after an update.
    """
    handlers = [
            ('PUT', '/api/internal/v1/tenantadm/tenants/'+tenant_id+'/users/'+id, fake_update_user(update, status)),
            ('GET', '/api/internal/v1/tenantadm/tenants', fake_get_tenants(tenant_id, 200))
        ]

    with mockserver.run_fake(get_fake_tenantadm_addr(),
                             handlers=handlers) as server:
        yield server
예제 #16
0
def devadm_fake_status_update(authset_id):
    def fake_status_update(request, aid):
        assert aid == authset_id
        return (200, {}, '')

    handlers = [
        ('PUT', '/api/internal/v1/admission/devices/(.*)/status',
         fake_status_update),
    ]

    with mockserver.run_fake(deviceadm.get_fake_deviceadm_addr(),
                             handlers=handlers) as server:
        yield server
예제 #17
0
def run_fake_for_device_id(devid, status=None):
    if status is None:
        handlers = [
            (
                "POST",
                "/api/v1/workflow/provision_device",
                provision_device_handler(devid),
            ),
            (
                "POST",
                "/api/v1/workflow/decommission_device",
                decommission_device_handler(devid),
            ),
            (
                "POST",
                "/api/v1/workflow/update_device_status",
                update_device_status_handler(devid),
            ),
            (
                "POST",
                "/api/v1/workflow/update_device_inventory",
                update_device_inventory_handler(devid),
            ),
        ]
    else:
        handlers = [
            (
                "POST",
                "/api/v1/workflow/provision_device",
                provision_device_handler(devid, status),
            ),
            (
                "POST",
                "/api/v1/workflow/decommission_device",
                decommission_device_handler(devid, status),
            ),
            (
                "POST",
                "/api/v1/workflow/update_device_status",
                update_device_status_handler(devid, status),
            ),
            (
                "POST",
                "/api/v1/workflow/update_device_inventory",
                update_device_inventory_handler(devid, status),
            ),
        ]

    with mockserver.run_fake(get_fake_orchestrator_addr(),
                             handlers=handlers) as server:
        yield server
예제 #18
0
    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
예제 #20
0
    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='')
예제 #21
0
def tenant_foobar_devices(device_api, management_api, tenant_foobar, request):
    """Make unauthorized devices owned by tenant with ID 'foobar'. The fixture can
    be parametrized a number of devices to make. Yields a list of tuples:
    (instance of Device, instance of DevAuthorizer)
    """
    handlers = [
        ('POST', '/api/internal/v1/tenantadm/tenants/verify', lambda _:
         (200, {}, '')),
    ]
    with mockserver.run_fake(get_fake_tenantadm_addr(),
                             handlers=handlers) as fake:

        if not hasattr(request, 'param'):
            devcount = 1
        else:
            devcount = int(request.param)

        yield make_devices(device_api, devcount, tenant_token=tenant_foobar)
예제 #22
0
    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)
예제 #23
0
    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 mock_tenantadm_auth(tenant_addons):
    def tenantadm_handler(req):
        auth = req.headers["Authorization"]
        # jwt = <header (base64)>.<claims (base64)>.<signature (base64)>
        jwt_b64 = auth.split(".")
        if len(jwt_b64) > 1:
            print(jwt_b64)
            # Convert base64 from url- to std-encoding and append padding
            claims_b64 = jwt_b64[1].replace("+", "-").replace("?", "_")
            # Add padding
            claims_b64 += "=" * (-len(claims_b64) % 4)
            # Decode claims
            claims = base64.b64decode(claims_b64)
            d = json.loads(claims)
            tenant_id = d["mender.tenant"]
            return (
                200,
                {},
                {
                    "id":
                    tenant_id,
                    "name":
                    "Acme",
                    "addons": [{
                        "name": addon,
                        "enabled": True
                    } for addon in tenant_addons],
                },
            )
        else:
            return (500, {}, {})

    with mockserver.run_fake(
            get_fake_tenantadm_addr(),
            handlers=[("POST", "/api/internal/v1/tenantadm/tenants/verify",
                       tenantadm_handler)],
    ) as srv:
        yield srv
예제 #25
0
def run_fake_for_device_id(devid, status=None):
    if status is None:
        handlers = [
            ('POST', '/api/v1/workflow/provision_device',
             provision_device_handler(devid)),
            ('POST', '/api/v1/workflow/decommission_device',
             decommission_device_handler(devid)),
            ('POST', '/api/v1/workflow/update_device_status',
             update_device_status_handler(devid)),
        ]
    else:
        handlers = [
            ('POST', '/api/v1/workflow/provision_device',
             provision_device_handler(devid, status)),
            ('POST', '/api/v1/workflow/decommission_device',
             decommission_device_handler(devid, status)),
            ('POST', '/api/v1/workflow/update_device_status',
             update_device_status_handler(devid, status)),
        ]

    with mockserver.run_fake(get_fake_orchestrator_addr(),
                             handlers=handlers) as server:
        yield server
예제 #26
0
    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',
             })),
        ]
        with mockserver.run_fake(get_fake_tenantadm_addr(),
                                 handlers=handlers) as fake:
            rsp = device_auth_req(url, da, d)
            assert rsp.status_code == 401

        # device should be appear in devices listing
        TestMultiTenant.verify_tenant_dev_present(management_api,
                                                  d.identity,
                                                  tenant_foobar,
                                                  present=True)
예제 #27
0
    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

        # handler for orchestrator's job endpoint
        def decommission_device_handler(request):
            dreq = json.loads(request.body.decode())
            print('decommision request', 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 = 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 = management_api.find_device_by_identity(dev.identity)
        assert not found
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