async def _get_realm_role_for_not_revoked( conn, organization_id: OrganizationID, realm_id: RealmID, users: Optional[List[UserID]] = None): now = pendulum.now() def _cook_role(row): if row["revoked_on"] and row["revoked_on"] <= now: return None if row["role"] is None: return None return RealmRole(row["role"]) if users: rep = await conn.fetch(*_q_get_realm_role_for_not_revoked_with_users( organization_id=organization_id.str, realm_id=realm_id.uuid, users_ids=[u.str for u in users], )) roles = {UserID(row["user_id"]): _cook_role(row) for row in rep} for user in users or (): if user not in roles: raise RealmNotFoundError(f"User `{user}` doesn't exist") return roles else: rep = await conn.fetch(*_q_get_realm_role_for_not_revoked( organization_id=organization_id.str, realm_id=realm_id.uuid)) return { UserID(row["user_id"]): _cook_role(row) for row in rep if _cook_role(row) is not None }
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
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 _get_realm_role_for_not_revoked(conn, organization_id, realm_id, users=None): now = pendulum.now() def _cook_role(row): if row["revoked_on"] and row["revoked_on"] <= now: return None if row["role"] is None: return None return STR_TO_REALM_ROLE[row["role"]] if users: rep = await conn.fetch(*_q_get_realm_role_for_not_revoked_with_users( organization_id=organization_id, realm_id=realm_id, users_ids=users)) roles = {row["user_id"]: _cook_role(row) for row in rep} for user in users or (): if user not in roles: raise RealmNotFoundError(f"User `{user}` doesn't exist") return roles else: rep = await conn.fetch(*_q_get_realm_role_for_not_revoked( organization_id=organization_id, realm_id=realm_id)) return { row["user_id"]: _cook_role(row) for row in rep if _cook_role(row) is not None }
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
async def query_get_current_roles(conn, organization_id: OrganizationID, realm_id: UUID) -> Dict[UserID, RealmRole]: ret = await conn.fetch(_q_get_current_roles, 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") return { UserID(user_id): STR_TO_REALM_ROLE[role] for user_id, role in ret if role is not None }
async def get_realm_status(conn, organization_id, realm_id): query = (q_realm(organization_id=Parameter("$1"), realm_id=Parameter("$2")).select( "encryption_revision", "maintenance_started_by", "maintenance_started_on", "maintenance_type", )).get_sql() rep = await conn.fetchrow(query, organization_id, realm_id) if not rep: raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist") return rep
async def get_realm_status(conn, organization_id: OrganizationID, realm_id: UUID) -> RealmStatus: rep = await conn.fetchrow(*_q_get_realm_status( organization_id=organization_id, realm_id=realm_id)) if not rep: raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist") return RealmStatus( maintenance_type=STR_TO_REALM_MAINTENANCE_TYPE.get( rep["maintenance_type"]), maintenance_started_on=rep["maintenance_started_on"], maintenance_started_by=rep["maintenance_started_by"], encryption_revision=rep["encryption_revision"], )
async def query_get_current_roles( conn, organization_id: OrganizationID, realm_id: RealmID) -> Dict[UserID, RealmRole]: ret = await conn.fetch(*_q_get_current_roles( 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") return { UserID(user_id): RealmRole(role) for user_id, role in ret if role is not None }
async def get_realm_status(conn, organization_id: OrganizationID, realm_id: RealmID) -> RealmStatus: rep = await conn.fetchrow(*_q_get_realm_status( organization_id=organization_id.str, realm_id=realm_id.uuid)) if not rep: raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist") return RealmStatus( maintenance_type=MaintenanceType(rep["maintenance_type"]) if rep["maintenance_type"] else None, maintenance_started_on=rep["maintenance_started_on"], maintenance_started_by=DeviceID(rep["maintenance_started_by"]) if rep["maintenance_started_by"] else None, encryption_revision=rep["encryption_revision"], )
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"], )
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)
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"], )
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
def _get_realm(self, organization_id: OrganizationID, realm_id: RealmID) -> Realm: try: return self._realms[(organization_id, realm_id)] except KeyError: raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")
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: 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, )
async def get_realm_role_for_not_revoked(conn, organization_id, realm_id, users=None): now = pendulum.now() def _cook_role(row): if row["revoked_on"] and row["revoked_on"] <= now: return None if row["role"] is None: return None return STR_TO_REALM_ROLE[row["role"]] if users: query = """ WITH cte_current_realm_roles AS ( SELECT DISTINCT ON(user_) user_, role FROM realm_user_role WHERE realm = ({}) ORDER BY user_, certified_on DESC ) SELECT user_.user_id as user_id, user_.revoked_on as revoked_on, role FROM user_ LEFT JOIN cte_current_realm_roles ON user_._id = cte_current_realm_roles.user_ WHERE organization = ({}) AND user_.user_id = ANY({}::VARCHAR[]) """.format( q_realm_internal_id(organization_id=Parameter("$1"), realm_id=Parameter("$2")), q_organization_internal_id(Parameter("$1")), Parameter("$3"), ) rep = await conn.fetch(query, organization_id, realm_id, users) roles = {row["user_id"]: _cook_role(row) for row in rep} for user in users or (): if user not in roles: raise RealmNotFoundError(f"User `{user}` doesn't exist") return roles else: query = """ SELECT DISTINCT ON(user_) ({}) as user_id, ({}) as revoked_on, role FROM realm_user_role WHERE realm = ({}) ORDER BY user_, certified_on DESC """.format( q_user(_id=Parameter("realm_user_role.user_")).select("user_id"), q_user( _id=Parameter("realm_user_role.user_")).select("revoked_on"), q_realm_internal_id(organization_id=Parameter("$1"), realm_id=Parameter("$2")), ) rep = await conn.fetch(query, organization_id, realm_id) return { row["user_id"]: _cook_role(row) for row in rep if _cook_role(row) is not None }
async def get_realm_status(conn, organization_id, realm_id): rep = await conn.fetchrow(*_q_get_realm_status( organization_id=organization_id, realm_id=realm_id)) if not rep: raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist") return rep
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_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, )