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, )
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)
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)
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, )
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, )
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, )
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)
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, )