Пример #1
0
async def test_user_claim_ok(monkeypatch, backend, anonymous_backend_sock,
                             coolorg, alice, mallory_invitation):
    user_invitation_retreived = trio.Event()

    vanilla_claim_user_invitation = backend.user.claim_user_invitation

    async def _mocked_claim_user_invitation(*args, **kwargs):
        ret = await vanilla_claim_user_invitation(*args, **kwargs)
        user_invitation_retreived.set()
        return ret

    monkeypatch.setattr(backend.user, "claim_user_invitation",
                        _mocked_claim_user_invitation)

    with freeze_time(mallory_invitation.created_on):
        async with user_claim(
                anonymous_backend_sock,
                invited_user_id=mallory_invitation.user_id,
                encrypted_claim=b"<foo>",
        ) as prep:

            # `backend.user.create_user` will destroy the user invitation,
            # so make sure we retreived it before
            await user_invitation_retreived.wait()

            # No the user we are waiting for
            await backend.user.create_user(
                alice.organization_id,
                User(
                    user_id=UserID("dummy"),
                    user_certificate=b"<user certif>",
                    user_certifier=alice.device_id,
                ),
                Device(
                    device_id=DeviceID("dummy@pc1"),
                    device_certificate=b"<device certif>",
                    device_certifier=alice.device_id,
                ),
            )

            await backend.user.create_user(
                alice.organization_id,
                User(
                    user_id=mallory_invitation.user_id,
                    user_certificate=b"<user certif>",
                    user_certifier=alice.device_id,
                ),
                Device(
                    device_id=DeviceID(f"{mallory_invitation.user_id}@pc1"),
                    device_certificate=b"<device certif>",
                    device_certifier=alice.device_id,
                ),
            )

    assert prep[0] == {
        "status": "ok",
        "user_certificate": b"<user certif>",
        "device_certificate": b"<device certif>",
    }
async def test_device_claim_ok(monkeypatch, backend,
                               apiv1_anonymous_backend_sock, alice,
                               alice_nd_invitation):
    device_invitation_retrieved = trio.Event()

    vanilla_claim_device_invitation = backend.user.claim_device_invitation

    async def _mocked_claim_device_invitation(*args, **kwargs):
        ret = await vanilla_claim_device_invitation(*args, **kwargs)
        device_invitation_retrieved.set()
        return ret

    monkeypatch.setattr(backend.user, "claim_device_invitation",
                        _mocked_claim_device_invitation)

    with freeze_time(alice_nd_invitation.created_on):
        async with device_claim(
                apiv1_anonymous_backend_sock,
                invited_device_id=alice_nd_invitation.device_id,
                encrypted_claim=b"<foo>",
        ) as prep:

            # `backend.user.create_device` will destroy the device invitation,
            # so make sure we retrieved it before
            await device_invitation_retrieved.wait()

            # No the device we are waiting for
            await backend.user.create_device(
                alice.organization_id,
                Device(
                    device_id=alice.user_id.to_device_id("dummy"),
                    device_label=None,
                    device_certificate=b"<alice@dummy certificate>",
                    redacted_device_certificate=
                    b"<redacted alice@dummy certificate>",
                    device_certifier=alice.device_id,
                ),
                encrypted_answer=b"<alice@dummy answer>",
            )

            await backend.user.create_device(
                alice.organization_id,
                Device(
                    device_id=alice_nd_invitation.device_id,
                    device_label=None,
                    device_certificate=b"<alice@new_device certificate>",
                    redacted_device_certificate=
                    b"<redacted alice@new_device certificate>",
                    device_certifier=alice.device_id,
                ),
                encrypted_answer=b"<alice@new_device answer>",
            )

    assert prep[0] == {
        "status": "ok",
        "encrypted_answer": b"<alice@new_device answer>",
        "device_certificate": b"<alice@new_device certificate>",
    }
Пример #3
0
async def query_get_user_with_device(
        conn, organization_id: OrganizationID,
        device_id: DeviceID) -> Tuple[User, Device]:
    d_row = await conn.fetchrow(_q_get_device, organization_id, device_id)
    u_row = await conn.fetchrow(_q_get_user, organization_id,
                                device_id.user_id)
    if not u_row or not d_row:
        raise UserNotFoundError(device_id)

    device = Device(
        device_id=device_id,
        device_label=d_row["device_label"],
        device_certificate=d_row["device_certificate"],
        redacted_device_certificate=d_row["redacted_device_certificate"],
        device_certifier=d_row["device_certifier"],
        created_on=d_row["created_on"],
    )
    user = User(
        user_id=device_id.user_id,
        profile=STR_TO_USER_PROFILE[u_row["profile"]],
        user_certificate=u_row["user_certificate"],
        redacted_user_certificate=u_row["redacted_user_certificate"],
        user_certifier=u_row["user_certifier"],
        created_on=u_row["created_on"],
        revoked_on=u_row["revoked_on"],
        revoked_user_certificate=u_row["revoked_user_certificate"],
        revoked_user_certifier=u_row["revoked_user_certifier"],
    )
    return user, device
Пример #4
0
async def test_user_claim_already_exists(mock_clock, backend,
                                         apiv1_anonymous_backend_sock, alice,
                                         mallory_invitation):
    await backend.user.create_user(
        alice.organization_id,
        User(
            user_id=mallory_invitation.user_id,
            human_handle=None,
            user_certificate=b"<foo>",
            redacted_user_certificate=b"<foo>",
            user_certifier=alice.device_id,
        ),
        Device(
            device_id=mallory_invitation.user_id.to_device_id("pc1"),
            device_label=None,
            device_certificate=b"<bar>",
            redacted_device_certificate=b"<bar>",
            device_certifier=alice.device_id,
        ),
    )

    with freeze_time(mallory_invitation.created_on):
        async with user_claim(
                apiv1_anonymous_backend_sock,
                invited_user_id=mallory_invitation.user_id,
                encrypted_claim=b"<foo>",
        ) as prep:

            pass

    assert prep[0] == {"status": "not_found"}
Пример #5
0
async def _get_user_devices(conn, organization_id: OrganizationID,
                            user_id: UserID) -> Tuple[Device]:
    results = await conn.fetch(_q_get_user_devices, organization_id, user_id)

    return tuple(
        Device(
            device_id=DeviceID(row["device_id"]),
            device_certificate=row["device_certificate"],
            device_certifier=row["device_certifier"],
            created_on=row["created_on"],
        ) for row in results)
Пример #6
0
async def test_device_create_ok(backend, backend_sock_factory,
                                alice_backend_sock, alice, alice_nd,
                                with_labels):
    now = pendulum.now()
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=alice_nd.device_id,
        device_label=alice_nd.device_label,
        verify_key=alice_nd.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)
    if not with_labels:
        device_certificate = redacted_device_certificate
    device_certificate = device_certificate.dump_and_sign(alice.signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        alice.signing_key)

    with backend.event_bus.listen() as spy:
        rep = await device_create(
            alice_backend_sock,
            device_certificate=device_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        assert rep == {"status": "ok"}

        # No guarantees this event occurs before the command's return
        await spy.wait_with_timeout(
            BackendEvent.DEVICE_CREATED,
            {
                "organization_id": alice_nd.organization_id,
                "device_id": alice_nd.device_id,
                "device_certificate": device_certificate,
                "encrypted_answer": b"",
            },
        )

    # Make sure the new device can connect now
    async with backend_sock_factory(backend, alice_nd) as sock:
        rep = await ping(sock, ping="Hello world !")
        assert rep == {"status": "ok", "pong": "Hello world !"}

    # Check the resulting data in the backend
    _, backend_device = await backend.user.get_user_with_device(
        alice_nd.organization_id, alice_nd.device_id)
    assert backend_device == Device(
        device_id=alice_nd.device_id,
        device_label=alice_nd.device_label if with_labels else None,
        device_certificate=device_certificate,
        redacted_device_certificate=redacted_device_certificate,
        device_certifier=alice.device_id,
        created_on=now,
    )
Пример #7
0
async def _get_device(conn, organization_id: OrganizationID,
                      device_id: DeviceID) -> Device:
    row = await conn.fetchrow(_q_get_device, organization_id, device_id)
    if not row:
        raise UserNotFoundError(device_id)

    return Device(
        device_id=device_id,
        device_certificate=row["device_certificate"],
        device_certifier=row["device_certifier"],
        created_on=row["created_on"],
    )
Пример #8
0
    async def _get_trustchain(self, conn, organization_id: OrganizationID,
                              user: User) -> Dict[DeviceID, Device]:
        # TODO: it's time to do a super awesome SQL query fetching everything
        # in one go...
        devices = {}
        devices_to_fetch = []
        if user.user_certifier:
            devices_to_fetch.append(user.user_certifier)
        for device in user.devices.values():
            if device.device_certifier:
                devices_to_fetch.append(device.device_certifier)
            if device.revocation_certifier:
                devices_to_fetch.append(device.revocation_certifier)

        while devices_to_fetch:
            results = await conn.fetch(
                """
SELECT
    d1.device_id,
    d1.certified_device,
    (
        SELECT devices.device_id FROM devices WHERE devices._id = d1.device_certifier
    ) AS device_certifier,
    d1.created_on,
    d1.revocated_on,
    d1.certified_revocation,
    (
        SELECT devices.device_id FROM devices WHERE devices._id = d1.revocation_certifier
    ) as revocation_certifier
FROM devices as d1
WHERE
    organization = (
        SELECT _id from organizations WHERE organization_id = $1
    )
    AND device_id = any($2::text[]);
""",
                organization_id,
                devices_to_fetch,
            )

            for result in results:
                devices[result[0]] = Device(*result)

            devices_to_fetch = []
            for device in devices.values():
                if device.device_certifier and device.device_certifier not in devices:
                    devices_to_fetch.append(device.device_certifier)
                if device.revocation_certifier and device.revocation_certifier not in devices:
                    devices_to_fetch.append(device.revocation_certifier)

        return devices
Пример #9
0
async def _get_device(conn, organization_id: OrganizationID,
                      device_id: DeviceID) -> Device:
    row = await conn.fetchrow(*_q_get_device(
        organization_id=organization_id.str, device_id=device_id.str))
    if not row:
        raise UserNotFoundError(device_id)

    return Device(
        device_id=device_id,
        device_label=DeviceLabel(row["device_label"]),
        device_certificate=row["device_certificate"],
        redacted_device_certificate=row["redacted_device_certificate"],
        device_certifier=DeviceID(row["device_certifier"]),
        created_on=row["created_on"],
    )
Пример #10
0
async def _get_user_devices(conn, organization_id: OrganizationID,
                            user_id: UserID) -> Tuple[Device, ...]:
    results = await conn.fetch(*_q_get_user_devices(
        organization_id=organization_id.str, user_id=user_id.str))

    return tuple(
        Device(
            device_id=DeviceID(row["device_id"]),
            device_label=DeviceLabel(row["device_label"]
                                     ) if row["device_label"] else None,
            device_certificate=row["device_certificate"],
            redacted_device_certificate=row["redacted_device_certificate"],
            device_certifier=DeviceID(row["device_certifier"]
                                      ) if row["device_certifier"] else None,
            created_on=row["created_on"],
        ) for row in results)
Пример #11
0
async def query_get_user_with_device(
        conn, organization_id: OrganizationID,
        device_id: DeviceID) -> Tuple[User, Device]:
    d_row = await conn.fetchrow(*_q_get_device(
        organization_id=organization_id.str, device_id=device_id.str))
    u_row = await conn.fetchrow(*_q_get_user(
        organization_id=organization_id.str, user_id=device_id.user_id.str))
    if not u_row or not d_row:
        raise UserNotFoundError(device_id)

    human_handle = None
    if u_row["human_email"]:
        human_handle = HumanHandle(email=u_row["human_email"],
                                   label=u_row["human_label"])

    device = Device(
        device_id=device_id,
        device_label=DeviceLabel(d_row["device_label"])
        if d_row["device_label"] else None,
        device_certificate=d_row["device_certificate"],
        redacted_device_certificate=d_row["redacted_device_certificate"],
        device_certifier=DeviceID(d_row["device_certifier"])
        if d_row["device_certifier"] else None,
        created_on=d_row["created_on"],
    )
    user = User(
        user_id=device_id.user_id,
        human_handle=human_handle,
        profile=UserProfile(u_row["profile"]),
        user_certificate=u_row["user_certificate"],
        redacted_user_certificate=u_row["redacted_user_certificate"],
        user_certifier=DeviceID(u_row["user_certifier"])
        if u_row["user_certifier"] else None,
        created_on=u_row["created_on"],
        revoked_on=u_row["revoked_on"],
        revoked_user_certificate=u_row["revoked_user_certificate"],
        revoked_user_certifier=DeviceID(u_row["revoked_user_certifier"])
        if u_row["revoked_user_certifier"] else None,
    )
    return user, device
Пример #12
0
async def test_device_claim_already_exists(
    mock_clock, backend, anonymous_backend_sock, alice, alice_nd_invitation
):
    await backend.user.create_device(
        alice.organization_id,
        Device(
            device_id=alice_nd_invitation.device_id,
            device_certificate=b"<foo>",
            device_certifier=alice.device_id,
        ),
    )

    with freeze_time(alice_nd_invitation.created_on):
        async with device_claim(
            anonymous_backend_sock,
            invited_device_id=alice_nd_invitation.device_id,
            encrypted_claim=b"<foo>",
        ) as prep:

            pass

    assert prep[0] == {"status": "not_found"}
Пример #13
0
async def test_user_create_ok(
    backend, backend_sock_factory, alice_backend_sock, alice, mallory, profile, with_labels
):
    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        human_handle=mallory.human_handle,
        public_key=mallory.public_key,
        profile=profile,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        device_label=mallory.device_label,
        verify_key=mallory.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)
    if not with_labels:
        user_certificate = redacted_user_certificate
        device_certificate = redacted_device_certificate

    user_certificate = user_certificate.dump_and_sign(alice.signing_key)
    device_certificate = device_certificate.dump_and_sign(alice.signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(alice.signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(alice.signing_key)

    rep = await user_create(
        alice_backend_sock,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=redacted_user_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )
    assert rep == {"status": "ok"}

    # Make sure mallory can connect now
    async with backend_sock_factory(backend, mallory) as sock:
        rep = await user_get(sock, user_id=mallory.user_id)
        assert rep["status"] == "ok"

    # Check the resulting data in the backend
    backend_user, backend_device = await backend.user.get_user_with_device(
        mallory.organization_id, mallory.device_id
    )

    assert backend_user == User(
        user_id=mallory.user_id,
        human_handle=mallory.human_handle if with_labels else None,
        profile=profile,
        user_certificate=user_certificate,
        redacted_user_certificate=redacted_user_certificate,
        user_certifier=alice.device_id,
        created_on=now,
    )
    assert backend_device == Device(
        device_id=mallory.device_id,
        device_label=mallory.device_label if with_labels else None,
        device_certificate=device_certificate,
        redacted_device_certificate=redacted_device_certificate,
        device_certifier=alice.device_id,
        created_on=now,
    )
Пример #14
0
    async def api_organization_bootstrap(self, client_ctx, msg):
        msg = apiv1_organization_bootstrap_serializer.req_load(msg)
        bootstrap_token = msg["bootstrap_token"]
        root_verify_key = msg["root_verify_key"]

        try:
            u_data = UserCertificateContent.verify_and_load(
                msg["user_certificate"],
                author_verify_key=root_verify_key,
                expected_author=None)
            d_data = DeviceCertificateContent.verify_and_load(
                msg["device_certificate"],
                author_verify_key=root_verify_key,
                expected_author=None)

            ru_data = rd_data = None
            if "redacted_user_certificate" in msg:
                ru_data = UserCertificateContent.verify_and_load(
                    msg["redacted_user_certificate"],
                    author_verify_key=root_verify_key,
                    expected_author=None,
                )
            if "redacted_device_certificate" in msg:
                rd_data = DeviceCertificateContent.verify_and_load(
                    msg["redacted_device_certificate"],
                    author_verify_key=root_verify_key,
                    expected_author=None,
                )

        except DataError as exc:
            return {
                "status": "invalid_certification",
                "reason": f"Invalid certification data ({exc}).",
            }
        if u_data.profile != UserProfile.ADMIN:
            return {
                "status": "invalid_data",
                "reason": "Bootstrapping user must have admin profile.",
            }

        if u_data.timestamp != d_data.timestamp:
            return {
                "status":
                "invalid_data",
                "reason":
                "Device and user certificates must have the same timestamp.",
            }

        if u_data.user_id != d_data.device_id.user_id:
            return {
                "status": "invalid_data",
                "reason": "Device and user must have the same user ID.",
            }

        now = pendulum.now()
        if not timestamps_in_the_ballpark(u_data.timestamp, now):
            return {
                "status": "invalid_certification",
                "reason": f"Invalid timestamp in certification.",
            }

        if ru_data:
            if ru_data.evolve(human_handle=u_data.human_handle) != u_data:
                return {
                    "status":
                    "invalid_data",
                    "reason":
                    "Redacted User certificate differs from User certificate.",
                }
            if ru_data.human_handle:
                return {
                    "status":
                    "invalid_data",
                    "reason":
                    "Redacted User certificate must not contain a human_handle field.",
                }

        if rd_data:
            if rd_data.evolve(device_label=d_data.device_label) != d_data:
                return {
                    "status":
                    "invalid_data",
                    "reason":
                    "Redacted Device certificate differs from Device certificate.",
                }
            if rd_data.device_label:
                return {
                    "status":
                    "invalid_data",
                    "reason":
                    "Redacted Device certificate must not contain a device_label field.",
                }

        if (rd_data and not ru_data) or (ru_data and not rd_data):
            return {
                "status":
                "invalid_data",
                "reason":
                "Redacted user&device certificate muste be provided together",
            }

        user = User(
            user_id=u_data.user_id,
            human_handle=u_data.human_handle,
            profile=u_data.profile,
            user_certificate=msg["user_certificate"],
            redacted_user_certificate=msg.get("redacted_user_certificate",
                                              msg["user_certificate"]),
            user_certifier=u_data.author,
            created_on=u_data.timestamp,
        )
        first_device = Device(
            device_id=d_data.device_id,
            device_label=d_data.device_label,
            device_certificate=msg["device_certificate"],
            redacted_device_certificate=msg.get("redacted_device_certificate",
                                                msg["device_certificate"]),
            device_certifier=d_data.author,
            created_on=d_data.timestamp,
        )
        try:
            await self.bootstrap(client_ctx.organization_id, user,
                                 first_device, bootstrap_token,
                                 root_verify_key)

        except OrganizationAlreadyBootstrappedError:
            return {"status": "already_bootstrapped"}

        except (OrganizationNotFoundError,
                OrganizationInvalidBootstrapTokenError):
            return {"status": "not_found"}

        # Note: we let OrganizationFirstUserCreationError bobbles up given
        # it should not occurs under normal circumstances

        # Finally notify webhook
        await self.webhooks.on_organization_bootstrap(
            organization_id=client_ctx.organization_id,
            device_id=first_device.device_id,
            device_label=first_device.device_label,
            human_email=user.human_handle.email if user.human_handle else None,
            human_label=user.human_handle.label if user.human_handle else None,
        )

        return apiv1_organization_bootstrap_serializer.rep_dump(
            {"status": "ok"})
Пример #15
0
async def test_user_create_ok(backend, apiv1_backend_sock_factory, alice,
                              mallory, profile):
    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        human_handle=None,
        public_key=mallory.public_key,
        profile=profile,
    ).dump_and_sign(alice.signing_key)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        device_label=None,
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)

    with backend.event_bus.listen() as spy:
        async with apiv1_backend_sock_factory(backend, alice) as sock:
            rep = await user_create(sock,
                                    user_certificate=user_certificate,
                                    device_certificate=device_certificate)
        assert rep == {"status": "ok"}

        # No guarantees this event occurs before the command's return
        await spy.wait_with_timeout(
            BackendEvent.USER_CREATED,
            {
                "organization_id": alice.organization_id,
                "user_id": mallory.user_id,
                "first_device_id": mallory.device_id,
                "user_certificate": user_certificate,
                "first_device_certificate": device_certificate,
            },
        )

    # Make sure mallory can connect now
    async with apiv1_backend_sock_factory(backend, mallory) as sock:
        rep = await user_get(sock, user_id=mallory.user_id)
        assert rep["status"] == "ok"

    # Check the resulting data in the backend
    backend_user, backend_device = await backend.user.get_user_with_device(
        mallory.organization_id, mallory.device_id)
    assert backend_user == User(
        user_id=mallory.user_id,
        human_handle=None,
        profile=profile,
        user_certificate=user_certificate,
        redacted_user_certificate=user_certificate,
        user_certifier=alice.device_id,
        created_on=now,
    )
    assert backend_device == Device(
        device_id=mallory.device_id,
        device_label=None,
        device_certificate=device_certificate,
        redacted_device_certificate=device_certificate,
        device_certifier=alice.device_id,
        created_on=now,
    )
Пример #16
0
    async def _get_user(self, conn, organization_id: OrganizationID,
                        user_id: UserID) -> User:
        user_result = await conn.fetchrow(
            """
SELECT
    is_admin,
    certified_user,
    (SELECT device_id FROM devices WHERE _id = user_certifier),
    created_on
FROM users
WHERE
    organization = (
        SELECT _id from organizations WHERE organization_id = $1
    )
    AND user_id = $2
""",
            organization_id,
            user_id,
        )
        if not user_result:
            raise UserNotFoundError(user_id)

        devices_results = await conn.fetch(
            """
SELECT
    d1.device_id,
    d1.certified_device,
    (
        SELECT devices.device_id FROM devices WHERE devices._id = d1.device_certifier
    ) AS device_certifier,
    d1.created_on,
    d1.revocated_on,
    d1.certified_revocation,
    (
        SELECT devices.device_id FROM devices WHERE devices._id = d1.revocation_certifier
    ) as revocation_certifier
FROM devices as d1
WHERE user_ = (
    SELECT _id FROM users WHERE
        organization = (
            SELECT _id from organizations WHERE organization_id = $1
        )
        AND user_id = $2
);
""",
            organization_id,
            user_id,
        )
        devices = DevicesMapping(*[
            Device(DeviceID(device_result[0]), *device_result[1:])
            for device_result in devices_results
        ])

        return User(
            user_id=UserID(user_id),
            is_admin=user_result[0],
            certified_user=user_result[1],
            user_certifier=user_result[2],
            created_on=user_result[3],
            devices=devices,
        )