Esempio n. 1
0
    async def finish_reencryption_maintenance(
        self,
        organization_id: OrganizationID,
        author: DeviceID,
        realm_id: RealmID,
        encryption_revision: int,
    ) -> None:
        realm = self._get_realm(organization_id, realm_id)
        if realm.roles.get(author.user_id) != RealmRole.OWNER:
            raise RealmAccessError()
        if not realm.status.in_maintenance:
            raise RealmNotInMaintenanceError(
                f"Realm `{realm_id}` not under maintenance")
        if encryption_revision != realm.status.encryption_revision:
            raise RealmEncryptionRevisionError("Invalid encryption revision")
        if not self._vlob_component._maintenance_reencryption_is_finished_hook(
                organization_id, realm_id, encryption_revision):
            raise RealmMaintenanceError("Reencryption operations are not over")

        realm.status = RealmStatus(
            maintenance_type=None,
            maintenance_started_on=None,
            maintenance_started_by=None,
            encryption_revision=encryption_revision,
        )

        await self._send_event(
            BackendEvent.REALM_MAINTENANCE_FINISHED,
            organization_id=organization_id,
            author=author,
            realm_id=realm_id,
            encryption_revision=encryption_revision,
        )
Esempio n. 2
0
async def query_get_stats(
    conn, organization_id: OrganizationID, author: DeviceID, realm_id: UUID
) -> RealmStats:
    ret = await conn.fetchrow(
        *_q_has_realm_access(
            organization_id=organization_id, realm_id=realm_id, user_id=author.user_id
        )
    )
    if not ret:
        raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")

    if not ret["has_access"]:
        raise RealmAccessError()
    blocks_size = await conn.fetch(
        *_q_get_blocks_size_from_realm(organization_id=organization_id, realm_id=realm_id)
    )
    vlobs_size = await conn.fetch(
        *_q_get_vlob_size_from_realm(organization_id=organization_id, realm_id=realm_id)
    )
    RealmStats.blocks_size = 0
    RealmStats.vlobs_size = 0
    if "sum" in blocks_size[0] and blocks_size[0]["sum"] is not None:
        RealmStats.blocks_size = blocks_size[0]["sum"]
    if "sum" in vlobs_size[0] and vlobs_size[0]["sum"] is not None:
        RealmStats.vlobs_size = vlobs_size[0]["sum"]

    return RealmStats
Esempio n. 3
0
 async def get_status(
     self, organization_id: OrganizationID, author: DeviceID, realm_id: UUID
 ) -> RealmStatus:
     realm = self._get_realm(organization_id, realm_id)
     if author.user_id not in realm.roles:
         raise RealmAccessError()
     return realm.status
Esempio n. 4
0
    async def update_roles(
        self,
        organization_id: OrganizationID,
        new_role: RealmGrantedRole,
        recipient_message: Optional[bytes] = None,
    ) -> None:
        assert new_role.granted_by.user_id != new_role.user_id

        try:
            user = self._user_component._get_user(organization_id, new_role.user_id)
        except UserNotFoundError:
            raise RealmNotFoundError(f"User `{new_role.user_id}` doesn't exist")

        if user.profile == UserProfile.OUTSIDER and new_role.role in (
            RealmRole.MANAGER,
            RealmRole.OWNER,
        ):
            raise RealmIncompatibleProfileError(
                "User with OUTSIDER profile cannot be MANAGER or OWNER"
            )

        realm = self._get_realm(organization_id, new_role.realm_id)

        if realm.status.in_maintenance:
            raise RealmInMaintenanceError("Data realm is currently under maintenance")

        owner_only = (RealmRole.OWNER,)
        owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)
        existing_user_role = realm.roles.get(new_role.user_id)
        if existing_user_role in owner_or_manager or new_role.role in owner_or_manager:
            needed_roles = owner_only
        else:
            needed_roles = owner_or_manager

        author_role = realm.roles.get(new_role.granted_by.user_id)
        if author_role not in needed_roles:
            raise RealmAccessError()

        if existing_user_role == new_role.role:
            raise RealmRoleAlreadyGranted()

        realm.granted_roles.append(new_role)

        await self._send_event(
            "realm.roles_updated",
            organization_id=organization_id,
            author=new_role.granted_by,
            realm_id=new_role.realm_id,
            user=new_role.user_id,
            role=new_role.role,
        )

        if recipient_message is not None:
            await self._message_component.send(
                organization_id,
                new_role.granted_by,
                new_role.user_id,
                new_role.granted_on,
                recipient_message,
            )
Esempio n. 5
0
 async def get_role_certificates(self, organization_id: OrganizationID,
                                 author: DeviceID,
                                 realm_id: RealmID) -> List[bytes]:
     realm = self._get_realm(organization_id, realm_id)
     if author.user_id not in realm.roles:
         raise RealmAccessError()
     return [x.certificate for x in realm.granted_roles]
Esempio n. 6
0
async def query_get_role_certificates(
    conn,
    organization_id: OrganizationID,
    author: DeviceID,
    realm_id: UUID,
    since: pendulum.Pendulum,
) -> List[bytes]:
    ret = await conn.fetch(_q_get_role_certificates, organization_id, realm_id)

    if not ret:
        # Existing group must have at least one owner user
        raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")

    out = []
    author_current_role = None
    for user_id, role, certif, certified_on in ret:
        if not since or certified_on > since:
            out.append(certif)
        if user_id == author.user_id:
            author_current_role = role

    if author_current_role is None:
        raise RealmAccessError()

    return out
Esempio n. 7
0
async def query_start_reencryption_maintenance(
    conn,
    organization_id: OrganizationID,
    author: DeviceID,
    realm_id: RealmID,
    encryption_revision: int,
    per_participant_message: Dict[UserID, bytes],
    timestamp: pendulum.DateTime,
) -> None:
    # Retrieve realm and make sure it is not under maintenance
    status = await get_realm_status(conn, organization_id, realm_id)
    if status.in_maintenance:
        raise RealmInMaintenanceError(
            f"Realm `{realm_id}` alrealy in maintenance")
    if encryption_revision != status.encryption_revision + 1:
        raise RealmEncryptionRevisionError("Invalid encryption revision")

    roles = await _get_realm_role_for_not_revoked(conn, organization_id,
                                                  realm_id)

    if roles.get(author.user_id) != RealmRole.OWNER:
        raise RealmAccessError()

    if per_participant_message.keys() ^ roles.keys():
        raise RealmParticipantsMismatchError(
            "Realm participants and message recipients mismatch")

    await conn.execute(*_q_query_start_reencryption_maintenance_update_realm(
        organization_id=organization_id.str,
        realm_id=realm_id.uuid,
        maintenance_started_by=author.str,
        maintenance_started_on=timestamp,
        maintenance_type="REENCRYPTION",
        encryption_revision=encryption_revision,
    ))

    await conn.execute(
        *
        _q_query_start_reencryption_maintenance_update_vlob_encryption_revision(
            organization_id=organization_id.str,
            realm_id=realm_id.uuid,
            encryption_revision=encryption_revision,
        ))

    await send_signal(
        conn,
        BackendEvent.REALM_MAINTENANCE_STARTED,
        organization_id=organization_id,
        author=author,
        realm_id=realm_id,
        encryption_revision=encryption_revision,
    )

    for recipient, body in per_participant_message.items():
        await send_message(conn, organization_id, author, recipient, timestamp,
                           body)
Esempio n. 8
0
    async def start_reencryption_maintenance(
        self,
        organization_id: OrganizationID,
        author: DeviceID,
        realm_id: RealmID,
        encryption_revision: int,
        per_participant_message: Dict[UserID, bytes],
        timestamp: pendulum.DateTime,
    ) -> None:
        realm = self._get_realm(organization_id, realm_id)
        if realm.roles.get(author.user_id) != RealmRole.OWNER:
            raise RealmAccessError()
        if realm.status.in_maintenance:
            raise RealmInMaintenanceError(
                f"Realm `{realm_id}` alrealy in maintenance")
        if encryption_revision != realm.status.encryption_revision + 1:
            raise RealmEncryptionRevisionError("Invalid encryption revision")
        now = pendulum.now()
        not_revoked_roles = set()
        for user_id in realm.roles.keys():
            user = await self._user_component.get_user(organization_id,
                                                       user_id)
            if not user.revoked_on or user.revoked_on > now:
                not_revoked_roles.add(user_id)
        if per_participant_message.keys() ^ not_revoked_roles:
            raise RealmParticipantsMismatchError(
                "Realm participants and message recipients mismatch")

        realm.status = RealmStatus(
            maintenance_type=MaintenanceType.REENCRYPTION,
            maintenance_started_on=timestamp,
            maintenance_started_by=author,
            encryption_revision=encryption_revision,
        )
        self._vlob_component._maintenance_reencryption_start_hook(
            organization_id, realm_id, encryption_revision)

        # Should first send maintenance event, then message to each participant

        await self._send_event(
            BackendEvent.REALM_MAINTENANCE_STARTED,
            organization_id=organization_id,
            author=author,
            realm_id=realm_id,
            encryption_revision=encryption_revision,
        )

        for recipient, msg in per_participant_message.items():
            await self._message_component.send(organization_id, author,
                                               recipient, timestamp, msg)
Esempio n. 9
0
 async def get_role_certificates(
     self,
     organization_id: OrganizationID,
     author: DeviceID,
     realm_id: UUID,
     since: pendulum.Pendulum,
 ) -> List[bytes]:
     realm = self._get_realm(organization_id, realm_id)
     if author.user_id not in realm.roles:
         raise RealmAccessError()
     if since:
         return [x.certificate for x in realm.granted_roles if x.granted_on > since]
     else:
         return [x.certificate for x in realm.granted_roles]
Esempio n. 10
0
    async def get_stats(self, organization_id: OrganizationID,
                        author: DeviceID, realm_id: RealmID) -> RealmStats:
        realm = self._get_realm(organization_id, realm_id)
        if author.user_id not in realm.roles:
            raise RealmAccessError()

        blocks_size = 0
        vlobs_size = 0
        for value in self._block_component._blockmetas.values():
            if value.realm_id == realm_id:
                blocks_size += value.size
        for value in self._vlob_component._vlobs.values():
            if value.realm_id == realm_id:
                vlobs_size += sum(len(blob) for (blob, _, _) in value.data)

        return RealmStats(blocks_size=blocks_size, vlobs_size=vlobs_size)
Esempio n. 11
0
async def query_finish_reencryption_maintenance(
    conn,
    organization_id: OrganizationID,
    author: DeviceID,
    realm_id: RealmID,
    encryption_revision: int,
) -> None:
    # Retrieve realm and make sure it is not under maintenance
    status = await get_realm_status(conn, organization_id, realm_id)
    roles = await _get_realm_role_for_not_revoked(conn, organization_id,
                                                  realm_id, [author.user_id])
    if roles.get(author.user_id) != RealmRole.OWNER:
        raise RealmAccessError()
    if not status.in_maintenance:
        raise RealmNotInMaintenanceError(
            f"Realm `{realm_id}` not under maintenance")
    if encryption_revision != status.encryption_revision:
        raise RealmEncryptionRevisionError("Invalid encryption revision")

    # Test reencryption operations are over
    rep = await conn.fetch(*_query_finish_reencryption_maintenance_get_info(
        organization_id=organization_id.str,
        realm_id=realm_id,
        encryption_revision=encryption_revision,
    ))

    try:
        previous, current = rep
    except ValueError:
        raise RealmMaintenanceError("Reencryption operations are not over")
    assert previous["encryption_revision"] == encryption_revision - 1
    assert current["encryption_revision"] == encryption_revision
    assert previous["count"] >= current["count"]
    if previous["count"] != current["count"]:
        raise RealmMaintenanceError("Reencryption operations are not over")

    await conn.execute(*_query_finish_reencryption_maintenance_update_realm(
        organization_id=organization_id.str, realm_id=realm_id))

    await send_signal(
        conn,
        BackendEvent.REALM_MAINTENANCE_FINISHED,
        organization_id=organization_id,
        author=author,
        realm_id=realm_id,
        encryption_revision=encryption_revision,
    )
Esempio n. 12
0
async def query_get_status(conn, organization_id: OrganizationID,
                           author: DeviceID, realm_id: UUID) -> RealmStatus:
    ret = await conn.fetchrow(_q_get_realm_status, organization_id, realm_id,
                              author.user_id)
    if not ret:
        raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")

    if not ret["has_access"]:
        raise RealmAccessError()

    return RealmStatus(
        maintenance_type=STR_TO_REALM_MAINTENANCE_TYPE.get(
            ret["maintenance_type"]),
        maintenance_started_on=ret["maintenance_started_on"],
        maintenance_started_by=ret["maintenance_started_by"],
        encryption_revision=ret["encryption_revision"],
    )
Esempio n. 13
0
async def query_get_stats(conn, organization_id: OrganizationID,
                          author: DeviceID, realm_id: RealmID) -> RealmStats:
    ret = await conn.fetchrow(
        *_q_has_realm_access(organization_id=organization_id.str,
                             realm_id=realm_id.uuid,
                             user_id=author.user_id.str))
    if not ret:
        raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")

    if not ret["has_access"]:
        raise RealmAccessError()
    blocks_size_rep = await conn.fetchrow(*_q_get_blocks_size_from_realm(
        organization_id=organization_id.str, realm_id=realm_id.uuid))
    vlobs_size_rep = await conn.fetchrow(*_q_get_vlob_size_from_realm(
        organization_id=organization_id.str, realm_id=realm_id.uuid))

    blocks_size = blocks_size_rep["sum"] or 0
    vlobs_size = vlobs_size_rep["sum"] or 0

    return RealmStats(blocks_size=blocks_size, vlobs_size=vlobs_size)
Esempio n. 14
0
async def query_get_status(conn, organization_id: OrganizationID,
                           author: DeviceID, realm_id: RealmID) -> RealmStatus:
    ret = await conn.fetchrow(
        *_q_get_realm_status(organization_id=organization_id.str,
                             realm_id=realm_id.uuid,
                             user_id=author.user_id.str))
    if not ret:
        raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")

    if not ret["has_access"]:
        raise RealmAccessError()

    return RealmStatus(
        maintenance_type=MaintenanceType(ret["maintenance_type"])
        if ret["maintenance_type"] else None,
        maintenance_started_on=ret["maintenance_started_on"],
        maintenance_started_by=DeviceID(ret["maintenance_started_by"])
        if ret["maintenance_started_by"] else None,
        encryption_revision=ret["encryption_revision"],
    )
Esempio n. 15
0
async def query_get_role_certificates(conn, organization_id: OrganizationID,
                                      author: DeviceID,
                                      realm_id: RealmID) -> List[bytes]:
    ret = await conn.fetch(*_q_get_role_certificates(
        organization_id=organization_id.str, realm_id=realm_id.uuid))

    if not ret:
        # Existing group must have at least one owner user
        raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")

    out = []
    author_current_role = None
    for user_id, role, certif, _ in ret:
        user_id = UserID(user_id)
        out.append(certif)
        if user_id == author.user_id:
            author_current_role = role

    if author_current_role is None:
        raise RealmAccessError()

    return out
Esempio n. 16
0
async def query_start_reencryption_maintenance(
    conn,
    organization_id: OrganizationID,
    author: DeviceID,
    realm_id: UUID,
    encryption_revision: int,
    per_participant_message: Dict[UserID, bytes],
    timestamp: pendulum.Pendulum,
) -> None:
    # Retrieve realm and make sure it is not under maintenance
    rep = await get_realm_status(conn, organization_id, realm_id)
    if rep["maintenance_type"]:
        raise RealmInMaintenanceError(
            f"Realm `{realm_id}` alrealy in maintenance")
    if encryption_revision != rep["encryption_revision"] + 1:
        raise RealmEncryptionRevisionError("Invalid encryption revision")

    roles = await get_realm_role_for_not_revoked(conn, organization_id,
                                                 realm_id)
    if per_participant_message.keys() ^ roles.keys():
        raise RealmParticipantsMismatchError(
            "Realm participants and message recipients mismatch")

    if roles.get(author.user_id) != RealmRole.OWNER:
        raise RealmAccessError()

    query = """
UPDATE realm
SET
    encryption_revision=$6,
    maintenance_started_by=({}),
    maintenance_started_on=$4,
    maintenance_type=$5
WHERE
    _id = ({})
""".format(
        q_device_internal_id(organization_id=Parameter("$1"),
                             device_id=Parameter("$3")),
        q_realm_internal_id(organization_id=Parameter("$1"),
                            realm_id=Parameter("$2")),
    )

    await conn.execute(query, organization_id, realm_id, author, timestamp,
                       "REENCRYPTION", encryption_revision)

    query = """
INSERT INTO vlob_encryption_revision(
    realm,
    encryption_revision
) SELECT
    ({}),
    $3
""".format(
        q_realm_internal_id(organization_id=Parameter("$1"),
                            realm_id=Parameter("$2")))

    await conn.execute(query, organization_id, realm_id, encryption_revision)

    await send_signal(
        conn,
        "realm.maintenance_started",
        organization_id=organization_id,
        author=author,
        realm_id=realm_id,
        encryption_revision=encryption_revision,
    )

    for recipient, body in per_participant_message.items():
        await send_message(conn, organization_id, author, recipient, timestamp,
                           body)
Esempio n. 17
0
    async def update_roles(
        self,
        organization_id: OrganizationID,
        new_role: RealmGrantedRole,
        recipient_message: Optional[bytes] = None,
    ) -> None:
        assert new_role.granted_by is not None
        assert new_role.granted_by.user_id != new_role.user_id

        # The only way for an OUTSIDER to be OWNER is to create his own realm
        # (given he needs to have one to store it user manifest).
        try:
            user = self._user_component._get_user(organization_id,
                                                  new_role.user_id)
        except UserNotFoundError:
            raise RealmNotFoundError(
                f"User `{new_role.user_id}` doesn't exist")

        if user.profile == UserProfile.OUTSIDER and new_role.role in (
                RealmRole.MANAGER,
                RealmRole.OWNER,
        ):
            raise RealmIncompatibleProfileError(
                "User with OUTSIDER profile cannot be MANAGER or OWNER")

        realm = self._get_realm(organization_id, new_role.realm_id)

        if realm.status.in_maintenance:
            raise RealmInMaintenanceError(
                "Data realm is currently under maintenance")

        owner_only = (RealmRole.OWNER, )
        owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)
        existing_user_role = realm.roles.get(new_role.user_id)
        needed_roles: Tuple[RealmRole, ...]
        if existing_user_role in owner_or_manager or new_role.role in owner_or_manager:
            needed_roles = owner_only
        else:
            needed_roles = owner_or_manager

        author_role = realm.roles.get(new_role.granted_by.user_id)
        if author_role not in needed_roles:
            raise RealmAccessError()

        if existing_user_role == new_role.role:
            raise RealmRoleAlreadyGranted()

        # Timestamps for the role certificates of a given user should be striclty increasing
        last_role = realm.get_last_role(new_role.user_id)
        if last_role is not None and last_role.granted_on >= new_role.granted_on:
            raise RealmRoleRequireGreaterTimestampError(last_role.granted_on)

        # Perfrom extra checks when removing write rights
        if new_role.role in (RealmRole.READER, None):

            # The change of role needs to occur strictly after the last upload for this user
            realm_last_vlob_update = self._vlob_component._get_last_vlob_update(
                organization_id, new_role.realm_id, new_role.user_id)
            if realm_last_vlob_update is not None and realm_last_vlob_update >= new_role.granted_on:
                raise RealmRoleRequireGreaterTimestampError(
                    realm_last_vlob_update)

        # Perform extra checks when removing management rights
        if new_role.role in (RealmRole.CONTRIBUTOR, RealmRole.READER, None):

            # The change of role needs to occur strictly after the last change of role performed by this user
            realm_last_role_change = realm.last_role_change_per_user.get(
                new_role.user_id)
            if realm_last_role_change is not None and realm_last_role_change >= new_role.granted_on:
                raise RealmRoleRequireGreaterTimestampError(
                    realm_last_role_change)

        # Update role and record last change timestamp for this user
        realm.granted_roles.append(new_role)
        author_user_id = new_role.granted_by.user_id
        current_value = realm.last_role_change_per_user.get(author_user_id)
        realm.last_role_change_per_user[author_user_id] = (
            new_role.granted_on if current_value is None else max(
                current_value, new_role.granted_on))

        await self._send_event(
            BackendEvent.REALM_ROLES_UPDATED,
            organization_id=organization_id,
            author=new_role.granted_by,
            realm_id=new_role.realm_id,
            user=new_role.user_id,
            role=new_role.role,
        )

        if recipient_message is not None:
            await self._message_component.send(
                organization_id,
                new_role.granted_by,
                new_role.user_id,
                new_role.granted_on,
                recipient_message,
            )
Esempio n. 18
0
async def query_update_roles(
    conn,
    organization_id: OrganizationID,
    new_role: RealmGrantedRole,
    recipient_message: Optional[bytes],
) -> None:
    assert new_role.granted_by is not None
    if new_role.granted_by.user_id == new_role.user_id:
        raise RealmAccessError("Cannot modify our own role")

    # Make sure user profile is compatible
    rep = await conn.fetchrow(*_q_get_user_profile(
        organization_id=organization_id, user_id=new_role.user_id))
    if not rep:
        raise RealmNotFoundError(f"User `{new_role.user_id}` doesn't exist")
    if rep["profile"] == UserProfile.OUTSIDER.value and new_role.role in (
            RealmRole.MANAGER,
            RealmRole.OWNER,
    ):
        raise RealmIncompatibleProfileError(
            "User with OUTSIDER profile cannot be MANAGER or OWNER")

    # Retrieve realm and make sure it is not under maintenance
    rep = await conn.fetchrow(*_q_get_realm_status(
        organization_id=organization_id, realm_id=new_role.realm_id))
    if not rep:
        raise RealmNotFoundError(f"Realm `{new_role.realm_id}` doesn't exist")
    if rep["maintenance_type"]:
        raise RealmInMaintenanceError(
            "Data realm is currently under maintenance")

    # Check access rights and user existance
    ((author_id, author_role),
     (user_id, existing_user_role)) = await conn.fetch(*_q_get_roles(
         organization_id=organization_id,
         realm_id=new_role.realm_id,
         users_ids=(new_role.granted_by.user_id, new_role.user_id),
     ))
    assert author_id
    assert user_id

    author_role = STR_TO_REALM_ROLE.get(author_role)
    existing_user_role = STR_TO_REALM_ROLE.get(existing_user_role)
    owner_only = (RealmRole.OWNER, )
    owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)

    needed_roles: Tuple[RealmRole, ...]
    if existing_user_role in owner_or_manager or new_role.role in owner_or_manager:
        needed_roles = owner_only
    else:
        needed_roles = owner_or_manager

    if author_role not in needed_roles:
        raise RealmAccessError()

    if existing_user_role == new_role.role:
        raise RealmRoleAlreadyGranted()

    await conn.execute(*_q_insert_realm_user_role(
        organization_id=organization_id,
        realm_id=new_role.realm_id,
        user_id=new_role.user_id,
        role=new_role.role.value if new_role.role else None,
        certificate=new_role.certificate,
        granted_by=new_role.granted_by,
        granted_on=new_role.granted_on,
    ))

    await send_signal(
        conn,
        BackendEvent.REALM_ROLES_UPDATED,
        organization_id=organization_id,
        author=new_role.granted_by,
        realm_id=new_role.realm_id,
        user=new_role.user_id,
        role_str=new_role.role.value if new_role.role else None,
    )

    if recipient_message:
        await send_message(
            conn,
            organization_id,
            new_role.granted_by,
            new_role.user_id,
            new_role.granted_on,
            recipient_message,
        )
Esempio n. 19
0
async def query_update_roles(
    conn,
    organization_id: OrganizationID,
    new_role: RealmGrantedRole,
    recipient_message: Optional[bytes],
) -> None:
    if new_role.granted_by.user_id == new_role.user_id:
        raise RealmAccessError("Cannot modify our own role")

    # Retrieve realm and make sure it is not under maintenance
    rep = await conn.fetchrow(_q_get_realm_status, organization_id, new_role.realm_id)
    if not rep:
        raise RealmNotFoundError(f"Realm `{new_role.realm_id}` doesn't exist")
    if rep["maintenance_type"]:
        raise RealmInMaintenanceError("Data realm is currently under maintenance")

    # Check access rights and user existance
    ((author_id, author_role), (user_id, existing_user_role)) = await conn.fetch(
        _q_get_roles,
        organization_id,
        new_role.realm_id,
        (new_role.granted_by.user_id, new_role.user_id),
    )
    assert author_id
    if not user_id:
        raise RealmNotFoundError(f"User `{new_role.user_id}` doesn't exist")

    author_role = STR_TO_REALM_ROLE.get(author_role)
    existing_user_role = STR_TO_REALM_ROLE.get(existing_user_role)
    owner_only = (RealmRole.OWNER,)
    owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)
    if existing_user_role in owner_or_manager or new_role.role in owner_or_manager:
        needed_roles = owner_only
    else:
        needed_roles = owner_or_manager

    if author_role not in needed_roles:
        raise RealmAccessError()

    if existing_user_role == new_role.role:
        raise RealmRoleAlreadyGranted()

    await conn.execute(
        _q_insert_realm_user_role,
        organization_id,
        new_role.realm_id,
        new_role.user_id,
        new_role.role.value if new_role.role else None,
        new_role.certificate,
        new_role.granted_by,
        new_role.granted_on,
    )

    await send_signal(
        conn,
        "realm.roles_updated",
        organization_id=organization_id,
        author=new_role.granted_by,
        realm_id=new_role.realm_id,
        user=new_role.user_id,
        role_str=new_role.role.value if new_role.role else None,
    )

    if recipient_message:
        await send_message(
            conn,
            organization_id,
            new_role.granted_by,
            new_role.user_id,
            new_role.granted_on,
            recipient_message,
        )
Esempio n. 20
0
async def query_update_roles(
    conn,
    organization_id: OrganizationID,
    new_role: RealmGrantedRole,
    recipient_message: Optional[bytes],
) -> None:
    assert new_role.granted_by is not None
    if new_role.granted_by.user_id == new_role.user_id:
        raise RealmAccessError("Cannot modify our own role")

    # Make sure user profile is compatible
    rep = await conn.fetchrow(*_q_get_user_profile(
        organization_id=organization_id.str, user_id=new_role.user_id.str))
    if not rep:
        raise RealmNotFoundError(f"User `{new_role.user_id}` doesn't exist")
    if rep["profile"] == UserProfile.OUTSIDER.value and new_role.role in (
            RealmRole.MANAGER,
            RealmRole.OWNER,
    ):
        raise RealmIncompatibleProfileError(
            "User with OUTSIDER profile cannot be MANAGER or OWNER")

    # Retrieve realm and make sure it is not under maintenance
    rep = await conn.fetchrow(*_q_get_realm_status(
        organization_id=organization_id.str, realm_id=new_role.realm_id.uuid))
    if not rep:
        raise RealmNotFoundError(f"Realm `{new_role.realm_id}` doesn't exist")
    if rep["maintenance_type"]:
        raise RealmInMaintenanceError(
            "Data realm is currently under maintenance")

    # Check access rights and user existance
    ((author_id, author_role),
     (user_id, existing_user_role)) = await conn.fetch(*_q_get_roles(
         organization_id=organization_id.str,
         realm_id=new_role.realm_id.uuid,
         users_ids=(new_role.granted_by.user_id.str, new_role.user_id.str),
     ))
    assert author_id
    assert user_id

    if author_role is not None:
        author_role, _ = author_role
        if author_role is not None:
            author_role = RealmRole(author_role)

    last_role_granted_on = None
    if existing_user_role is not None:
        existing_user_role, last_role_granted_on = existing_user_role
        if existing_user_role is not None:
            existing_user_role = RealmRole(existing_user_role)

    owner_only = (RealmRole.OWNER, )
    owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER)

    needed_roles: Tuple[RealmRole, ...]
    if existing_user_role in owner_or_manager or new_role.role in owner_or_manager:
        needed_roles = owner_only
    else:
        needed_roles = owner_or_manager

    if author_role not in needed_roles:
        raise RealmAccessError()

    if existing_user_role == new_role.role:
        raise RealmRoleAlreadyGranted()

    # Timestamps for the role certificates of a given user should be striclty increasing
    if last_role_granted_on is not None and last_role_granted_on >= new_role.granted_on:
        raise RealmRoleRequireGreaterTimestampError(last_role_granted_on)

    # Perfrom extra checks when removing write rights
    if new_role.role in (RealmRole.READER, None):

        # The change of role needs to occur strictly after the last upload for this user
        rep = await conn.fetchrow(*_q_get_last_vlob_update(
            organization_id=organization_id.str,
            realm_id=new_role.realm_id.uuid,
            user_id=new_role.user_id.str,
        ))
        realm_last_vlob_update = None if not rep else rep[0]
        if realm_last_vlob_update is not None and realm_last_vlob_update >= new_role.granted_on:
            raise RealmRoleRequireGreaterTimestampError(realm_last_vlob_update)

    # Perform extra checks when removing management rights
    if new_role.role in (RealmRole.CONTRIBUTOR, RealmRole.READER, None):

        # The change of role needs to occur strictly after the last change of role performed by this user
        rep = await conn.fetchrow(*_q_get_last_role_change(
            organization_id=organization_id.str,
            realm_id=new_role.realm_id.uuid,
            user_id=new_role.user_id.str,
        ))
        realm_last_role_change = None if not rep else rep[0]
        if realm_last_role_change is not None and realm_last_role_change >= new_role.granted_on:
            raise RealmRoleRequireGreaterTimestampError(realm_last_role_change)

    await conn.execute(*_q_insert_realm_user_role(
        organization_id=organization_id.str,
        realm_id=new_role.realm_id.uuid,
        user_id=new_role.user_id.str,
        role=new_role.role.value if new_role.role else None,
        certificate=new_role.certificate,
        granted_by=new_role.granted_by.str,
        granted_on=new_role.granted_on,
    ))

    await conn.execute(*_q_set_last_role_change(
        organization_id=organization_id.str,
        realm_id=new_role.realm_id.uuid,
        user_id=new_role.granted_by.user_id.str,
        granted_on=new_role.granted_on,
    ))

    await send_signal(
        conn,
        BackendEvent.REALM_ROLES_UPDATED,
        organization_id=organization_id,
        author=new_role.granted_by,
        realm_id=new_role.realm_id,
        user=new_role.user_id,
        role=new_role.role,
    )

    if recipient_message:
        await send_message(
            conn,
            organization_id,
            new_role.granted_by,
            new_role.user_id,
            new_role.granted_on,
            recipient_message,
        )
Esempio n. 21
0
async def query_finish_reencryption_maintenance(
    conn,
    organization_id: OrganizationID,
    author: DeviceID,
    realm_id: UUID,
    encryption_revision: int,
) -> None:
    # Retrieve realm and make sure it is not under maintenance
    rep = await get_realm_status(conn, organization_id, realm_id)
    roles = await get_realm_role_for_not_revoked(conn, organization_id,
                                                 realm_id, [author.user_id])
    if roles.get(author.user_id) != RealmRole.OWNER:
        raise RealmAccessError()
    if not rep["maintenance_type"]:
        raise RealmNotInMaintenanceError(
            f"Realm `{realm_id}` not under maintenance")
    if encryption_revision != rep["encryption_revision"]:
        raise RealmEncryptionRevisionError("Invalid encryption revision")

    # Test reencryption operations are over

    query = """
WITH cte_encryption_revisions AS (
    SELECT
        _id,
        encryption_revision
    FROM vlob_encryption_revision
    WHERE
        realm = ({})
        AND (encryption_revision = $3 OR encryption_revision = $3 - 1)
)
SELECT encryption_revision, COUNT(*) as count
FROM vlob_atom
INNER JOIN cte_encryption_revisions
ON cte_encryption_revisions._id = vlob_atom.vlob_encryption_revision
GROUP BY encryption_revision
ORDER BY encryption_revision
    """.format(
        q_realm_internal_id(organization_id=Parameter("$1"),
                            realm_id=Parameter("$2")))

    rep = await conn.fetch(query, organization_id, realm_id,
                           encryption_revision)

    try:
        previous, current = rep
    except ValueError:
        raise RealmMaintenanceError("Reencryption operations are not over")
    assert previous["encryption_revision"] == encryption_revision - 1
    assert current["encryption_revision"] == encryption_revision
    assert previous["count"] >= current["count"]
    if previous["count"] != current["count"]:
        raise RealmMaintenanceError("Reencryption operations are not over")

    query = """
UPDATE realm
SET
    maintenance_started_by=NULL,
    maintenance_started_on=NULL,
    maintenance_type=NULL
WHERE
    _id = ({})
""".format(
        q_realm_internal_id(organization_id=Parameter("$1"),
                            realm_id=Parameter("$2")))

    await conn.execute(query, organization_id, realm_id)

    await send_signal(
        conn,
        "realm.maintenance_finished",
        organization_id=organization_id,
        author=author,
        realm_id=realm_id,
        encryption_revision=encryption_revision,
    )