def __init__(
     self,
     backend_cmds: BackendAuthenticatedCmds,
     root_verify_key: VerifyKey,
     cache_validity: int = DEFAULT_CACHE_VALIDITY,
 ):
     self._backend_cmds = backend_cmds
     self._trustchain_ctx = TrustchainContext(root_verify_key,
                                              cache_validity)
        def load_trustchain(self, user):
            ctx = TrustchainContext(coolorg.root_verify_key, 1)

            user_certif = next(
                certif for user_id, certif in self.users_certifs.items()
                if user_id == user)
            revoked_user_certif = next(
                (certif
                 for user_id, certif in self.revoked_users_certifs.items()
                 if user_id == user),
                None,
            )
            devices_certifs = [
                certif for device_id, certif in self.devices_certifs.items()
                if device_id.user_id == user
            ]
            user_content, revoked_user_content, devices_contents = ctx.load_user_and_devices(
                trustchain={
                    "users":
                    [certif for certif in self.users_certifs.values()],
                    "revoked_users":
                    [certif for certif in self.revoked_users_certifs.values()],
                    "devices":
                    [certif for certif in self.devices_certifs.values()],
                },
                user_certif=user_certif,
                revoked_user_certif=revoked_user_certif,
                devices_certifs=devices_certifs,
                expected_user_id=user,
            )

            expected_user_content = next(
                content for user_id, content in self.users_content.items()
                if user_id == user)
            expected_revoked_user_content = next(
                (content
                 for user_id, content in self.revoked_users_content.items()
                 if user_id == user),
                None,
            )
            expected_devices_contents = [
                content for device_id, content in self.devices_content.items()
                if device_id.user_id == user
            ]
            assert user_content == expected_user_content
            assert revoked_user_content == expected_revoked_user_content
            assert sorted(devices_contents,
                          key=lambda device: device.device_id) == sorted(
                              expected_devices_contents,
                              key=lambda device: device.device_id)
async def get_device_invitation_creator(
    backend_cmds: APIV1_BackendAnonymousCmds, root_verify_key: VerifyKey,
    new_device_id: DeviceID
) -> Tuple[UserCertificateContent, Optional[RevokedUserCertificateContent],
           DeviceCertificateContent]:
    """
    Raises:
        RemoteDevicesManagerError
        RemoteDevicesManagerBackendOfflineError
        RemoteDevicesManagerUserNotFoundError
        RemoteDevicesManagerInvalidTrustchainError
    """
    try:
        rep = await backend_cmds.device_get_invitation_creator(new_device_id)
    except BackendNotAvailable as exc:
        raise RemoteDevicesManagerBackendOfflineError(*exc.args) from exc
    except BackendConnectionError as exc:
        raise RemoteDevicesManagerError(
            "Failed to fetch invitation creator for device "
            f"`{new_device_id}` from the backend: {exc}") from exc

    if rep["status"] == "not_found":
        raise RemoteDevicesManagerUserNotFoundError(
            f"User `{new_device_id}` doesn't exist in backend")
    elif rep["status"] != "ok":
        raise RemoteDevicesManagerError(
            f"Cannot fetch invitation creator for device `{new_device_id}`: `{rep['status']}`"
        )

    try:
        ctx = TrustchainContext(root_verify_key, DEFAULT_CACHE_VALIDITY)
        user, _, (device, ) = ctx.load_user_and_devices(
            trustchain=rep["trustchain"],
            user_certif=rep["user_certificate"],
            devices_certifs=(rep["device_certificate"], ),
        )
    except TrustchainError as exc:
        raise RemoteDevicesManagerInvalidTrustchainError(exc) from exc

    return user, device
class RemoteDevicesManager:
    """
    Fetch users&devices from backend, verify their trustchain and keep
    a cache of them for a limited duration.
    """
    def __init__(
        self,
        backend_cmds: BackendAuthenticatedCmds,
        root_verify_key: VerifyKey,
        cache_validity: int = DEFAULT_CACHE_VALIDITY,
    ):
        self._backend_cmds = backend_cmds
        self._trustchain_ctx = TrustchainContext(root_verify_key,
                                                 cache_validity)

    @property
    def cache_validity(self) -> int:
        return self._trustchain_ctx.cache_validity

    def invalidate_user_cache(self, user_id: UserID) -> None:
        self._trustchain_ctx.invalidate_user_cache(user_id)

    async def get_user(
        self,
        user_id: UserID,
        no_cache: bool = False
    ) -> Tuple[UserCertificateContent,
               Optional[RevokedUserCertificateContent]]:
        """
        Raises:
            RemoteDevicesManagerError
            RemoteDevicesManagerBackendOfflineError
            RemoteDevicesManagerUserNotFoundError
            RemoteDevicesManagerInvalidTrustchainError
        """
        try:
            verified_user = None if no_cache else self._trustchain_ctx.get_user(
                user_id)
            verified_revoked_user = (
                None if no_cache else
                self._trustchain_ctx.get_revoked_user(user_id))
        except TrustchainError as exc:
            raise RemoteDevicesManagerInvalidTrustchainError(exc) from exc
        if not verified_user:
            verified_user, verified_revoked_user, _ = await self.get_user_and_devices(
                user_id, no_cache=True)
        return verified_user, verified_revoked_user

    async def get_device(self,
                         device_id: DeviceID,
                         no_cache: bool = False) -> DeviceCertificateContent:
        """
        Raises:
            RemoteDevicesManagerError
            RemoteDevicesManagerBackendOfflineError
            RemoteDevicesManagerUserNotFoundError
            RemoteDevicesManagerDeviceNotFoundError
            RemoteDevicesManagerInvalidTrustchainError
        """
        try:
            verified_device = None if no_cache else self._trustchain_ctx.get_device(
                device_id)
        except TrustchainError as exc:
            raise RemoteDevicesManagerInvalidTrustchainError(exc) from exc
        if not verified_device:
            _, _, verified_devices = await self.get_user_and_devices(
                device_id.user_id, no_cache=True)
            try:
                verified_device = next(vd for vd in verified_devices
                                       if vd.device_id == device_id)

            except StopIteration:
                raise RemoteDevicesManagerDeviceNotFoundError(
                    f"User `{device_id.user_id}` doesn't have a device `{device_id}`"
                )
        return verified_device

    async def get_user_and_devices(
        self,
        user_id: UserID,
        no_cache: bool = False
    ) -> Tuple[UserCertificateContent, Optional[RevokedUserCertificateContent],
               List[DeviceCertificateContent], ]:
        """
        Note: unlike `get_user` and `get_device`, this method don't rely on cache
        considering only part of the devices to retrieve could be in cache.
        Raises:
            RemoteDevicesManagerError
            RemoteDevicesManagerBackendOfflineError
            RemoteDevicesManagerUserNotFoundError
            RemoteDevicesManagerInvalidTrustchainError
        """
        try:
            rep = await self._backend_cmds.user_get(user_id)
        except BackendNotAvailable as exc:
            raise RemoteDevicesManagerBackendOfflineError(
                f"User `{user_id}` is not in local cache and we are offline."
            ) from exc
        except BackendConnectionError as exc:
            raise RemoteDevicesManagerError(
                f"Failed to fetch user `{user_id}` from the backend: {exc}"
            ) from exc

        if rep["status"] == "not_found":
            raise RemoteDevicesManagerUserNotFoundError(
                f"User `{user_id}` doesn't exist in backend")
        elif rep["status"] != "ok":
            raise RemoteDevicesManagerError(
                f"Cannot fetch user {user_id}: `{rep['status']}`")

        try:
            return self._trustchain_ctx.load_user_and_devices(
                trustchain=rep["trustchain"],
                user_certif=rep["user_certificate"],
                revoked_user_certif=rep["revoked_user_certificate"],
                devices_certifs=rep["device_certificates"],
                expected_user_id=user_id,
            )
        except TrustchainError as exc:
            raise RemoteDevicesManagerInvalidTrustchainError(exc) from exc
Ejemplo n.º 5
0
 def trustchain_ctx_factory(self):
     return TrustchainContext(self.root_verify_key, 1)