Пример #1
0
        def leave_private_room(user_id, current_sid):
            all_sids = utils.get_sids_for_user_id(user_id)

            # only one of the user sessions disconnected
            if len(all_sids) > 1:
                return

            try:
                # todo: only broadcast 'offline' status if currently 'online' (i.e. don't broadcast if e.g. 'invisible')
                user_name = environ.env.session.get(SessionKeys.user_name.value)
                logger.debug('a user disconnected [id: "%s", name: "%s", sid: "%s"]' % (user_id, user_name, current_sid))

                try:
                    environ.env.leave_room(current_sid)
                    environ.env.db.remove_sid_for_user(user_id, current_sid)
                except Exception as e:
                    logger.warning('could not remove sid {} for user {}: {}'.format(current_sid, user_id, str(e)))

                if user_id is None or len(user_id.strip()) == 0:
                    return

                all_sids = utils.get_sids_for_user_id(user_id)
                all_sids = all_sids.copy()

                if len(all_sids) == 0 or (current_sid in all_sids and len(all_sids) == 1):
                    environ.env.leave_room(user_id)
                    environ.env.db.reset_sids_for_user(user_id)
                    for key in SessionKeys.temporary_keys.value:
                        environ.env.auth.update_session_for_key(activity.actor.id, key, False)

            except Exception as e:
                logger.error('could not leave private room: %s' % str(e))
                logger.debug('request for failed leave_private_room(): %s' % str(data))
                logger.exception(traceback.format_exc())
                environ.env.capture_exception(sys.exc_info())
Пример #2
0
        def set_user_offline(user_id, current_sid):
            try:
                if not utils.is_valid_id(user_id):
                    logger.warning(
                        'got invalid id on disconnect for act: {}'.format(
                            str(activity.id)))
                    # TODO: sentry
                    return

                environ.env.db.remove_sid_for_user(user_id, current_sid)
                all_sids = utils.get_sids_for_user_id(user_id)

                # if the user still has another session up we don't set the user as offline
                if all_sids is not None and len(all_sids) > 0:
                    logger.debug(
                        'when setting user offline, found other sids: [%s]' %
                        ','.join(all_sids))
                    return

                if utils.get_user_status(user_id) == UserKeys.STATUS_INVISIBLE:
                    environ.env.cache.remove_from_multicast_on_disconnect(
                        user_id)
                else:
                    environ.env.db.set_user_offline(user_id)
            except Exception as e:
                logger.error('could not set user offline: %s' % str(e))
                logger.debug('request for failed set_user_offline(): %s' %
                             str(data))
                logger.exception(traceback.format_exc())
Пример #3
0
        def emit_disconnect_event(user_id, current_sid) -> None:
            try:
                if is_socket_disconnect:
                    user_name = environ.env.session.get(SessionKeys.user_name.value)
                    if user_name is None or len(user_name.strip()) == 0:
                        try:
                            user_name = utils.get_user_name_for(user_id)
                        except NoSuchUserException:
                            user_name = '<unknown>'
                else:
                    try:
                        user_name = utils.get_user_name_for(user_id)
                    except NoSuchUserException:
                        user_name = '<unknown>'

                all_sids = utils.get_sids_for_user_id(user_id)

                # race condition might lead to db cache saying it's still there or similar
                make_sure_current_sid_removed(all_sids, user_id, current_sid)

                if user_id is None or user_id == 'None':
                    logger.warning('blank user_id on disconnect event, trying sid instead')

                    if current_sid is None or current_sid == 'None' or current_sid == '':
                        logger.error('blank sid as well as blank user id, ignoring disconnect event')
                        return

                    try:
                        user_id = utils.get_user_for_sid(current_sid)
                    except Exception as e:
                        logger.warning('could not get user id from sid "{}": {}'.format(current_sid, str(e)))
                        environ.env.capture_exception(sys.exc_info())
                        user_id = '-1'

                    if user_id is None or len(user_id.strip()) == 0:
                        logger.warning('blank user id for sid "{}"'.format(current_sid))
                        user_id = '-1'

                logger.debug(
                    'sid %s disconnected, all_sids: [%s] for user %s (%s)' % (
                        current_sid, ','.join(all_sids), user_id, user_name))

                if user_id != '-1':
                    sid_ended_event = utils.activity_for_sid_disconnect(user_id, user_name, current_sid)
                    environ.env.publish(sid_ended_event, external=True)

                # if the user still has another session up we don't send disconnect event
                if all_sids is not None and len(all_sids) > 0:
                    return

                if user_id != '-1':
                    activity_json = utils.activity_for_disconnect(user_id, user_name)
                    environ.env.publish(activity_json, external=True)

            except Exception as e:
                logger.error('could not emit disconnect event: %s' % str(e))
                logger.debug('request for failed emit_disconnect_event(): %s' % str(data))
                logger.exception(traceback.format_exc())
                environ.env.capture_exception(sys.exc_info())
Пример #4
0
    def handle_kick(self, activity: Activity):
        kicker_id = activity.actor.id
        if kicker_id == '0':
            kicker_name = 'admin'
        else:
            try:
                kicker_name = activity.actor.display_name or utils.get_user_name_for(
                    kicker_id)
            except NoSuchUserException:
                # if kicking from rest api the user might not exist
                logger.error('no such user when kicking: %s' % kicker_id)
                return

        kicked_id = activity.object.id
        kicked_name = activity.object.display_name or utils.get_user_name_for(
            kicked_id)
        kicked_sids = utils.get_sids_for_user_id(kicked_id)
        room_id = activity.target.id

        if room_id is not None:
            room_name = utils.get_room_name(room_id)
        else:
            room_name = activity.target.display_name
        namespace = activity.target.url

        if kicked_sids is None or len(kicked_sids) == 0 or kicked_sids == [
                None
        ] or kicked_sids[0] == '':
            logger.warning('no sid(s) found for user id %s' % kicked_id)
            return

        reason = None
        if hasattr(activity.object, 'content'):
            reason = activity.object.content

        activity_json = utils.activity_for_user_kicked(kicker_id, kicker_name,
                                                       kicked_id, kicked_name,
                                                       room_id, room_name,
                                                       reason)

        try:
            # user just got banned globally, kick from all rooms
            if room_id is None or room_id == '':
                room_keys = self.env.db.rooms_for_user(kicked_id).copy().keys()
                for room_key in room_keys:
                    self.kick(activity_json, activity, room_key, kicked_id,
                              kicked_sids, namespace)
            else:
                self.kick(activity_json, activity, room_id, kicked_id,
                          kicked_sids, namespace)
        except KeyError as e:
            logger.error('could not kick user %s: %s' % (kicked_id, str(e)))
            self.env.capture_exception(sys.exc_info())
Пример #5
0
    def user_is_on_this_node(self, activity: Activity) -> bool:
        if self.env.node not in {'app', 'wio'}:
            return False

        room_id = activity.target.id
        namespace = activity.target.url or '/ws'
        user_id = activity.object.id or activity.target.id
        user_sids = utils.get_sids_for_user_id(user_id)
        users = list()

        try:
            if room_id is None:
                logger.debug('checking if we have user %s in namespace %s' %
                             (user_id, namespace))
                for user_sid in user_sids:
                    if user_sid in self.socketio.server.manager.rooms[
                            namespace]:
                        logger.debug('found user %s on this node' % user_id)
                        return True
                logger.info(
                    'no user %s for namespace [%s] (or user not on this node)'
                    % (user_id, namespace))
                return False

            else:
                logger.debug('checking if we have room %s in namespace %s' %
                             (room_id, namespace))
                if room_id in self.socketio.server.manager.rooms[namespace]:
                    users = self.socketio.server.manager.rooms[namespace][
                        room_id]
                    logger.debug('found users for room %s: %s' %
                                 (room_id, str(users)))
                else:
                    logger.warning(
                        'no room %s for namespace [%s] (or room is empty/removed)'
                        % (room_id, namespace))
                return any(user_sid in users for user_sid in user_sids)

        except KeyError as e:
            logger.warning(
                'namespace %s does not exist (maybe this is web/rest node?): %s'
                % (namespace, str(e)))
            return False
        except Exception as e:
            logger.error(
                'could not get users for namespace "%s" and room "%s": %s' %
                (namespace, room_id, str(e)))
            logger.exception(traceback.format_exc())
            return False
Пример #6
0
    def handle_ban(self, activity: Activity):
        banner_id = activity.actor.id
        if banner_id == '0' or banner_id is None:
            banner_id = '0'
            banner_name = 'admin'
        else:
            try:
                banner_name = utils.get_user_name_for(banner_id)
            except NoSuchUserException:
                # if banning from rest api the user might not exist
                logger.error('no such user when banning: %s' % banner_id)
                return

        banned_id = activity.object.id
        if not utils.is_valid_id(banned_id):
            logger.warning('got invalid id on ban activity: {}'.format(
                str(activity.id)))
            # TODO: sentry
            return

        banned_name = utils.get_user_name_for(banned_id)
        banned_sids = utils.get_sids_for_user_id(banned_id)
        namespace = activity.target.url or '/ws'
        target_type = activity.target.object_type

        if target_type == 'room':
            target_id = activity.target.id
            target_name = utils.get_room_name(target_id)
        elif target_type == 'channel':
            target_id = activity.target.id
            target_name = utils.get_channel_name(target_id)
        else:
            target_id = ''
            target_name = ''

        if len(banned_sids) == 0 or banned_sids == [None
                                                    ] or banned_sids[0] == '':
            logger.warning('no sid(s) found for user id %s' % banned_id)
            return

        reason = None
        if hasattr(activity.object, 'content'):
            reason = activity.object.content

        activity_json = utils.activity_for_user_banned(banner_id, banner_name,
                                                       banned_id, banned_name,
                                                       target_id, target_name,
                                                       reason)

        try:
            ban_activity = self.get_ban_activity(activity, target_type)
            self.env.out_of_scope_emit('gn_banned',
                                       ban_activity,
                                       json=True,
                                       namespace=namespace,
                                       room=banned_id)

            if target_id is None or target_id == '':
                rooms_for_user = self.env.db.rooms_for_user(banned_id)
                logger.info(
                    'user %s is in these rooms (will ban from all): %s' %
                    (banned_id, str(rooms_for_user)))
                self.ban_globally(activity_json, activity, rooms_for_user,
                                  banned_id, banned_sids, namespace)

                if utils.get_user_status(
                        banned_id) == UserKeys.STATUS_INVISIBLE:
                    environ.env.cache.remove_from_multicast_on_disconnect(
                        banned_id)
                else:
                    environ.env.db.set_user_offline(banned_id)

                disconnect_activity = utils.activity_for_disconnect(
                    banned_id, banned_name)
                self.env.publish(disconnect_activity, external=True)

            elif target_type == 'channel':
                rooms_in_channel = self.env.db.rooms_for_channel(target_id)
                self.ban_channel(activity_json, activity, rooms_in_channel,
                                 target_id, banned_id, banned_sids, namespace)
            else:
                self.ban_room(activity_json, activity, target_id, banned_id,
                              banned_sids, namespace)

        except KeyError as ke:
            logger.error('could not ban: %s' % str(ke))
            logger.exception(traceback.format_exc())
            self.env.capture_exception(sys.exc_info())