コード例 #1
0
    async def load_block(self, access: BlockAccess) -> None:
        """
        Raises:
            FSError
            FSRemoteBlockNotFound
            FSBackendOfflineError
            FSWorkspaceInMaintenance
            FSWorkspaceNoAccess
        """
        # Download
        rep = await self._backend_cmds("block_read", access.id)
        if rep["status"] == "not_found":
            raise FSRemoteBlockNotFound(access)
        elif rep["status"] == "not_allowed":
            # Seems we lost the access to the realm
            raise FSWorkspaceNoReadAccess("Cannot load block: no read access")
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                f"Cannot download block while the workspace in maintenance")
        elif rep["status"] != "ok":
            raise FSError(f"Cannot download block: `{rep['status']}`")

        # Decryption
        try:
            block = access.key.decrypt(rep["block"])

        # Decryption error
        except CryptoError as exc:
            raise FSError(f"Cannot decrypt block: {exc}") from exc

        # TODO: let encryption manager do the digest check ?
        assert HashDigest.from_data(block) == access.digest, access
        await self.local_storage.set_clean_block(access.id, block)
コード例 #2
0
    async def workspace_continue_reencryption(self, workspace_id: EntryID) -> ReencryptionJob:
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSWorkspaceNoAccess
            FSWorkspaceNotFoundError
        """
        user_manifest = self.get_user_manifest()
        workspace_entry = user_manifest.get_workspace_entry(workspace_id)
        if not workspace_entry:
            raise FSWorkspaceNotFoundError(f"Unknown workspace `{workspace_id}`")

        # First make sure the workspace is under maintenance
        try:
            rep = await self.backend_cmds.realm_status(workspace_entry.id)

        except BackendNotAvailable as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except BackendConnectionError as exc:
            raise FSError(
                f"Cannot continue maintenance on workspace {workspace_id}: {exc}"
            ) from exc

        if rep["status"] == "not_allowed":
            raise FSWorkspaceNoAccess(f"Not allowed to access workspace {workspace_id}: {rep}")
        elif rep["status"] != "ok":
            raise FSError(f"Error while getting status for workspace {workspace_id}: {rep}")

        if not rep["in_maintenance"] or rep["maintenance_type"] != MaintenanceType.REENCRYPTION:
            raise FSWorkspaceNotInMaintenance("Not in reencryption maintenance")
        current_encryption_revision = rep["encryption_revision"]
        if rep["encryption_revision"] != workspace_entry.encryption_revision:
            raise FSError("Bad encryption revision")

        # Must retrieve the previous encryption revision's key
        version_to_fetch = None
        while True:
            previous_user_manifest = await self._fetch_remote_user_manifest(
                version=version_to_fetch
            )
            previous_workspace_entry = previous_user_manifest.get_workspace_entry(
                workspace_entry.id
            )
            if not previous_workspace_entry:
                raise FSError(
                    f"Never had access to encryption revision {current_encryption_revision - 1}"
                )

            if previous_workspace_entry.encryption_revision == current_encryption_revision - 1:
                break
            else:
                version_to_fetch = previous_user_manifest.version - 1

        return ReencryptionJob(self.backend_cmds, workspace_entry, previous_workspace_entry)
コード例 #3
0
ファイル: userfs.py プロジェクト: Scille/parsec-cloud
    async def process_last_messages(self) -> Sequence[Tuple[int, Exception]]:
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSSharingNotAllowedError
        """
        errors = []
        # Concurrent message processing is totally pointless
        async with self._process_messages_lock:
            user_manifest = self.get_user_manifest()
            initial_last_processed_message = user_manifest.last_processed_message
            try:
                rep = await self.backend_cmds.message_get(
                    offset=initial_last_processed_message)

            except BackendNotAvailable as exc:
                raise FSBackendOfflineError(str(exc)) from exc

            except BackendConnectionError as exc:
                raise FSError(f"Cannot retrieve user messages: {exc}") from exc

            if rep["status"] != "ok":
                raise FSError(f"Cannot retrieve user messages: {rep}")

            new_last_processed_message = initial_last_processed_message
            for msg in rep["messages"]:
                try:
                    await self._process_message(msg["sender"],
                                                msg["timestamp"], msg["body"])
                    new_last_processed_message = msg["count"]

                except FSBackendOfflineError:
                    raise

                except FSError as exc:
                    logger.warning("Invalid message",
                                   reason=exc,
                                   sender=msg["sender"],
                                   count=msg["count"])
                    errors.append((msg["count"], exc))

            # Update message offset in user manifest
            async with self._update_user_manifest_lock:
                user_manifest = self.get_user_manifest()
                if user_manifest.last_processed_message < new_last_processed_message:
                    user_manifest = user_manifest.evolve_and_mark_updated(
                        last_processed_message=new_last_processed_message,
                        timestamp=self.device.timestamp(),
                    )
                    await self.set_user_manifest(user_manifest)
                    self.event_bus.send(CoreEvent.FS_ENTRY_UPDATED,
                                        id=self.user_manifest_id)

        return errors
コード例 #4
0
ファイル: userfs.py プロジェクト: AntoineDupre/parsec-cloud
    async def _fetch_remote_user_manifest(self,
                                          version: int = None) -> UserManifest:
        """
        Raises:
            FSError
            FSWorkspaceInMaintenance
            FSBackendOfflineError
        """
        try:
            # Note encryption_revision is always 1 given we never reencrypt
            # the user manifest's realm
            rep = await self.backend_cmds.vlob_read(1, self.user_manifest_id,
                                                    version)

        except BackendNotAvailable as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        if rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                "Cannot access workspace data while it is in maintenance")
        elif rep["status"] != "ok":
            raise FSError(f"Cannot fetch user manifest from backend: {rep}")

        expected_author = rep["author"]
        expected_timestamp = rep["timestamp"]
        expected_version = rep["version"]
        blob = rep["blob"]

        try:
            author = await self.remote_devices_manager.get_device(
                expected_author)

        except RemoteDevicesManagerBackendOfflineError as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except RemoteDevicesManagerError as exc:
            raise FSError(f"Cannot retrieve author public key: {exc}") from exc

        try:
            manifest = UserManifest.decrypt_verify_and_load(
                blob,
                key=self.device.user_manifest_key,
                author_verify_key=author.verify_key,
                expected_id=self.device.user_manifest_id,
                expected_author=expected_author,
                expected_timestamp=expected_timestamp,
                expected_version=version
                if version is not None else expected_version,
            )

        except DataError as exc:
            raise FSError(f"Invalid user manifest: {exc}") from exc

        return manifest
コード例 #5
0
def translate_backend_cmds_errors() -> Iterator[None]:
    try:
        yield
    except BackendNotAvailable as exc:
        raise FSBackendOfflineError(str(exc)) from exc
    except BackendConnectionError as exc:
        raise FSError(str(exc)) from exc
コード例 #6
0
ファイル: userfs.py プロジェクト: anthrax3/parsec-cloud
    async def _retreive_participants(self, workspace_id):
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSWorkspaceNoAccess
        """
        # First retrieve workspace participants list
        roles = await self.remote_loader.load_realm_current_roles(workspace_id)

        # Then retrieve each participant user data
        try:
            users = []
            for user_id in roles.keys():
                user, revoked_user = await self.remote_devices_manager.get_user(user_id)
                if not revoked_user:
                    users.append(user)

        except RemoteDevicesManagerBackendOfflineError as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except RemoteDevicesManagerError as exc:
            raise FSError(f"Cannot retrieve workspace {workspace_id} participants: {exc}") from exc

        return users
コード例 #7
0
ファイル: userfs.py プロジェクト: anthrax3/parsec-cloud
    def _generate_reencryption_messages(self, new_workspace_entry, users, now: Pendulum):
        """
        Raises:
            FSError
        """
        msg = SharingReencryptedMessageContent(
            author=self.device.device_id,
            timestamp=now,
            name=new_workspace_entry.name,
            id=new_workspace_entry.id,
            encryption_revision=new_workspace_entry.encryption_revision,
            encrypted_on=new_workspace_entry.encrypted_on,
            key=new_workspace_entry.key,
        )

        per_user_ciphered_msgs = {}
        for user in users:
            try:
                ciphered = msg.dump_sign_and_encrypt_for(
                    author_signkey=self.device.signing_key, recipient_pubkey=user.public_key
                )
                per_user_ciphered_msgs[user.user_id] = ciphered
            except DataError as exc:
                raise FSError(
                    f"Cannot create reencryption message for `{user.user_id}`: {exc}"
                ) from exc

        return per_user_ciphered_msgs
コード例 #8
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,
            )
コード例 #9
0
ファイル: remote_loader.py プロジェクト: Scille/parsec-cloud
    async def list_versions(
            self, entry_id: EntryID) -> Dict[int, Tuple[DateTime, DeviceID]]:
        """
        Raises:
            FSError
            FSRemoteOperationError
            FSBackendOfflineError
            FSWorkspaceInMaintenance
            FSRemoteManifestNotFound
        """
        with translate_backend_cmds_errors():
            rep = await self.backend_cmds.vlob_list_versions(
                VlobID(entry_id.uuid))
        if rep["status"] == "not_allowed":
            # Seems we lost the access to the realm
            raise FSWorkspaceNoReadAccess(
                "Cannot load manifest: no read access")
        elif rep["status"] == "not_found":
            raise FSRemoteManifestNotFound(entry_id)
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                "Cannot download vlob while the workspace is in maintenance")
        elif rep["status"] != "ok":
            raise FSError(f"Cannot fetch vlob {entry_id}: `{rep['status']}`")

        return rep["versions"]
コード例 #10
0
    async def _vlob_create(self, encryption_revision: int, entry_id: EntryID,
                           ciphered: bytes, now: Pendulum):
        """
        Raises:
            FSError
            FSRemoteSyncError
            FSBackendOfflineError
            FSWorkspaceInMaintenance
            FSBadEncryptionRevision
            FSWorkspaceNoAccess
        """

        # Vlob upload
        rep = await self._backend_cmds("vlob_create", self.workspace_id,
                                       encryption_revision, entry_id, now,
                                       ciphered)
        if rep["status"] == "already_exists":
            raise FSRemoteSyncError(entry_id)
        elif rep["status"] == "not_allowed":
            # Seems we lost the access to the realm
            raise FSWorkspaceNoWriteAccess(
                "Cannot upload manifest: no write access")
        elif rep["status"] == "bad_encryption_revision":
            raise FSBadEncryptionRevision(
                f"Cannot create vlob {entry_id}: Bad encryption revision provided"
            )
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                f"Cannot create vlob while the workspace is in maintenance")
        elif rep["status"] != "ok":
            raise FSError(f"Cannot create vlob {entry_id}: `{rep['status']}`")
コード例 #11
0
    async def _process_message(
        self, sender_id: DeviceID, expected_timestamp: DateTime, ciphered: bytes
    ) -> None:
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSSharingNotAllowedError
        """
        # Retrieve the sender
        sender = await self.remote_loader.get_device(sender_id)

        # Decrypt&verify message
        try:
            msg = BaseMessageContent.decrypt_verify_and_load_for(
                ciphered,
                recipient_privkey=self.device.private_key,
                author_verify_key=sender.verify_key,
                expected_author=sender_id,
                expected_timestamp=expected_timestamp,
            )

        except DataError as exc:
            raise FSError(f"Cannot decrypt&validate message from `{sender_id}`: {exc}") from exc

        if isinstance(msg, (SharingGrantedMessageContent, SharingReencryptedMessageContent)):
            await self._process_message_sharing_granted(msg)

        elif isinstance(msg, SharingRevokedMessageContent):
            await self._process_message_sharing_revoked(msg)

        elif isinstance(msg, PingMessageContent):
            self.event_bus.send(CoreEvent.MESSAGE_PINGED, ping=msg.ping)
コード例 #12
0
    async def _get_previous_workspace_entry(
            self, workspace_entry: WorkspaceEntry) -> WorkspaceEntry:
        """
        Return the most recent workspace entry using the previous encryption revision.

        Raises:
            FSError
            FSBackendOfflineError
            FSWorkspaceInMaintenance
        """
        # Must retrieve the previous encryption revision's key
        version_to_fetch = None
        current_encryption_revision = workspace_entry.encryption_revision
        while True:
            previous_user_manifest = await self._fetch_remote_user_manifest(
                version=version_to_fetch)
            previous_workspace_entry = previous_user_manifest.get_workspace_entry(
                workspace_entry.id)
            if not previous_workspace_entry:
                raise FSError(
                    f"Never had access to encryption revision {current_encryption_revision - 1}"
                )

            if previous_workspace_entry.encryption_revision == current_encryption_revision - 1:
                return previous_workspace_entry
            else:
                version_to_fetch = previous_user_manifest.version - 1
コード例 #13
0
ファイル: remote_loader.py プロジェクト: Scille/parsec-cloud
 async def upload_manifest(
     self,
     entry_id: EntryID,
     manifest: BaseRemoteManifest,
     timestamp_greater_than: Optional[DateTime] = None,
 ) -> BaseRemoteManifest:
     raise FSError(
         "Cannot upload manifest through a timestamped remote loader")
コード例 #14
0
    async def get_reencryption_need(self) -> ReencryptionNeed:
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSWorkspaceNoAccess
        """
        wentry = self.get_workspace_entry()
        try:
            workspace_manifest = await self.local_storage.get_manifest(
                self.workspace_id)
            if workspace_manifest.is_placeholder:
                return ReencryptionNeed(user_revoked=(),
                                        role_revoked=(),
                                        reencryption_already_in_progress=False)

        except FSLocalMissError:
            pass

        try:
            rep = await self.backend_cmds.realm_status(self.workspace_id)

        except BackendNotAvailable as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except BackendConnectionError as exc:
            raise FSError(
                f"Cannot retreive remote status for workspace {self.workspace_id}: {exc}"
            ) from exc

        reencryption_already_in_progress = (rep["in_maintenance"]
                                            and rep["maintenance_type"]
                                            == MaintenanceType.REENCRYPTION)

        certificates = await self.remote_loader.load_realm_role_certificates()
        has_role = set()
        role_revoked = set()
        for certif in certificates:
            if certif.role is None:
                if certif.timestamp > wentry.encrypted_on:
                    role_revoked.add(certif.user_id)
                has_role.discard(certif.user_id)
            else:
                role_revoked.discard(certif.user_id)
                has_role.add(certif.user_id)

        user_revoked = []
        for user_id in has_role:
            _, revoked_user = await self.remote_loader.get_user(user_id,
                                                                no_cache=True)
            if revoked_user and revoked_user.timestamp > wentry.encrypted_on:
                user_revoked.append(user_id)

        return ReencryptionNeed(
            user_revoked=tuple(user_revoked),
            role_revoked=tuple(role_revoked),
            reencryption_already_in_progress=reencryption_already_in_progress,
        )
コード例 #15
0
ファイル: userfs.py プロジェクト: AntoineDupre/parsec-cloud
    async def _process_message(self, sender_id: DeviceID,
                               expected_timestamp: Pendulum, ciphered: bytes):
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSSharingNotAllowedError
        """
        # Retrieve the sender
        try:
            sender = await self.remote_devices_manager.get_device(sender_id)

        except RemoteDevicesManagerBackendOfflineError as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except RemoteDevicesManagerError as exc:
            raise FSError(
                f"Cannot retrieve message sender `{sender_id}`: {exc}"
            ) from exc

        # Decrypt&verify message
        try:
            msg = MessageContent.decrypt_verify_and_load_for(
                ciphered,
                recipient_privkey=self.device.private_key,
                author_verify_key=sender.verify_key,
                expected_author=sender_id,
                expected_timestamp=expected_timestamp,
            )

        except DataError as exc:
            raise FSError(
                f"Cannot decrypt&validate message from `{sender_id}`: {exc}"
            ) from exc

        if isinstance(
                msg,
            (SharingGrantedMessageContent, SharingReencryptedMessageContent)):
            await self._process_message_sharing_granted(msg)

        elif isinstance(msg, SharingRevokedMessageContent):
            await self._process_message_sharing_revoked(msg)

        elif isinstance(msg, PingMessageContent):
            self.event_bus.send("pinged", ping=msg.ping)
コード例 #16
0
 async def _vlob_update(
     self,
     encryption_revision: int,
     entry_id: EntryID,
     ciphered: bytes,
     now: DateTime,
     version: int,
 ) -> None:
     raise FSError("Cannot update vlob through a timestamped remote loader")
コード例 #17
0
ファイル: userfs.py プロジェクト: Scille/parsec-cloud
    async def _send_start_reencryption_cmd(
        self,
        workspace_id: EntryID,
        encryption_revision: int,
        timestamp: DateTime,
        per_user_ciphered_msgs: Dict[UserID, bytes],
    ) -> bool:
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSWorkspaceNoAccess
            BackendCmdsParticipantsMismatchError
        """
        # Finally send command to the backend
        try:
            rep = await self.backend_cmds.realm_start_reencryption_maintenance(
                RealmID(workspace_id.uuid), encryption_revision, timestamp,
                per_user_ciphered_msgs)

        except BackendNotAvailable as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except BackendConnectionError as exc:
            raise FSError(
                f"Cannot start maintenance on workspace {workspace_id}: {exc}"
            ) from exc

        if rep["status"] == "participants_mismatch":
            # Catched by caller
            return False
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                f"Workspace {workspace_id} already in maintenance: {rep}")
        elif rep["status"] == "not_allowed":
            raise FSWorkspaceNoAccess(
                f"Not allowed to start maintenance on workspace {workspace_id}: {rep}"
            )
        elif rep["status"] != "ok":
            raise FSError(
                f"Cannot start maintenance on workspace {workspace_id}: {rep}")
        return True
コード例 #18
0
    async def _backend_cmds(self, cmd, *args, **kwargs):
        try:
            return await getattr(self.backend_cmds, cmd)(*args, **kwargs)

        except BackendNotAvailable as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except BackendConnectionError as exc:
            raise FSError(
                f"`{cmd}` request has failed due to connection error `{exc}`"
            ) from exc
コード例 #19
0
ファイル: remote_loader.py プロジェクト: Scille/parsec-cloud
    async def upload_manifest(
        self,
        entry_id: EntryID,
        manifest: BaseRemoteManifest,
        timestamp_greater_than: Optional[DateTime] = None,
    ) -> BaseRemoteManifest:
        """
        Raises:
            FSError
            FSRemoteSyncError
            FSBackendOfflineError
            FSWorkspaceInMaintenance
            FSBadEncryptionRevision
        """
        assert manifest.author == self.device.device_id

        # Restamp the manifest before uploading
        timestamp = self.device.timestamp()
        if timestamp_greater_than is not None:
            timestamp = max(
                timestamp,
                timestamp_greater_than.add(
                    microseconds=MANIFEST_STAMP_AHEAD_US))

        manifest = manifest.evolve(timestamp=timestamp)

        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
        try:
            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,
                )
        # The backend notified us that some restamping is required
        except VlobRequireGreaterTimestampError as exc:
            return await self.upload_manifest(entry_id, manifest,
                                              exc.strictly_greater_than)
        else:
            return manifest
コード例 #20
0
ファイル: remote_loader.py プロジェクト: Scille/parsec-cloud
    async def upload_block(self, access: BlockAccess, data: bytes) -> None:
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSRemoteOperationError
            FSWorkspaceInMaintenance
            FSWorkspaceNoAccess
        """
        # Encryption
        try:
            ciphered = access.key.encrypt(data)

        # Encryption error
        except CryptoError as exc:
            raise FSError(f"Cannot encrypt block: {exc}") from exc

        # Upload block
        with translate_backend_cmds_errors():
            rep = await self.backend_cmds.block_create(
                access.id, RealmID(self.workspace_id.uuid), ciphered)

        if rep["status"] == "already_exists":
            # Ignore exception if the block has already been uploaded
            # This might happen when a failure occurs before the local storage is updated
            pass
        elif rep["status"] == "not_allowed":
            # Seems we lost the access to the realm
            raise FSWorkspaceNoWriteAccess(
                "Cannot upload block: no write access")
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                "Cannot upload block while the workspace in maintenance")
        elif rep["status"] != "ok":
            raise FSError(f"Cannot upload block: {rep}")

        # Update local storage
        await self.local_storage.set_clean_block(access.id, data)
        await self.local_storage.clear_chunk(ChunkID(access.id.uuid),
                                             miss_ok=True)
コード例 #21
0
ファイル: workspacefs.py プロジェクト: anthrax3/parsec-cloud
    async def get_reencryption_need(self) -> ReencryptionNeed:
        """
        Raises:
            FSError
            FSBackendOfflineError
            FSWorkspaceNoAccess
        """
        wentry = self.get_workspace_entry()
        try:
            workspace_manifest = await self.local_storage.get_manifest(
                self.workspace_id)
            if workspace_manifest.is_placeholder:
                return ReencryptionNeed()

        except FSLocalMissError:
            pass

        certificates = await self.remote_loader.load_realm_role_certificates()
        has_role = set()
        role_revoked = set()
        for certif in certificates:
            if certif.role is None:
                if certif.timestamp > wentry.encrypted_on:
                    role_revoked.add(certif.user_id)
                has_role.discard(certif.user_id)
            else:
                role_revoked.discard(certif.user_id)
                has_role.add(certif.user_id)

        user_revoked = []
        try:
            for user_id in has_role:
                _, revoked_user = await self.remote_device_manager.get_user(
                    user_id, no_cache=True)
                if revoked_user and revoked_user.timestamp > wentry.encrypted_on:
                    user_revoked.append(user_id)

        except RemoteDevicesManagerBackendOfflineError as exc:
            raise FSBackendOfflineError(str(exc)) from exc

        except RemoteDevicesManagerError as exc:
            raise FSError(
                f"Cannot retrieve workspace participant {user_id}: {exc}"
            ) from exc

        return ReencryptionNeed(user_revoked=tuple(user_revoked),
                                role_revoked=tuple(role_revoked))
コード例 #22
0
ファイル: remote_loader.py プロジェクト: Scille/parsec-cloud
    async def _vlob_update(
        self,
        encryption_revision: int,
        entry_id: EntryID,
        ciphered: bytes,
        now: DateTime,
        version: int,
    ) -> None:
        """
        Raises:
            FSError
            FSRemoteSyncError
            FSBackendOfflineError
            FSRemoteOperationError
            FSWorkspaceInMaintenance
            FSBadEncryptionRevision
            FSWorkspaceNoAccess
        """
        # Vlob upload
        with translate_backend_cmds_errors():
            rep = await self.backend_cmds.vlob_update(encryption_revision,
                                                      VlobID(entry_id.uuid),
                                                      version, now, ciphered)

        if rep["status"] == "not_found":
            raise FSRemoteSyncError(entry_id)
        elif rep["status"] == "not_allowed":
            # Seems we lost the access to the realm
            raise FSWorkspaceNoWriteAccess(
                "Cannot upload manifest: no write access")
        elif rep["status"] == "require_greater_timestamp":
            raise VlobRequireGreaterTimestampError(
                rep["strictly_greater_than"])
        elif rep["status"] == "bad_version":
            raise FSRemoteSyncError(entry_id)
        elif rep["status"] == "bad_encryption_revision":
            raise FSBadEncryptionRevision(
                f"Cannot update vlob {entry_id}: Bad encryption revision provided"
            )
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                "Cannot create vlob while the workspace is in maintenance")
        elif rep["status"] != "ok":
            raise FSError(f"Cannot update vlob {entry_id}: `{rep['status']}`")
コード例 #23
0
    async def _vlob_update(
        self,
        encryption_revision: int,
        entry_id: EntryID,
        ciphered: bytes,
        now: DateTime,
        version: int,
    ) -> None:
        """
        Raises:
            FSError
            FSRemoteSyncError
            FSBackendOfflineError
            FSWorkspaceInMaintenance
            FSBadEncryptionRevision
            FSWorkspaceNoAccess
        """
        # Vlob upload
        with translate_backend_cmds_errors():
            rep = await self.backend_cmds.vlob_update(encryption_revision,
                                                      entry_id, version, now,
                                                      ciphered)

        if rep["status"] == "not_found":
            raise FSRemoteSyncError(entry_id)
        elif rep["status"] == "not_allowed":
            # Seems we lost the access to the realm
            raise FSWorkspaceNoWriteAccess(
                "Cannot upload manifest: no write access")
        elif rep["status"] == "bad_version":
            raise FSRemoteSyncError(entry_id)
        elif rep["status"] == "bad_timestamp":
            # Quick and dirty fix before a better version with a retry loop : go offline so we
            # don't have to deal with another client updating manifest with a later timestamp
            raise FSBackendOfflineError(rep)
        elif rep["status"] == "bad_encryption_revision":
            raise FSBadEncryptionRevision(
                f"Cannot update vlob {entry_id}: Bad encryption revision provided"
            )
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                "Cannot create vlob while the workspace is in maintenance")
        elif rep["status"] != "ok":
            raise FSError(f"Cannot update vlob {entry_id}: `{rep['status']}`")
コード例 #24
0
    async def create_realm(self, realm_id: EntryID):
        """
        Raises:
            FSError
            FSBackendOfflineError
        """
        certif = RealmRoleCertificateContent.build_realm_root_certif(
            author=self.device.device_id,
            timestamp=pendulum_now(),
            realm_id=realm_id).dump_and_sign(self.device.signing_key)

        rep = await self._backend_cmds("realm_create", certif)
        if rep["status"] == "already_exists":
            # It's possible a previous attempt to create this realm
            # succeeded but we didn't receive the confirmation, hence
            # we play idempotent here.
            return
        elif rep["status"] != "ok":
            raise FSError(f"Cannot create realm {realm_id}: `{rep['status']}`")
コード例 #25
0
ファイル: remote_loader.py プロジェクト: Scille/parsec-cloud
    async def create_realm(self, realm_id: EntryID) -> None:
        """
        Raises:
            FSError
            FSRemoteOperationError
            FSBackendOfflineError
        """
        timestamp = self.device.timestamp()
        certif = RealmRoleCertificateContent.build_realm_root_certif(
            author=self.device.device_id,
            timestamp=timestamp,
            realm_id=RealmID(realm_id.uuid)).dump_and_sign(
                self.device.signing_key)

        with translate_backend_cmds_errors():
            rep = await self.backend_cmds.realm_create(certif)

        if rep["status"] == "already_exists":
            # It's possible a previous attempt to create this realm
            # succeeded but we didn't receive the confirmation, hence
            # we play idempotent here.
            return
        elif rep["status"] != "ok":
            raise FSError(f"Cannot create realm {realm_id}: `{rep['status']}`")
コード例 #26
0
 def _throw_permission_error(self) -> NoReturn:
     raise FSError("Not implemented : WorkspaceStorage is timestamped")
コード例 #27
0
    async def _load_realm_role_certificates(self,
                                            realm_id: Optional[EntryID] = None
                                            ):
        rep = await self._backend_cmds("realm_get_role_certificates", realm_id
                                       or self.workspace_id)
        if rep["status"] == "not_allowed":
            # Seems we lost the access to the realm
            raise FSWorkspaceNoReadAccess(
                "Cannot get workspace roles: no read access")
        elif rep["status"] != "ok":
            raise FSError(
                f"Cannot retrieve workspace roles: `{rep['status']}`")

        try:
            # Must read unverified certificates to access metadata
            unsecure_certifs = sorted(
                [(RealmRoleCertificateContent.unsecure_load(uv_role), uv_role)
                 for uv_role in rep["certificates"]],
                key=lambda x: x[0].timestamp,
            )

            current_roles = {}
            owner_only = (RealmRole.OWNER, )
            owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)

            # Now verify each certif
            for unsecure_certif, raw_certif in unsecure_certifs:
                author = await self.remote_device_manager.get_device(
                    unsecure_certif.author)

                RealmRoleCertificateContent.verify_and_load(
                    raw_certif,
                    author_verify_key=author.verify_key,
                    expected_author=author.device_id,
                )

                # Make sure author had the right to do this
                existing_user_role = current_roles.get(unsecure_certif.user_id)
                if not current_roles and unsecure_certif.user_id == author.device_id.user_id:
                    # First user is autosigned
                    needed_roles = (None, )
                elif (existing_user_role in owner_or_manager
                      or unsecure_certif.role in owner_or_manager):
                    needed_roles = owner_only
                else:
                    needed_roles = owner_or_manager
                if current_roles.get(
                        unsecure_certif.author.user_id) not in needed_roles:
                    raise FSError(
                        f"Invalid realm role certificates: "
                        f"{unsecure_certif.author} has not right to give "
                        f"{unsecure_certif.role} role to {unsecure_certif.user_id} "
                        f"on {unsecure_certif.timestamp}")

                if unsecure_certif.role is None:
                    current_roles.pop(unsecure_certif.user_id, None)
                else:
                    current_roles[
                        unsecure_certif.user_id] = unsecure_certif.role

        # Decryption error
        except DataError as exc:
            raise FSError(f"Invalid realm role certificates: {exc}") from exc

        # Now unsecure_certifs is no longer unsecure we have valided it items
        return [c for c, _ in unsecure_certifs], current_roles
コード例 #28
0
 async def _vlob_update(self, *e, **ke):
     raise FSError(
         f"Cannot update vlob through a timestamped remote loader")
コード例 #29
0
 async def upload_manifest(self, *e, **ke):
     raise FSError(
         f"Cannot upload manifest through a timestamped remote loader")
コード例 #30
0
 async def upload_block(self, *e, **ke):
     raise FSError(
         f"Cannot upload block through a timestamped remote loader")