Exemple #1
0
    def emit_join_event(activity, user_id, user_name, image) -> None:
        room_id = activity.target.id
        room_name = utils.get_room_name(room_id)

        # if invisible, only sent 'invisible' join to admins in the room
        if utils.get_user_status(user_id) == UserKeys.STATUS_INVISIBLE:
            admins_in_room = environ.env.db.get_admins_in_room(
                room_id, user_id)
            if admins_in_room is None or len(admins_in_room) == 0:
                return

            room_name = utils.get_room_name(room_id)
            activity_json = utils.activity_for_user_joined_invisibly(
                user_id, user_name, room_id, room_name, image)
            for admin_id in admins_in_room:
                environ.env.out_of_scope_emit('gn_user_joined',
                                              activity_json,
                                              room=admin_id,
                                              broadcast=False,
                                              namespace='/ws')
            return

        activity_json = utils.activity_for_user_joined(user_id, user_name,
                                                       room_id, room_name,
                                                       image)
        environ.env.out_of_scope_emit('gn_user_joined',
                                      activity_json,
                                      room=room_id,
                                      broadcast=True,
                                      namespace='/ws')
        environ.env.publish(activity_json, external=True)
Exemple #2
0
    def set_online(user_id: str, user_name: str, image: str = '') -> None:
        was_invisible = utils.user_is_invisible(user_id)
        OnStatusHooks.logger.info(
            'setting user {} ({}) online (was invisible before? {})'.format(
                user_id, user_name, was_invisible))
        environ.env.db.set_user_online(user_id)
        rooms = utils.rooms_for_user(user_id)

        if not was_invisible:
            for room_id in rooms:
                room_name = utils.get_room_name(room_id)
                join_activity = utils.activity_for_user_joined(
                    user_id, user_name, room_id, room_name, image)
                environ.env.emit('gn_user_joined',
                                 join_activity,
                                 room=room_id,
                                 broadcast=True,
                                 include_self=False,
                                 namespace='/ws')
            return

        visible_activity = utils.activity_for_going_visible(user_id)
        for room_id in rooms:
            room_name = utils.get_room_name(room_id)
            join_activity = utils.activity_for_user_joined(
                user_id, user_name, room_id, room_name, image)
            admins_in_room = environ.env.db.get_admins_in_room(
                room_id, user_id)

            if admins_in_room is None or len(admins_in_room) == 0:
                environ.env.emit('gn_user_joined',
                                 join_activity,
                                 room=room_id,
                                 broadcast=True,
                                 include_self=False,
                                 namespace='/ws')
                continue

            users_in_room = utils.get_users_in_room(room_id).copy()
            for user_id_in_room, _ in users_in_room.items():
                if user_id_in_room in admins_in_room:
                    continue
                environ.env.emit('gn_user_joined',
                                 join_activity,
                                 room=user_id_in_room,
                                 broadcast=True,
                                 include_self=False,
                                 namespace='/ws')

            for admin_id in admins_in_room:
                environ.env.emit('gn_user_visible',
                                 visible_activity,
                                 room=admin_id,
                                 broadcast=False,
                                 namespace='/ws')
Exemple #3
0
    def on_ban(self, activity: Activity) -> (bool, int, str):
        room_id = activity.target.id
        target_type = activity.target.object_type
        user_id = activity.actor.id
        kicked_id = activity.object.id
        ban_duration = activity.object.summary

        is_global_ban = target_type == 'global' or room_id is None or room_id == ''

        channel_id = None
        if not is_global_ban:
            if hasattr(activity, 'object') and hasattr(activity.object, 'url'):
                channel_id = activity.object.url
            if channel_id is None or len(channel_id.strip()) == 0:
                channel_id = utils.get_channel_for_room(room_id)

        try:
            DurationValidator(ban_duration)
        except ValueError as e:
            return False, ECodes.INVALID_BAN_DURATION, 'invalid ban duration: %s' % str(
                e)

        if not is_global_ban and room_id is not None and len(
                room_id.strip()) > 0:
            try:
                utils.get_room_name(room_id)
            except NoSuchRoomException as e:
                return False, ECodes.NO_SUCH_ROOM, 'no room found for uuid: %s' % str(
                    e)

        if kicked_id is None or kicked_id.strip() == '':
            return False, ECodes.MISSING_OBJECT_ID, 'got blank user id, can not ban'

        if not is_global_ban and not utils.room_exists(channel_id, room_id):
            return False, ECodes.NO_SUCH_ROOM, 'no room with id "%s" exists' % room_id

        if utils.is_super_user(user_id) or utils.is_global_moderator(user_id):
            return True, None, None

        if utils.is_super_user(kicked_id) or utils.is_global_moderator(
                kicked_id):
            return False, ECodes.NO_SUCH_ROOM, 'not allowed to kick super users or global mobs'

        if not is_global_ban:
            if not utils.is_owner(room_id, user_id):
                return False, ECodes.NOT_ALLOWED, 'only owners can ban'
        elif not utils.is_admin(channel_id, user_id):
            return False, ECodes.NOT_ALLOWED, 'only admins, super users and global mods can do global bans'

        return True, None, None
Exemple #4
0
def get_room_name(room_uuid: str) -> str:
    if room_uuid is not None and len(room_uuid.strip()) > 0:
        try:
            return utils.get_room_name(room_uuid)
        except NoSuchRoomException:
            pass
    return ''
Exemple #5
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())
Exemple #6
0
    def on_kick(self, activity: Activity) -> (bool, int, str):
        room_id = activity.target.id
        user_id_to_kick = activity.object.id

        if room_id is None or room_id.strip() == '':
            return False, ECodes.MISSING_TARGET_ID, 'got blank room id, can not kick'

        try:
            utils.get_room_name(room_id)
        except NoSuchRoomException:
            return False, ECodes.NO_SUCH_ROOM, 'no room with id "%s" exists' % room_id

        if user_id_to_kick is None or user_id_to_kick.strip() == '':
            return False, ECodes.MISSING_TARGET_DISPLAY_NAME, 'got blank user id, can not kick'

        if utils.is_super_user(user_id_to_kick) or utils.is_global_moderator(
                user_id_to_kick):
            return False, ECodes.NOT_ALLOWED, "not allowed to kick operators"

        channel_id = utils.get_channel_for_room(room_id)
        channel_acls = utils.get_acls_in_channel_for_action(
            channel_id, ApiActions.KICK)
        is_valid, msg = validation.acl.validate_acl_for_action(
            activity, ApiTargets.CHANNEL, ApiActions.KICK, channel_acls)

        if not is_valid:
            return False, ECodes.NOT_ALLOWED, msg

        try:
            room_acls = utils.get_acls_in_room_for_action(
                room_id, ApiActions.KICK)
        except NoSuchRoomException:
            return False, ECodes.NO_SUCH_ROOM, 'no such room'

        is_valid, msg = validation.acl.validate_acl_for_action(
            activity, ApiTargets.ROOM, ApiActions.KICK, room_acls)
        if not is_valid:
            return False, ECodes.NOT_ALLOWED, msg

        return True, None, None
Exemple #7
0
    def join_room(arg: tuple) -> None:
        data, activity = arg
        room_id = activity.target.id
        user_id = activity.actor.id

        user_name = environ.env.session.get(SessionKeys.user_name.value)
        room_name = utils.get_room_name(room_id)

        try:
            utils.join_the_room(user_id, user_name, room_id, room_name)
        except NoSuchRoomException:
            logger.error('tried to join non-existing room "{}" ({})'.format(
                room_id, room_name))
Exemple #8
0
    def send_request(arg: tuple) -> None:
        data, activity = arg

        room_id = activity.target.id
        user_id = activity.actor.id
        message = activity.object.content

        username = utils.get_user_name_for(user_id)
        room_name = utils.get_room_name(room_id)
        admin_room_id = utils.get_admin_room()

        activity_json = utils.activity_for_request_admin(user_id, username, room_id, room_name, message, admin_room_id)
        environ.env.emit(
            'gn_admin_requested', activity_json, json=True, broadcast=True, room=admin_room_id, namespace='/ws')
Exemple #9
0
    def leave_room(arg: tuple) -> None:
        data, activity = arg

        user_id = activity.actor.id
        user_name = activity.actor.display_name
        room_id = activity.target.id

        try:
            room_name = utils.get_room_name(room_id)
        except NoSuchRoomException:
            room_name = '[removed]'

        utils.remove_sid_for_user_in_room(user_id, room_id,
                                          environ.env.request.sid)

        # multi-login, can be in same room as another session
        sids = utils.sids_for_user_in_room(user_id, room_id)
        if sids is not None and len(sids) > 0:
            if len(sids) > 1 or next(iter(sids)) != environ.env.request.sid:
                return

        utils.remove_user_from_room(user_id, user_name, room_id)

        # if invisible, only send 'invisible' leave to admins in the room
        if utils.get_user_status(user_id) == UserKeys.STATUS_INVISIBLE:
            admins_in_room = environ.env.db.get_admins_in_room(
                room_id, user_id)
            if admins_in_room is None or len(admins_in_room) == 0:
                return

            activity_left = utils.activity_for_leave(user_id, user_name,
                                                     room_id, room_name)
            for admin_id in admins_in_room:
                environ.env.out_of_scope_emit('gn_user_left',
                                              activity_left,
                                              room=admin_id,
                                              broadcast=False,
                                              namespace='/ws')
            return

        activity_left = utils.activity_for_leave(user_id, user_name, room_id,
                                                 room_name)
        environ.env.emit('gn_user_left',
                         activity_left,
                         room=room_id,
                         broadcast=True,
                         include_self=False,
                         namespace='/ws')
        utils.check_if_should_remove_room(data, activity)
Exemple #10
0
    def send_invite(arg: tuple) -> None:
        data, activity = arg
        invitee = activity.target.id
        invite_room = activity.actor.url
        channel_id = activity.object.url
        channel_name = activity.object.display_name
        invitee_name = activity.target.display_name

        room_name = utils.get_room_name(invite_room)

        activity_json = utils.activity_for_invite(invitee, invitee_name,
                                                  invite_room, room_name,
                                                  channel_id, channel_name)
        environ.env.emit('gn_invitation',
                         activity_json,
                         json=True,
                         room=invitee,
                         namespace='/ws')
Exemple #11
0
    def leave_room(arg: tuple) -> None:
        data, activity = arg

        #  todo: should handle invisibility here? don't broadcast leaving a room if invisible
        user_id = activity.actor.id
        user_name = activity.actor.display_name
        room_id = activity.target.id

        try:
            room_name = utils.get_room_name(room_id)
        except NoSuchRoomException:
            room_name = '[removed]'

        utils.remove_user_from_room(user_id, user_name, room_id)

        # if invisible, only send 'invisible' leave to admins in the room
        if utils.get_user_status(user_id) == UserKeys.STATUS_INVISIBLE:
            admins_in_room = environ.env.db.get_admins_in_room(
                room_id, user_id)
            if admins_in_room is None or len(admins_in_room) == 0:
                return

            activity_left = utils.activity_for_leave(user_id, user_name,
                                                     room_id, room_name)
            for admin_id in admins_in_room:
                environ.env.out_of_scope_emit('gn_user_left',
                                              activity_left,
                                              room=admin_id,
                                              broadcast=False,
                                              namespace='/ws')
            return

        activity_left = utils.activity_for_leave(user_id, user_name, room_id,
                                                 room_name)
        environ.env.emit('gn_user_left',
                         activity_left,
                         room=room_id,
                         broadcast=True,
                         include_self=False,
                         namespace='/ws')
        utils.check_if_should_remove_room(data, activity)
Exemple #12
0
    def send_whisper(arg: tuple) -> None:
        data, activity = arg
        user_room = activity.target.id
        whisperer = activity.actor.id
        whisperer_name = activity.actor.display_name
        room_id = activity.actor.url
        channel_id = activity.object.url
        channel_name = activity.object.display_name
        message = activity.object.content

        room_name = utils.get_room_name(room_id)

        activity_json = utils.activity_for_whisper(message, whisperer,
                                                   whisperer_name, room_id,
                                                   room_name, channel_id,
                                                   channel_name)
        environ.env.emit('gn_whisper',
                         activity_json,
                         json=True,
                         room=user_room,
                         namespace='/ws')
Exemple #13
0
    def remove_ban(self, user_id: str, target_id: str,
                   target_type: str) -> None:
        ban_activity = {
            'actor': {
                'id': '0',
                'displayName': utils.b64e('admin')
            },
            'verb': 'unban',
            'object': {
                'id': user_id,
                'displayName': utils.b64e(utils.get_user_name_for(user_id))
            },
            'target': {
                'objectType': target_type
            },
            'id': str(uuid()),
            'published':
            datetime.utcnow().strftime(ConfigKeys.DEFAULT_DATE_FORMAT)
        }

        if target_type == 'global':
            self.env.db.remove_global_ban(user_id)

        elif target_type == 'channel':
            self.env.db.remove_channel_ban(target_id, user_id)
            ban_activity['target']['id'] = target_id
            ban_activity['target']['displayName'] = utils.b64e(
                utils.get_channel_name(target_id))

        elif target_type == 'room':
            self.env.db.remove_room_ban(target_id, user_id)
            ban_activity['target']['id'] = target_id
            ban_activity['target']['displayName'] = utils.b64e(
                utils.get_room_name(target_id))

        else:
            raise UnknownBanTypeException(target_type)

        self.env.publish(ban_activity)
Exemple #14
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())
Exemple #15
0
    def on_join(self, activity: Activity) -> (bool, int, str):
        room_id = activity.target.id
        room_name = activity.target.display_name
        user_id = environ.env.session.get(SessionKeys.user_id.value, None)

        if user_id is None or len(user_id.strip()) == 0:
            user_id = activity.actor.id

        if room_id is not None and len(room_id.strip()) > 0:
            try:
                room_name = utils.get_room_name(room_id)
            except NoSuchRoomException:
                return False, ECodes.NO_SUCH_ROOM, 'room does not exist'
        else:
            if room_name is None or len(room_name.strip()) == 0:
                return False, ECodes.MISSING_TARGET_DISPLAY_NAME, 'neither room id nor name supplied'

            try:
                room_id = utils.get_room_id(room_name)
            except NoSuchRoomException:
                return False, ECodes.NO_SUCH_ROOM, 'room does not exists with given name'
            except MultipleRoomsFoundForNameException:
                return False, ECodes.MULTIPLE_ROOMS_WITH_NAME, 'found multiple rooms with name "%s"' % room_name

        if not hasattr(activity, 'object'):
            activity.object = DefObject(dict())

        if not utils.user_is_online(user_id):
            user_name = '<unknown>'
            try:
                user_name = utils.get_user_name_for(user_id)
            except NoSuchUserException:
                logger.error('could not get username for user id %s' % user_id)

            logger.warning(
                'user "%s" (%s) is not online, not joining room "%s" (%s)!' %
                (user_name, user_id, room_name, room_id))
            return False, ECodes.NOT_ONLINE, 'user is not online'

        if utils.is_super_user(user_id) or utils.is_global_moderator(user_id):
            return True, None, None
        if utils.is_owner(room_id, user_id):
            return True, None, None

        channel_id = utils.get_channel_for_room(room_id)

        if utils.is_owner_channel(channel_id, user_id):
            return True, None, None

        activity.object.url = channel_id
        activity.object.display_name = utils.get_channel_name(channel_id)
        activity.target.object_type = 'room'

        try:
            acls = utils.get_acls_in_room_for_action(room_id, ApiActions.JOIN)
        except NoSuchRoomException:
            return False, ECodes.NO_SUCH_ROOM, 'no such room'

        is_valid, error_msg = validation.acl.validate_acl_for_action(
            activity, ApiTargets.ROOM, ApiActions.JOIN, acls)
        if not is_valid:
            return False, ECodes.NOT_ALLOWED, error_msg

        is_banned, info_dict = utils.is_banned(user_id, room_id)
        if is_banned:
            scope = info_dict['scope']
            seconds_left = info_dict['seconds']
            target_id = info_dict['id']
            target_name = ''
            if scope == 'room':
                target_name = utils.get_room_name(target_id)
            elif scope == 'channel':
                target_name = utils.get_channel_name(target_id)
            reason = utils.reason_for_ban(user_id, scope, target_id)

            json_act = utils.activity_for_already_banned(
                seconds_left, reason, scope, target_id, target_name)
            return False, ECodes.USER_IS_BANNED, json_act

        return True, None, None