示例#1
0
        def new_user_and_device(self, is_admin, certifier_id, certifier_key):
            device_id = self.next_device_id()

            local_device = local_device_factory(device_id, org=coolorg)
            self.local_devices[device_id] = local_device

            user = UserCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                user_id=local_device.user_id,
                public_key=local_device.public_key,
                is_admin=is_admin,
            )
            self.users_content[device_id.user_id] = user
            self.users_certifs[device_id.user_id] = user.dump_and_sign(
                certifier_key)

            device = DeviceCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                device_id=local_device.device_id,
                verify_key=local_device.verify_key,
            )
            self.devices_content[local_device.device_id] = device
            self.devices_certifs[
                local_device.device_id] = device.dump_and_sign(certifier_key)

            return device_id
        def new_user_and_device(self, is_admin, certifier_id, certifier_key):
            device_id = self.next_device_id()

            local_device = local_device_factory(device_id, org=coolorg)
            self.local_devices[device_id] = local_device

            user = UserCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                user_id=local_device.user_id,
                human_handle=local_device.human_handle,
                public_key=local_device.public_key,
                profile=UserProfile.ADMIN
                if is_admin else UserProfile.STANDARD,
            )
            self.users_content[device_id.user_id] = user
            self.users_certifs[device_id.user_id] = user.dump_and_sign(
                certifier_key)

            device = DeviceCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                device_id=local_device.device_id,
                device_label=local_device.device_label,
                verify_key=local_device.verify_key,
            )
            self.devices_content[local_device.device_id] = device
            self.devices_certifs[
                local_device.device_id] = device.dump_and_sign(certifier_key)

            return device_id
示例#3
0
async def query_find(conn, organization_id: OrganizationID, query: str,
                     page: int, per_page: int,
                     omit_revoked: bool) -> Tuple[List[UserID], int]:
    if query:
        try:
            UserID(query)
        except ValueError:
            # Contains invalid caracters, no need to go further
            return ([], 0)

        if omit_revoked:
            q = _q_factory(query=True, omit_revoked=True)
            args = (organization_id, query, pendulum_now())

        else:
            q = _q_factory(query=True, omit_revoked=False)
            args = (organization_id, query)

    else:

        if omit_revoked:
            q = _q_factory(query=False, omit_revoked=True)
            args = (organization_id, pendulum_now())

        else:
            q = _q_factory(query=False, omit_revoked=False)
            args = (organization_id, )

    all_results = await conn.fetch(q, *args)
    # TODO: should user LIMIT and OFFSET in the SQL query instead
    results = [
        UserID(x[0])
        for x in all_results[(page - 1) * per_page:page * per_page]
    ]
    return results, len(all_results)
示例#4
0
async def test_start_bad_per_participant_message(
    backend, alice_backend_sock, alice, bob, adam, realm
):
    # Bob used to be part of the realm
    await backend.realm.update_roles(
        alice.organization_id,
        RealmGrantedRole(
            certificate=b"<dummy>",
            realm_id=realm,
            user_id=bob.user_id,
            role=RealmRole.READER,
            granted_by=alice.device_id,
        ),
    )
    await backend.realm.update_roles(
        alice.organization_id,
        RealmGrantedRole(
            certificate=b"<dummy>",
            realm_id=realm,
            user_id=bob.user_id,
            role=None,
            granted_by=alice.device_id,
        ),
    )
    # Adam is still part of the realm, but is revoked
    await backend.realm.update_roles(
        alice.organization_id,
        RealmGrantedRole(
            certificate=b"<dummy>",
            realm_id=realm,
            user_id=adam.user_id,
            role=RealmRole.READER,
            granted_by=alice.device_id,
        ),
    )
    await backend.user.revoke_user(
        alice.organization_id,
        adam.user_id,
        revoked_user_certificate=b"<dummy>",
        revoked_user_certifier=alice.device_id,
    )

    for msg in [
        {},
        {alice.user_id: b"ok", bob.user_id: b"bad"},
        {alice.user_id: b"ok", "zack": b"bad"},
        {alice.user_id: b"ok", adam.user_id: b"bad"},
    ]:
        rep = await realm_start_reencryption_maintenance(
            alice_backend_sock, realm, 2, pendulum_now(), msg, check_rep=False
        )
        assert rep == {
            "status": "participants_mismatch",
            "reason": "Realm participants and message recipients mismatch",
        }

    # Finally make sure the reencryption is possible
    await realm_start_reencryption_maintenance(
        alice_backend_sock, realm, 2, pendulum_now(), {alice.user_id: b"ok"}
    )
示例#5
0
文件: find.py 项目: bitlogik/guardata
async def query_find(conn, organization_id: OrganizationID, query: str,
                     page: int, per_page: int,
                     omit_revoked: bool) -> Tuple[List[UserID], int]:
    if page > 1:
        offset = ((page - 1) * per_page) - 1
    else:
        offset = 0
    if query:
        try:
            UserID(query)
        except ValueError:
            # Contains invalid caracters, no need to go further
            return ([], 0)

        if omit_revoked:
            q = _q_factory(query=True,
                           omit_revoked=True,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id,
                     query="%" + query + "%",
                     now=pendulum_now())

        else:
            q = _q_factory(query=True,
                           omit_revoked=False,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id, query="%" + query + "%")

    else:

        if omit_revoked:
            q = _q_factory(query=False,
                           omit_revoked=True,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id, now=pendulum_now())

        else:
            q = _q_factory(query=False,
                           omit_revoked=False,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id)

    all_results = [user["user_id"] for user in await conn.fetch(*args)]
    q = _q_count_total_human(query,
                             omit_revoked=omit_revoked,
                             omit_non_human=True,
                             in_find=True)
    if omit_revoked:
        total = await conn.fetchrow(
            *q(organization_id=organization_id, now=pendulum_now()))
    else:
        total = await conn.fetchrow(*q(organization_id=organization_id))
    return all_results, total[0]
示例#6
0
async def test_start_check_access_rights(
    backend, bob_backend_sock, alice, bob, realm, next_timestamp
):
    # User not part of the realm
    rep = await realm_start_reencryption_maintenance(
        bob_backend_sock, realm, 2, pendulum_now(), {alice.user_id: b"wathever"}, check_rep=False
    )
    assert rep == {"status": "not_allowed"}

    # User part of the realm with various role
    for not_allowed_role in (RealmRole.READER, RealmRole.CONTRIBUTOR, RealmRole.MANAGER, None):
        await backend.realm.update_roles(
            alice.organization_id,
            RealmGrantedRole(
                certificate=b"<dummy>",
                realm_id=realm,
                user_id=bob.user_id,
                role=not_allowed_role,
                granted_by=alice.device_id,
                granted_on=next_timestamp(),
            ),
        )

        rep = await realm_start_reencryption_maintenance(
            bob_backend_sock,
            realm,
            2,
            next_timestamp(),
            {alice.user_id: b"foo", bob.user_id: b"bar"},
            check_rep=False,
        )
        assert rep == {"status": "not_allowed"}

    # Finally, just make sure owner can do it
    await backend.realm.update_roles(
        alice.organization_id,
        RealmGrantedRole(
            certificate=b"<dummy>",
            realm_id=realm,
            user_id=bob.user_id,
            role=RealmRole.OWNER,
            granted_by=alice.device_id,
            granted_on=next_timestamp(),
        ),
    )

    rep = await realm_start_reencryption_maintenance(
        bob_backend_sock,
        realm,
        2,
        pendulum_now(),
        {alice.user_id: b"foo", bob.user_id: b"bar"},
        check_rep=False,
    )
    assert rep == {"status": "ok"}
示例#7
0
async def _do_process_authenticated_answer(
    backend, transport: Transport, handshake: ServerHandshake, handshake_type
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:

    organization_id = handshake.answer_data["organization_id"]
    device_id = handshake.answer_data["device_id"]
    expected_rvk = handshake.answer_data["rvk"]

    def _make_error_infos(reason):
        return {
            "reason": reason,
            "handshake_type": handshake_type,
            "organization_id": organization_id,
            "device_id": device_id,
        }

    try:
        organization = await backend.organization.get(organization_id)
        user, device = await backend.user.get_user_with_device(
            organization_id, device_id)

    except (OrganizationNotFoundError, UserNotFoundError, KeyError) as exc:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos(str(exc))

    if organization.root_verify_key != expected_rvk:
        result_req = handshake.build_rvk_mismatch_result_req()
        return None, result_req, _make_error_infos("Bad root verify key")

    if organization.expiration_date is not None and organization.expiration_date <= pendulum_now(
    ):
        result_req = handshake.build_organization_expired_result_req()
        return None, result_req, _make_error_infos("Expired organization")

    if user.revoked_on and user.revoked_on <= pendulum_now():
        result_req = handshake.build_revoked_device_result_req()
        return None, result_req, _make_error_infos("Revoked device")

    context = AuthenticatedClientContext(
        transport=transport,
        handshake=handshake,
        organization_id=organization_id,
        device_id=device_id,
        human_handle=user.human_handle,
        profile=user.profile,
        public_key=user.public_key,
        verify_key=device.verify_key,
    )
    result_req = handshake.build_result_req(device.verify_key)
    return context, result_req, None
示例#8
0
async def query_find(
    conn,
    organization_id: OrganizationID,
    page: int = 1,
    per_page: int = 100,
    omit_revoked: bool = False,
    query: Optional[str] = None,
) -> Tuple[List[UserID], int]:
    if page >= 1:
        offset = (page - 1) * per_page
    else:
        return ([], 0)
    if query:
        try:
            UserID(query)
        except ValueError:
            # Contains invalid caracters, no need to go further
            return ([], 0)

    q = _q_factory(with_query=bool(query), omit_revoked=omit_revoked)
    if query:
        if omit_revoked:
            args = q(
                organization_id=organization_id,
                now=pendulum_now(),
                query=query,
                offset=offset,
                limit=per_page,
            )
        else:
            args = q(organization_id=organization_id,
                     query=query,
                     offset=offset,
                     limit=per_page)
    else:
        if omit_revoked:
            args = q(organization_id=organization_id,
                     now=pendulum_now(),
                     offset=offset,
                     limit=per_page)
        else:
            args = q(organization_id=organization_id,
                     offset=offset,
                     limit=per_page)

    raw_results = await conn.fetch(*args)
    total = raw_results[0]["total"]
    results = [UserID(x["user_id"]) for x in raw_results[1:]]

    return results, total
示例#9
0
async def test_start_already_in_maintenance(alice_backend_sock, realm):
    await realm_start_reencryption_maintenance(
        alice_backend_sock, realm, 2, pendulum_now(), {"alice": b"wathever"}
    )
    # Providing good or bad encryption revision shouldn't change anything
    for encryption_revision in (2, 3):
        rep = await realm_start_reencryption_maintenance(
            alice_backend_sock,
            realm,
            encryption_revision,
            pendulum_now(),
            {"alice": b"wathever"},
            check_rep=False,
        )
        assert rep == {"status": "in_maintenance"}
示例#10
0
文件: find.py 项目: bitlogik/guardata
async def query_retrieve_active_human_by_email(conn,
                                               organization_id: OrganizationID,
                                               email: str) -> Optional[UserID]:
    result = await conn.fetchrow(*_q_retrieve_active_human_by_email(
        organization_id=organization_id, now=pendulum_now(), email=email))
    if result:
        return UserID(result["user_id"])
示例#11
0
async def _apiv1_process_anonymous_answer(
    backend, transport: Transport, handshake: ServerHandshake
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:
    organization_id = handshake.answer_data["organization_id"]
    expected_rvk = handshake.answer_data["rvk"]

    def _make_error_infos(reason):
        return {
            "reason": reason,
            "handshake_type": APIV1_HandshakeType.ANONYMOUS,
            "organization_id": organization_id,
        }

    try:
        organization = await backend.organization.get(organization_id)

    except OrganizationNotFoundError:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad organization")

    if organization.expiration_date is not None and organization.expiration_date <= pendulum_now():
        result_req = handshake.build_organization_expired_result_req()
        return None, result_req, _make_error_infos("Expired organization")

    if expected_rvk and organization.root_verify_key != expected_rvk:
        result_req = handshake.build_rvk_mismatch_result_req()
        return None, result_req, _make_error_infos("Bad root verify key")

    context = APIV1_AnonymousClientContext(transport, handshake, organization_id=organization_id)
    result_req = handshake.build_result_req()
    return context, result_req, None
示例#12
0
async def test_status(backend, bob_backend_sock, alice_backend_sock, alice, bob, realm):
    rep = await realm_status(alice_backend_sock, realm)
    assert rep == {
        "status": "ok",
        "in_maintenance": False,
        "maintenance_type": None,
        "maintenance_started_by": None,
        "maintenance_started_on": None,
        "encryption_revision": 1,
    }
    # Cheap test on no access
    rep = await realm_status(bob_backend_sock, realm)
    assert rep == {"status": "not_allowed"}
    # Also test lesser role have access
    await realm_update_roles(
        alice_backend_sock,
        RealmRoleCertificateContent(
            author=alice.device_id,
            timestamp=pendulum_now(),
            realm_id=realm,
            user_id=bob.user_id,
            role=RealmRole.READER,
        ).dump_and_sign(alice.signing_key),
    )
    rep = await realm_status(bob_backend_sock, realm)
    assert rep == {
        "status": "ok",
        "in_maintenance": False,
        "maintenance_type": None,
        "maintenance_started_by": None,
        "maintenance_started_on": None,
        "encryption_revision": 1,
    }
示例#13
0
    async def new_for_user(
        self,
        organization_id: OrganizationID,
        greeter_user_id: UserID,
        claimer_email: str,
        created_on: Optional[DateTime] = None,
    ) -> UserInvitation:
        """
        Raise: InvitationAlreadyMemberError
        """
        created_on = created_on or pendulum_now()
        async with self.dbh.pool.acquire() as conn, conn.transaction():
            user_id = await query_retrieve_active_human_by_email(
                conn, organization_id, claimer_email
            )
            if user_id:
                raise InvitationAlreadyMemberError()

            token = await _do_new_user_invitation(
                conn,
                organization_id=organization_id,
                greeter_user_id=greeter_user_id,
                claimer_email=claimer_email,
                created_on=created_on,
            )
        return UserInvitation(
            greeter_user_id=greeter_user_id,
            greeter_human_handle=None,
            claimer_email=claimer_email,
            token=token,
            created_on=created_on,
        )
示例#14
0
 def new_placeholder(
     cls,
     author: DeviceID,
     parent: EntryID,
     id: Optional[EntryID] = None,
     now: DateTime = None,
     blocksize=DEFAULT_BLOCK_SIZE,
 ) -> "LocalFileManifest":
     now = now or pendulum_now()
     blocks = ()
     return cls(
         base=RemoteFileManifest(
             author=author,
             timestamp=now,
             id=id or EntryID.new(),
             parent=parent,
             version=0,
             created=now,
             updated=now,
             blocksize=blocksize,
             size=0,
             blocks=blocks,
         ),
         need_sync=True,
         updated=now,
         blocksize=blocksize,
         size=0,
         blocks=blocks,
     )
示例#15
0
    async def api_pki_enrollment_reject(self,
                                        client_ctx: AuthenticatedClientContext,
                                        msg: dict) -> dict:
        if client_ctx.profile != UserProfile.ADMIN:
            return {
                "status": "not_allowed",
                "reason":
                f"User `{client_ctx.device_id.user_id}` is not admin",
            }
        msg = pki_enrollment_reject_serializer.req_load(msg)
        try:
            await self.reject(
                organization_id=client_ctx.organization_id,
                enrollment_id=msg["enrollment_id"],
                rejected_on=pendulum_now(),
            )
            rep = {"status": "ok"}

        except PkiEnrollmentNotFoundError as exc:
            rep = {"status": "not_found", "reason": str(exc)}

        except PkiEnrollmentNoLongerAvailableError as exc:
            rep = {"status": "no_longer_available", "reason": str(exc)}

        return pki_enrollment_reject_serializer.rep_dump(rep)
示例#16
0
async def test_reencryption_events(backend, alice, alice_backend_sock,
                                   alice2_backend_sock, realm, vlobs,
                                   vlob_atoms):

    # Start listening events
    await events_subscribe(alice_backend_sock)

    with backend.event_bus.listen() as spy:
        # Start maintenance and check for events
        await realm_start_reencryption_maintenance(alice2_backend_sock, realm,
                                                   2, pendulum_now(),
                                                   {"alice": b"foo"})

        with trio.fail_after(1):
            # No guarantees those events occur before the commands' return
            await spy.wait_multiple([
                BackendEvent.REALM_MAINTENANCE_STARTED,
                BackendEvent.MESSAGE_RECEIVED
            ])

        rep = await events_listen_nowait(alice_backend_sock)
        assert rep == {
            "status": "ok",
            "event": APIEvent.REALM_MAINTENANCE_STARTED,
            "realm_id": realm,
            "encryption_revision": 2,
        }
        rep = await events_listen_nowait(alice_backend_sock)
        assert rep == {
            "status": "ok",
            "event": APIEvent.MESSAGE_RECEIVED,
            "index": 1
        }

        # Do the reencryption
        rep = await vlob_maintenance_get_reencryption_batch(alice_backend_sock,
                                                            realm,
                                                            2,
                                                            size=100)
        await vlob_maintenance_save_reencryption_batch(alice_backend_sock,
                                                       realm, 2, rep["batch"])

        # Finish maintenance and check for events
        await realm_finish_reencryption_maintenance(alice2_backend_sock, realm,
                                                    2)

        # No guarantees those events occur before the commands' return
        await spy.wait_with_timeout(BackendEvent.REALM_MAINTENANCE_FINISHED)

        rep = await events_listen_nowait(alice_backend_sock)
        assert rep == {
            "status": "ok",
            "event": APIEvent.REALM_MAINTENANCE_FINISHED,
            "realm_id": realm,
            "encryption_revision": 2,
        }

    # Sanity check
    rep = await events_listen_nowait(alice_backend_sock)
    assert rep == {"status": "no_events"}
示例#17
0
async def test_claim_device_already_deleted(aqtbot, running_backend, backend,
                                            autoclose_dialog, alice, gui):

    invitation = await backend.invite.new_for_device(
        organization_id=alice.organization_id, greeter_user_id=alice.user_id)
    invitation_addr = BackendInvitationAddr.build(
        backend_addr=alice.organization_addr.get_backend_addr(),
        organization_id=alice.organization_id,
        invitation_type=InvitationType.DEVICE,
        token=invitation.token,
    )
    await backend.invite.delete(
        organization_id=alice.organization_id,
        greeter=alice.user_id,
        token=invitation_addr.token,
        on=pendulum_now(),
        reason=InvitationDeletedReason.CANCELLED,
    )

    gui.add_instance(invitation_addr.to_url())

    def _assert_dialogs():
        assert len(autoclose_dialog.dialogs) == 1
        assert autoclose_dialog.dialogs == [
            ("Error", translate("TEXT_INVITATION_ALREADY_USED"))
        ]

    await aqtbot.wait_until(_assert_dialogs)
示例#18
0
async def test_backend_close_on_user_revoke(backend, alice_backend_sock,
                                            backend_sock_factory, bob, alice):
    now = pendulum_now()
    bob_revocation = RevokedUserCertificateContent(
        author=alice.device_id, timestamp=now,
        user_id=bob.user_id).dump_and_sign(alice.signing_key)

    async with backend_sock_factory(
            backend, bob, freeze_on_transport_error=False) as bob_backend_sock:
        with backend.event_bus.listen() as spy:
            rep = await user_revoke(alice_backend_sock,
                                    revoked_user_certificate=bob_revocation)
            assert rep == {"status": "ok"}
            await spy.wait_with_timeout(
                BackendEvent.USER_REVOKED,
                {
                    "organization_id": bob.organization_id,
                    "user_id": bob.user_id
                },
            )
            # `user.revoked` event schedules connection cancellation, so wait
            # for things to settle down to make sure the cancellation is done
            await trio.testing.wait_all_tasks_blocked()
        # Bob cannot send new command
        with pytest.raises(TransportError):
            await ping(bob_backend_sock)
示例#19
0
 async def new_for_user(
     self,
     organization_id: OrganizationID,
     greeter_user_id: UserID,
     claimer_email: str,
     created_on: Optional[DateTime] = None,
 ) -> UserInvitation:
     """
     Raise: Nothing
     """
     created_on = created_on or pendulum_now()
     async with self.dbh.pool.acquire() as conn, conn.transaction():
         token = await _do_new_user_invitation(
             conn,
             organization_id=organization_id,
             greeter_user_id=greeter_user_id,
             claimer_email=claimer_email,
             created_on=created_on,
         )
     return UserInvitation(
         greeter_user_id=greeter_user_id,
         greeter_human_handle=None,
         claimer_email=claimer_email,
         token=token,
         created_on=created_on,
     )
示例#20
0
async def test_start_send_message_to_participants(backend, alice, bob,
                                                  alice_backend_sock,
                                                  bob_backend_sock, realm):
    await backend.realm.update_roles(
        alice.organization_id,
        RealmGrantedRole(
            certificate=b"<dummy>",
            realm_id=realm,
            user_id=bob.user_id,
            role=RealmRole.READER,
            granted_by=alice.device_id,
        ),
    )

    with freeze_time("2000-01-02"):
        await realm_start_reencryption_maintenance(alice_backend_sock, realm,
                                                   2, pendulum_now(), {
                                                       "alice": b"alice msg",
                                                       "bob": b"bob msg"
                                                   })

    # Each participant should have received a message
    for user, sock in ((alice, alice_backend_sock), (bob, bob_backend_sock)):
        rep = await message_get(sock)
        assert rep == {
            "status":
            "ok",
            "messages": [{
                "count": 1,
                "body": f"{user.user_id} msg".encode(),
                "timestamp": datetime(2000, 1, 2),
                "sender": alice.device_id,
            }],
        }
示例#21
0
async def _apply_migration(conn, migration: MigrationItem):
    async with conn.transaction():
        await conn.execute(migration.sql)
        if migration.idx >= CREATE_MIGRATION_TABLE_ID:
            # The migration table is created in the second migration
            sql = "INSERT INTO migration (_id, name, applied) VALUES ($1, $2, $3)"
            await conn.execute(sql, migration.idx, migration.name, pendulum_now())
示例#22
0
    async def upload_manifest(self, entry_id: EntryID,
                              manifest: RemoteManifest):
        """
        Raises:
            FSError
            FSRemoteSyncError
            FSBackendOfflineError
            FSWorkspaceInMaintenance
            FSBadEncryptionRevision
        """
        assert manifest.author == self.device.device_id
        assert timestamps_in_the_ballpark(manifest.timestamp, pendulum_now())

        workspace_entry = self.get_workspace_entry()

        try:
            ciphered = manifest.dump_sign_and_encrypt(
                key=workspace_entry.key,
                author_signkey=self.device.signing_key)
        except DataError as exc:
            raise FSError(f"Cannot encrypt vlob: {exc}") from exc

        # Upload the vlob
        if manifest.version == 1:
            await self._vlob_create(workspace_entry.encryption_revision,
                                    entry_id, ciphered, manifest.timestamp)
        else:
            await self._vlob_update(
                workspace_entry.encryption_revision,
                entry_id,
                ciphered,
                manifest.timestamp,
                manifest.version,
            )
示例#23
0
async def test_reencryption_provide_unknown_vlob_atom_and_duplications(
    backend, alice, alice_backend_sock, realm, vlob_atoms
):
    await realm_start_reencryption_maintenance(
        alice_backend_sock, realm, 2, pendulum_now(), {alice.user_id: b"foo"}
    )
    rep = await vlob_maintenance_get_reencryption_batch(alice_backend_sock, realm, 2)
    assert rep["status"] == "ok"
    assert len(rep["batch"]) == 3

    unknown_vlob_id = VlobID.new()
    duplicated_vlob_id = rep["batch"][0]["vlob_id"]
    duplicated_version = rep["batch"][0]["version"]
    duplicated_expected_blob = rep["batch"][0]["blob"]
    reencrypted_batch = [
        # Reencryption as identity
        *rep["batch"],
        # Add an unknown vlob
        {"vlob_id": unknown_vlob_id, "version": 1, "blob": b"ignored"},
        # Valid vlob ID with invalid version
        {"vlob_id": duplicated_vlob_id, "version": 99, "blob": b"ignored"},
        # Duplicate a vlob atom, should be ignored given the reencryption has already be done for it
        {"vlob_id": duplicated_vlob_id, "version": duplicated_version, "blob": b"ignored"},
    ]

    # Another level of duplication !
    for i in range(2):
        rep = await vlob_maintenance_save_reencryption_batch(
            alice_backend_sock, realm, 2, reencrypted_batch
        )
        assert rep == {"status": "ok", "total": 3, "done": 3}

    # Finish the reencryption
    await realm_finish_reencryption_maintenance(alice_backend_sock, realm, 2)

    # Check the vlobs
    with pytest.raises(VlobNotFoundError):
        await backend.vlob.read(
            organization_id=alice.organization_id,
            author=alice.device_id,
            encryption_revision=2,
            vlob_id=unknown_vlob_id,
        )
    with pytest.raises(VlobVersionError):
        await backend.vlob.read(
            organization_id=alice.organization_id,
            author=alice.device_id,
            encryption_revision=2,
            vlob_id=duplicated_vlob_id,
            version=99,
        )
    _, content, _, _, _ = await backend.vlob.read(
        organization_id=alice.organization_id,
        author=alice.device_id,
        encryption_revision=2,
        vlob_id=duplicated_vlob_id,
        version=duplicated_version,
    )
    assert content == duplicated_expected_blob
示例#24
0
    async def do_create_new_device(self, author: LocalDevice,
                                   device_label: Optional[str]) -> None:
        device_id = DeviceID(f"{author.user_id}@{DeviceName.new()}")
        try:
            now = pendulum_now()

            device_certificate = DeviceCertificateContent(
                author=author.device_id,
                timestamp=now,
                device_id=device_id,
                verify_key=self._verify_key,
                device_label=device_label,
            )
            redacted_device_certificate = device_certificate.evolve(
                device_label=None)

            device_certificate = device_certificate.dump_and_sign(
                author.signing_key)
            redacted_device_certificate = redacted_device_certificate.dump_and_sign(
                author.signing_key)

        except DataError as exc:
            raise InviteError(
                f"Cannot generate device certificate: {exc}") from exc

        rep = await self._cmds.device_create(
            device_certificate=device_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        if rep["status"] != "ok":
            raise InviteError(f"Cannot create device: {rep}")

        try:
            payload = InviteDeviceConfirmation(
                device_id=device_id,
                device_label=device_label,
                human_handle=author.human_handle,
                profile=author.profile,
                private_key=author.private_key,
                user_manifest_id=author.user_manifest_id,
                user_manifest_key=author.user_manifest_key,
                root_verify_key=author.root_verify_key,
            ).dump_and_encrypt(key=self._shared_secret_key)
        except DataError as exc:
            raise InviteError(
                "Cannot generate InviteUserConfirmation payload") from exc

        rep = await self._cmds.invite_4_greeter_communicate(token=self.token,
                                                            payload=payload)
        if rep["status"] in ("not_found", "already_deleted"):
            raise InviteNotAvailableError()
        elif rep["status"] == "invalid_state":
            raise InvitePeerResetError()
        elif rep["status"] != "ok":
            raise InviteError(
                f"Backend error during step 4 (confirmation exchange): {rep}")

        await self._cmds.invite_delete(token=self.token,
                                       reason=InvitationDeletedReason.FINISHED)
示例#25
0
async def bootstrap_organization(
    cmds: APIV1_BackendAnonymousCmds,
    human_handle: Optional[HumanHandle],
    device_label: Optional[str],
) -> LocalDevice:
    root_signing_key = SigningKey.generate()
    root_verify_key = root_signing_key.verify_key

    organization_addr = BackendOrganizationAddr.build(
        backend_addr=cmds.addr,
        organization_id=cmds.addr.organization_id,
        root_verify_key=root_verify_key,
    )

    device = generate_new_device(
        organization_addr=organization_addr,
        profile=UserProfile.ADMIN,
        human_handle=human_handle,
        device_label=device_label,
    )

    now = pendulum_now()
    user_certificate = UserCertificateContent(
        author=None,
        timestamp=now,
        user_id=device.user_id,
        human_handle=device.human_handle,
        public_key=device.public_key,
        profile=device.profile,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    device_certificate = DeviceCertificateContent(
        author=None,
        timestamp=now,
        device_id=device.device_id,
        device_label=device.device_label,
        verify_key=device.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user_certificate = user_certificate.dump_and_sign(root_signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(
        root_signing_key)
    device_certificate = device_certificate.dump_and_sign(root_signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        root_signing_key)

    rep = await cmds.organization_bootstrap(
        organization_id=cmds.addr.organization_id,
        bootstrap_token=cmds.addr.token,
        root_verify_key=root_verify_key,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=redacted_user_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )
    _check_rep(rep, step_name="organization bootstrap")

    return device
示例#26
0
 async def _cancel_invitation(self):
     await backend.invite.delete(
         organization_id=self.author.organization_id,
         greeter=self.author.user_id,
         token=self.invitation_addr.token,
         on=pendulum_now(),
         reason=InvitationDeletedReason.CANCELLED,
     )
 async def get_minimal_remote_manifest(
         self, entry_id: EntryID) -> Optional[RemoteManifest]:
     manifest = await self.local_storage.get_manifest(entry_id)
     if not manifest.is_placeholder:
         return None
     return manifest.base.evolve(author=self.local_author,
                                 timestamp=pendulum_now(),
                                 version=1)
示例#28
0
 async def _cancel_invitation():
     await backend.invite.delete(
         organization_id=alice.organization_id,
         greeter=alice.user_id,
         token=invitation_addr.token,
         on=pendulum_now(),
         reason=InvitationDeletedReason.CANCELLED,
     )
示例#29
0
 def timestamp(self) -> DateTime:
     """This method centralizes the production of parsec timestamps for a given device.
     At the moment it is simply an alias to `pendulum.now` but it has two main benefits:
     1. Allowing for easier testing by patching this method in device-sepecific way
     2. Allowing for other implementation in the future allowing to track, check and
        possibly alter the production of timestamps.
     """
     return pendulum_now()
示例#30
0
 def get_user(self, user_id: UserID, now: DateTime = None) -> Optional[UserCertificateContent]:
     now = now or pendulum_now()
     try:
         cached_on, verified_user = self._users_cache[user_id]
         if (now - cached_on).total_seconds() < self.cache_validity:
             return verified_user
     except KeyError:
         pass
     return None