Exemple #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>",
    }
Exemple #2
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
Exemple #3
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"}
Exemple #4
0
async def _get_user(conn, organization_id: OrganizationID,
                    user_id: UserID) -> User:
    row = await conn.fetchrow(
        *_q_get_user(organization_id=organization_id.str, user_id=user_id.str))
    if not row:
        raise UserNotFoundError(user_id)

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

    return User(
        user_id=user_id,
        human_handle=human_handle,
        profile=UserProfile(row["profile"]),
        user_certificate=row["user_certificate"],
        redacted_user_certificate=row["redacted_user_certificate"],
        user_certifier=DeviceID(row["user_certifier"])
        if row["user_certifier"] else None,
        created_on=row["created_on"],
        revoked_on=row["revoked_on"],
        revoked_user_certificate=row["revoked_user_certificate"],
        revoked_user_certifier=DeviceID(row["revoked_user_certifier"])
        if row["revoked_user_certifier"] else None,
    )
Exemple #5
0
async def _get_user(conn, organization_id: OrganizationID,
                    user_id: UserID) -> User:
    row = await conn.fetchrow(
        *_q_get_user(organization_id=organization_id, user_id=user_id))
    if not row:
        raise UserNotFoundError(user_id)

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

    return User(
        user_id=user_id,
        human_handle=human_handle,
        profile=STR_TO_USER_PROFILE[row["profile"]],
        user_certificate=row["user_certificate"],
        redacted_user_certificate=row["redacted_user_certificate"],
        user_certifier=row["user_certifier"],
        created_on=row["created_on"],
        revoked_on=row["revoked_on"],
        revoked_user_certificate=row["revoked_user_certificate"],
        revoked_user_certifier=row["revoked_user_certifier"],
    )
Exemple #6
0
async def _get_user(conn, organization_id: OrganizationID,
                    user_id: UserID) -> User:
    row = await conn.fetchrow(_q_get_user, organization_id, user_id)
    if not row:
        raise UserNotFoundError(user_id)

    return User(
        user_id=user_id,
        is_admin=row["is_admin"],
        user_certificate=row["user_certificate"],
        user_certifier=row["user_certifier"],
        created_on=row["created_on"],
        revoked_on=row["revoked_on"],
        revoked_user_certificate=row["revoked_user_certificate"],
        revoked_user_certifier=row["revoked_user_certifier"],
    )
Exemple #7
0
async def _get_user(conn, organization_id: OrganizationID,
                    user_id: UserID) -> User:
    row = await conn.fetchrow(_q_get_user, organization_id, user_id)
    if not row:
        raise UserNotFoundError(user_id)

    return User(
        user_id=user_id,
        profile=STR_TO_USER_PROFILE[row["profile"]],
        user_certificate=row["user_certificate"],
        redacted_user_certificate=row["redacted_user_certificate"],
        user_certifier=row["user_certifier"],
        created_on=row["created_on"],
        revoked_on=row["revoked_on"],
        revoked_user_certificate=row["revoked_user_certificate"],
        revoked_user_certifier=row["revoked_user_certifier"],
    )
Exemple #8
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
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,
    )
Exemple #10
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"})
Exemple #11
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,
    )
Exemple #12
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,
        )