Exemplo n.º 1
0
async def test_remove_role_idempotent(
    backend, alice, bob, alice_backend_sock, realm, start_with_existing_role
):
    if start_with_existing_role:
        with freeze_time("2000-01-03"):
            rep = await _realm_generate_certif_and_update_roles_or_fail(
                alice_backend_sock, alice, realm, bob.user_id, RealmRole.MANAGER
            )
            assert rep == {"status": "ok"}

    with freeze_time("2000-01-04"):
        rep = await _realm_generate_certif_and_update_roles_or_fail(
            alice_backend_sock, alice, realm, bob.user_id, None
        )
        if start_with_existing_role:
            assert rep == {"status": "ok"}
        else:
            assert rep == {"status": "already_granted"}

    with freeze_time("2000-01-05"):
        rep = await _realm_generate_certif_and_update_roles_or_fail(
            alice_backend_sock, alice, realm, bob.user_id, None
        )
        assert rep == {"status": "already_granted"}

    certifs = await _realm_get_clear_role_certifs(alice_backend_sock, realm)
    expected_certifs = [
        RealmRoleCertificateContent(
            author=alice.device_id,
            timestamp=datetime(2000, 1, 2),
            realm_id=realm,
            user_id=alice.user_id,
            role=RealmRole.OWNER,
        )
    ]
    if start_with_existing_role:
        expected_certifs += [
            RealmRoleCertificateContent(
                author=alice.device_id,
                timestamp=datetime(2000, 1, 3),
                realm_id=realm,
                user_id=bob.user_id,
                role=RealmRole.MANAGER,
            ),
            RealmRoleCertificateContent(
                author=alice.device_id,
                timestamp=datetime(2000, 1, 4),
                realm_id=realm,
                user_id=bob.user_id,
                role=None,
            ),
        ]
    assert certifs == expected_certifs
Exemplo n.º 2
0
async def test_realm_create_not_allowed_for_outsider(backend, alice, alice_backend_sock):
    realm_id = UUID("C0000000000000000000000000000000")
    certif = RealmRoleCertificateContent.build_realm_root_certif(
        author=alice.device_id, timestamp=pendulum.now(), realm_id=realm_id
    ).dump_and_sign(alice.signing_key)
    rep = await realm_create(alice_backend_sock, certif, check_rep=False)
    assert rep == {"status": "not_allowed", "reason": "Outsider user cannot create realm"}
Exemplo n.º 3
0
async def test_create_invalid_certif(backend, alice, bob, alice_backend_sock):
    realm_id = UUID("C0000000000000000000000000000000")
    certif = RealmRoleCertificateContent.build_realm_root_certif(
        author=bob.device_id, timestamp=pendulum.now(), realm_id=realm_id
    ).dump_and_sign(bob.signing_key)
    rep = await realm_create(alice_backend_sock, certif)
    assert rep == {
        "status": "invalid_certification",
        "reason": "Invalid certification data (Signature was forged or corrupt).",
    }
Exemplo n.º 4
0
async def _realm_generate_certif_and_update_roles_or_fail(
        backend_sock, author, realm_id, user_id, role):
    certif = RealmRoleCertificateContent(
        author=author.device_id,
        timestamp=pendulum_now(),
        realm_id=realm_id,
        user_id=user_id,
        role=role,
    ).dump_and_sign(author.signing_key)
    return await realm_update_roles(backend_sock, certif, check_rep=False)
Exemplo n.º 5
0
async def test_create_ok(backend, alice, alice_backend_sock):
    await events_subscribe(alice_backend_sock)

    realm_id = UUID("C0000000000000000000000000000000")
    certif = RealmRoleCertificateContent.build_realm_root_certif(
        author=alice.device_id, timestamp=pendulum.now(), realm_id=realm_id
    ).dump_and_sign(alice.signing_key)
    with backend.event_bus.listen() as spy:
        rep = await realm_create(alice_backend_sock, certif)
        assert rep == {"status": "ok"}
        await spy.wait_with_timeout(BackendEvent.REALM_ROLES_UPDATED)
Exemplo n.º 6
0
async def test_create_certif_too_old(backend, alice, alice_backend_sock):
    realm_id = UUID("C0000000000000000000000000000000")
    now = pendulum.now()
    certif = RealmRoleCertificateContent.build_realm_root_certif(
        author=alice.device_id, timestamp=now, realm_id=realm_id
    ).dump_and_sign(alice.signing_key)
    with freeze_time(now.add(seconds=TIMESTAMP_MAX_DT)):
        rep = await realm_create(alice_backend_sock, certif)
    assert rep == {
        "status": "invalid_certification",
        "reason": "Invalid timestamp in certification.",
    }
Exemplo n.º 7
0
async def test_create_certif_role_not_owner(backend, alice, bob, alice_backend_sock):
    realm_id = UUID("C0000000000000000000000000000000")
    certif = RealmRoleCertificateContent(
        author=alice.device_id,
        timestamp=pendulum.now(),
        realm_id=realm_id,
        user_id=alice.user_id,
        role=RealmRole.MANAGER,
    ).dump_and_sign(alice.signing_key)
    rep = await realm_create(alice_backend_sock, certif)
    assert rep == {
        "status": "invalid_data",
        "reason": "Initial realm role certificate must set OWNER role.",
    }
Exemplo n.º 8
0
async def _backend_realm_generate_certif_and_update_roles(backend, author, realm_id, user_id, role):
    now = pendulum_now()
    certif = RealmRoleCertificateContent(
        author=author.device_id, timestamp=now, realm_id=realm_id, user_id=user_id, role=role
    ).dump_and_sign(author.signing_key)
    await backend.realm.update_roles(
        author.organization_id,
        RealmGrantedRole(
            certificate=certif,
            realm_id=realm_id,
            user_id=user_id,
            role=role,
            granted_by=author.device_id,
            granted_on=now,
        ),
    )
    return certif
Exemplo n.º 9
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']}`")
Exemplo n.º 10
0
 async def _realm_factory(backend, author, realm_id=None, now=None):
     realm_id = realm_id or uuid4()
     now = now or pendulum_now()
     certif = RealmRoleCertificateContent.build_realm_root_certif(
         author=author.device_id, timestamp=now,
         realm_id=realm_id).dump_and_sign(author.signing_key)
     with backend.event_bus.listen() as spy:
         await backend.realm.create(
             organization_id=author.organization_id,
             self_granted_role=RealmGrantedRole(
                 realm_id=realm_id,
                 user_id=author.user_id,
                 certificate=certif,
                 role=RealmRole.OWNER,
                 granted_by=author.device_id,
                 granted_on=now,
             ),
         )
         await spy.wait_with_timeout(BackendEvent.REALM_ROLES_UPDATED)
     return realm_id
Exemplo n.º 11
0
        async def _give_role(self, author_sock, author, recipient, role):
            author_sock = await self.get_sock(author)

            certif = RealmRoleCertificateContent(
                author=author.device_id,
                timestamp=pendulum_now(),
                realm_id=self.realm_id,
                user_id=recipient.user_id,
                role=role,
            ).dump_and_sign(author.signing_key)
            rep = await realm_update_roles(author_sock,
                                           certif,
                                           check_rep=False)
            if author.user_id == recipient.user_id:
                assert rep == {
                    "status": "invalid_data",
                    "reason": "Realm role certificate cannot be self-signed.",
                }

            else:
                owner_only = (RealmRole.OWNER, )
                owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)
                existing_recipient_role = self.current_roles[recipient.user_id]
                if existing_recipient_role in owner_or_manager or role in owner_or_manager:
                    allowed_roles = owner_only
                else:
                    allowed_roles = owner_or_manager

                if self.current_roles[author.user_id] in allowed_roles:
                    # print(f"+ {author.user_id} -{role.value}-> {recipient.user_id}")
                    if existing_recipient_role != role:
                        assert rep == {"status": "ok"}
                        self.current_roles[recipient.user_id] = role
                        self.certifs.append(certif)
                    else:
                        assert rep == {"status": "already_granted"}
                else:
                    # print(f"- {author.user_id} -{role.value}-> {recipient.user_id}")
                    assert rep == {"status": "not_allowed"}

            return rep["status"] == "ok"
Exemplo n.º 12
0
 async def _update_role(self, author, user, role=RealmRole.MANAGER):
     now = pendulum_now()
     certif = RealmRoleCertificateContent(
         author=author.device_id,
         timestamp=now,
         realm_id=self.wid,
         user_id=user.user_id,
         role=role,
     ).dump_and_sign(author.signing_key)
     await self.backend.realm.update_roles(
         author.organization_id,
         RealmGrantedRole(
             certificate=certif,
             realm_id=self.wid,
             user_id=user.user_id,
             role=role,
             granted_by=author.device_id,
             granted_on=now,
         ),
     )
     return certif
Exemplo n.º 13
0
    async def _update_role_and_check_events(role):

        with backend.event_bus.listen() as spy:
            certif = RealmRoleCertificateContent(
                author=alice.device_id,
                timestamp=pendulum.now(),
                realm_id=realm,
                user_id=bob.user_id,
                role=role,
            ).dump_and_sign(alice.signing_key)
            rep = await realm_update_roles(alice_backend_sock,
                                           certif,
                                           check_rep=False)
            assert rep == {"status": "ok"}

            await spy.wait_with_timeout(
                BackendEvent.REALM_ROLES_UPDATED,
                {
                    "organization_id": alice.organization_id,
                    "author": alice.device_id,
                    "realm_id": realm,
                    "user": bob.user_id,
                    "role": role,
                },
            )

        # Check events propagated to the client
        rep = await events_listen_nowait(bob_backend_sock)
        assert rep == {
            "status": "ok",
            "event": APIEvent.REALM_ROLES_UPDATED,
            "realm_id": realm,
            "role": role,
        }
        rep = await events_listen_nowait(bob_backend_sock)
        assert rep == {"status": "no_events"}
Exemplo n.º 14
0
        async def _create_realm_and_first_vlob(self, device):
            manifest = initial_user_manifest_state.get_user_manifest_v1_for_backend(
                device)
            if manifest.author == device.device_id:
                author = device
            else:
                author = self.get_device(device.organization_id,
                                         manifest.author)
            realm_id = author.user_manifest_id

            with self.backend.event_bus.listen() as spy:

                await self.backend.realm.create(
                    organization_id=author.organization_id,
                    self_granted_role=RealmGrantedRole(
                        realm_id=realm_id,
                        user_id=author.user_id,
                        certificate=RealmRoleCertificateContent(
                            author=author.device_id,
                            timestamp=manifest.timestamp,
                            realm_id=realm_id,
                            user_id=author.user_id,
                            role=RealmRole.OWNER,
                        ).dump_and_sign(author.signing_key),
                        role=RealmRole.OWNER,
                        granted_by=author.device_id,
                        granted_on=manifest.timestamp,
                    ),
                )

                await self.backend.vlob.create(
                    organization_id=author.organization_id,
                    author=author.device_id,
                    realm_id=realm_id,
                    encryption_revision=1,
                    vlob_id=author.user_manifest_id,
                    timestamp=manifest.timestamp,
                    blob=manifest.dump_sign_and_encrypt(
                        author_signkey=author.signing_key,
                        key=author.user_manifest_key),
                )

                # Avoid possible race condition in tests listening for events
                await spy.wait_multiple_with_timeout([
                    (
                        BackendEvent.REALM_ROLES_UPDATED,
                        {
                            "organization_id": author.organization_id,
                            "author": author.device_id,
                            "realm_id": author.user_manifest_id,
                            "user": author.user_id,
                            "role": RealmRole.OWNER,
                        },
                    ),
                    (
                        BackendEvent.REALM_VLOBS_UPDATED,
                        {
                            "organization_id": author.organization_id,
                            "author": author.device_id,
                            "realm_id": author.user_manifest_id,
                            "checkpoint": 1,
                            "src_id": author.user_manifest_id,
                            "src_version": 1,
                        },
                    ),
                ])
Exemplo n.º 15
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: Dict[UserID, RealmRole] = {}
            owner_only = (RealmRole.OWNER, )
            owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)

            # Now verify each certif
            for unsecure_certif, raw_certif in unsecure_certifs:

                with translate_remote_devices_manager_errors():
                    author = await self.remote_devices_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: Tuple[Optional[RealmRole], ...] = (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
                # TODO: typing, author is optional in base.py but it seems that manifests always have an author (no RVK)
                if (current_roles.get(
                        cast(DeviceID, 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 given we have valided it items
        return [c for c, _ in unsecure_certifs], current_roles
Exemplo n.º 16
0
async def _realm_get_clear_role_certifs(sock, realm_id):
    rep = await realm_get_role_certificates(sock, realm_id)
    assert rep["status"] == "ok"
    cooked = [RealmRoleCertificateContent.unsecure_load(certif) for certif in rep["certificates"]]
    return [item for item in sorted(cooked, key=lambda x: x.timestamp)]
Exemplo n.º 17
0
    async def _outbound_sync_inner(self) -> bool:
        base_um = self.get_user_manifest()
        if not base_um.need_sync:
            return True

        # Make sure the corresponding realm has been created in the backend
        if base_um.is_placeholder:
            certif = RealmRoleCertificateContent.build_realm_root_certif(
                author=self.device.device_id,
                timestamp=pendulum_now(),
                realm_id=self.device.user_manifest_id,
            ).dump_and_sign(self.device.signing_key)

            try:
                rep = await self.backend_cmds.realm_create(certif)

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

            except BackendConnectionError as exc:
                raise FSError(
                    f"Cannot create user manifest's realm in backend: {exc}"
                ) from exc

            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.
                pass
            elif rep["status"] != "ok":
                raise FSError(
                    f"Cannot create user manifest's realm in backend: {rep}")

        # Sync placeholders
        for w in base_um.workspaces:
            await self._workspace_minimal_sync(w)

        # Build vlob
        now = pendulum_now()
        to_sync_um = base_um.to_remote(author=self.device.device_id,
                                       timestamp=now)
        ciphered = to_sync_um.dump_sign_and_encrypt(
            author_signkey=self.device.signing_key,
            key=self.device.user_manifest_key)

        # Sync the vlob with backend
        try:
            # Note encryption_revision is always 1 given we never reencrypt
            # the user manifest's realm
            if to_sync_um.version == 1:
                rep = await self.backend_cmds.vlob_create(
                    self.user_manifest_id, 1, self.user_manifest_id, now,
                    ciphered)
            else:
                rep = await self.backend_cmds.vlob_update(
                    1, self.user_manifest_id, to_sync_um.version, now,
                    ciphered)

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

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

        if rep["status"] in ("already_exists", "bad_version"):
            # Concurrency error (handled by the caller)
            return False
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                f"Cannot modify workspace data while it is in maintenance: {rep}"
            )
        elif rep["status"] != "ok":
            raise FSError(f"Cannot sync user manifest: {rep}")

        # Merge back the manifest in local
        async with self._update_user_manifest_lock:
            diverged_um = self.get_user_manifest()
            # Final merge could have been achieved by a concurrent operation
            if to_sync_um.version > diverged_um.base_version:
                merged_um = merge_local_user_manifests(diverged_um, to_sync_um)
                await self.set_user_manifest(merged_um)
            self.event_bus.send(ClientEvent.FS_ENTRY_SYNCED,
                                id=self.user_manifest_id)

        return True
Exemplo n.º 18
0
    async def workspace_share(self, workspace_id: EntryID, recipient: UserID,
                              role: Optional[WorkspaceRole]) -> None:
        """
        Raises:
            FSError
            FSWorkspaceNotFoundError
            FSBackendOfflineError
            FSSharingNotAllowedError
        """
        if self.device.user_id == recipient:
            raise FSError("Cannot share to oneself")

        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}`")

        # Make sure the workspace is not a placeholder
        await self._workspace_minimal_sync(workspace_entry)

        # Retrieve the user
        recipient_user, revoked_recipient_user = await self.remote_loader.get_user(
            recipient)

        if revoked_recipient_user:
            raise FSError(f"User {recipient} revoked")

        # Note we don't bother to check workspace's access roles given they
        # could be outdated (and backend will do the check anyway)

        now = pendulum_now()

        # Build the sharing message
        try:
            if role is not None:
                recipient_message: Union[
                    SharingGrantedMessageContent,
                    SharingRevokedMessageContent] = SharingGrantedMessageContent(
                        author=self.device.device_id,
                        timestamp=now,
                        name=workspace_entry.name,
                        id=workspace_entry.id,
                        encryption_revision=workspace_entry.
                        encryption_revision,
                        encrypted_on=workspace_entry.encrypted_on,
                        key=workspace_entry.key,
                    )

            else:
                recipient_message = SharingRevokedMessageContent(
                    author=self.device.device_id,
                    timestamp=now,
                    id=workspace_entry.id)

            ciphered_recipient_message = recipient_message.dump_sign_and_encrypt_for(
                author_signkey=self.device.signing_key,
                recipient_pubkey=recipient_user.public_key)

        except DataError as exc:
            raise FSError(
                f"Cannot create sharing message for `{recipient}`: {exc}"
            ) from exc

        # Build role certificate
        role_certificate = RealmRoleCertificateContent(
            author=self.device.device_id,
            timestamp=now,
            realm_id=workspace_id,
            user_id=recipient,
            role=role,
        ).dump_and_sign(self.device.signing_key)

        # Actually send the command to the backend
        try:
            rep = await self.backend_cmds.realm_update_roles(
                role_certificate, ciphered_recipient_message)

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

        except BackendConnectionError as exc:
            raise FSError(
                f"Error while trying to set vlob group roles in backend: {exc}"
            ) from exc

        if rep["status"] == "not_allowed":
            raise FSSharingNotAllowedError(
                f"Must be Owner or Manager on the workspace is mandatory to share it: {rep}"
            )
        elif rep["status"] == "in_maintenance":
            raise FSWorkspaceInMaintenance(
                f"Cannot share workspace while it is in maintenance: {rep}")
        elif rep["status"] == "already_granted":
            # Stay idempotent
            return
        elif rep["status"] != "ok":
            raise FSError(
                f"Error while trying to set vlob group roles in backend: {rep}"
            )
Exemplo n.º 19
0
async def test_create_realm_already_exists(backend, alice, alice_backend_sock, realm):
    certif = RealmRoleCertificateContent.build_realm_root_certif(
        author=alice.device_id, timestamp=pendulum.now(), realm_id=realm
    ).dump_and_sign(alice.signing_key)
    rep = await realm_create(alice_backend_sock, certif)
    assert rep == {"status": "already_exists"}
Exemplo n.º 20
0
    async def api_realm_create(self, client_ctx, msg):
        if client_ctx.profile == UserProfile.OUTSIDER:
            return {
                "status": "not_allowed",
                "reason": "Outsider user cannot create realm"
            }

        msg = realm_create_serializer.req_load(msg)

        try:
            data = RealmRoleCertificateContent.verify_and_load(
                msg["role_certificate"],
                author_verify_key=client_ctx.verify_key,
                expected_author=client_ctx.device_id,
            )

        except DataError as exc:
            return {
                "status": "invalid_certification",
                "reason": f"Invalid certification data ({exc}).",
            }

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

        granted_role = RealmGrantedRole(
            certificate=msg["role_certificate"],
            realm_id=data.realm_id,
            user_id=data.user_id,
            role=data.role,
            granted_by=data.author,
            granted_on=data.timestamp,
        )
        if granted_role.granted_by.user_id != granted_role.user_id:
            return {
                "status": "invalid_data",
                "reason":
                "Initial realm role certificate must be self-signed.",
            }
        if granted_role.role != RealmRole.OWNER:
            return {
                "status": "invalid_data",
                "reason":
                "Initial realm role certificate must set OWNER role.",
            }

        try:
            await self.create(client_ctx.organization_id, granted_role)

        except RealmNotFoundError as exc:
            return realm_create_serializer.rep_dump({
                "status": "not_found",
                "reason": str(exc)
            })

        except RealmAlreadyExistsError:
            return realm_create_serializer.rep_dump(
                {"status": "already_exists"})

        return realm_create_serializer.rep_dump({"status": "ok"})
Exemplo n.º 21
0
    async def api_realm_update_roles(self, client_ctx, msg):
        msg = realm_update_roles_serializer.req_load(msg)

        try:
            data = RealmRoleCertificateContent.verify_and_load(
                msg["role_certificate"],
                author_verify_key=client_ctx.verify_key,
                expected_author=client_ctx.device_id,
            )

        except DataError as exc:
            return {
                "status": "invalid_certification",
                "reason": f"Invalid certification data ({exc}).",
            }

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

        granted_role = RealmGrantedRole(
            certificate=msg["role_certificate"],
            realm_id=data.realm_id,
            user_id=data.user_id,
            role=data.role,
            granted_by=data.author,
            granted_on=data.timestamp,
        )
        if granted_role.granted_by.user_id == granted_role.user_id:
            return {
                "status": "invalid_data",
                "reason": "Realm role certificate cannot be self-signed.",
            }

        try:
            await self.update_roles(client_ctx.organization_id, granted_role,
                                    msg["recipient_message"])

        except RealmRoleAlreadyGranted:
            return realm_update_roles_serializer.rep_dump(
                {"status": "already_granted"})

        except RealmAccessError:
            return realm_update_roles_serializer.rep_dump(
                {"status": "not_allowed"})

        except RealmIncompatibleProfileError as exc:
            return realm_update_roles_serializer.rep_dump({
                "status": "incompatible_profile",
                "reason": str(exc)
            })

        except RealmNotFoundError as exc:
            return realm_update_roles_serializer.rep_dump({
                "status": "not_found",
                "reason": str(exc)
            })

        except RealmInMaintenanceError:
            return realm_update_roles_serializer.rep_dump(
                {"status": "in_maintenance"})

        return realm_update_roles_serializer.rep_dump({"status": "ok"})