Ejemplo n.º 1
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
Ejemplo n.º 2
0
 def _can_be_invisible(self, user_id: str):
     if environ.env.config.get(ConfigKeys.INVISIBLE_UNRESTRICTED,
                               default=False):
         return True
     if utils.is_super_user(user_id) or utils.is_global_moderator(user_id):
         return True
     return False
Ejemplo n.º 3
0
        def _can_edit_acl(_target_id: str, _user_id: str) -> bool:
            object_type = activity.target.object_type
            is_for_channel = object_type == 'channel'

            if is_for_channel:
                if utils.is_owner_channel(_target_id, _user_id):
                    return True
                if utils.is_admin(_target_id, _user_id):
                    return True
            else:
                if utils.is_owner(_target_id, _user_id):
                    return True
                channel_id = None
                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(_target_id)
                if channel_id is not None and utils.is_owner_channel(
                        channel_id, _user_id):
                    return True

            if utils.is_super_user(_user_id) or utils.is_global_moderator(
                    _user_id):
                return True
            return False
Ejemplo n.º 4
0
    def set_user_online_if_not_previously_invisible(arg: tuple) -> None:
        data, activity = arg
        user_id = activity.actor.id
        user_status = utils.get_user_status(user_id)

        if utils.is_super_user(user_id) or utils.is_global_moderator(user_id):
            try:
                info_message = \
                    'op {} ({}) signed in; ' \
                    'user status is currently set to {}; ' \
                    'if not "3" (invisible), I will now change it to "1" (online)'
                info_message = info_message.format(
                    user_id, utils.get_user_name_for(user_id), user_status)
                logger.info(info_message)
            except NoSuchUserException:
                logger.error(
                    'no username found for op user {}'.format(user_id))
            except Exception as e:
                logger.error(
                    'exception while getting username for op {}: {}'.format(
                        user_id, str(e)))
                logger.exception(e)
                environ.env.capture_exception(sys.exc_info())

        if user_status != UserKeys.STATUS_INVISIBLE:
            logger.info('setting user {} to online'.format(user_id))
            environ.env.db.set_user_online(user_id)
        else:
            # if login after server restart the cache value user:status:<user id> is non-existent, set to invisible
            environ.env.cache.set_user_invisible(user_id)
Ejemplo n.º 5
0
    def set_status(arg: tuple) -> None:
        data, activity = arg

        user_id = activity.actor.id
        user_name = environ.env.session.get(SessionKeys.user_name.value, None)
        image = environ.env.session.get(SessionKeys.image.value, '')
        status = activity.verb

        if user_name is None:
            try:
                user_name = utils.get_user_name_for(user_id)
            except NoSuchUserException:
                user_name = str(user_id)

        if not utils.is_valid_id(user_id):
            OnStatusHooks.logger.warning(
                'got invalid user id for activity: {}'.format(str(data)))
            return

        if utils.is_super_user(user_id) or utils.is_global_moderator(user_id):
            OnStatusHooks.log_admin_activity(user_id, user_name, status)

        if status == 'online':
            OnStatusHooks.set_online(user_id, user_name, image)
        elif status == 'invisible':
            OnStatusHooks.set_invisible(user_id, user_name)
        elif status == 'visible':
            OnStatusHooks.set_visible(user_id, user_name)
        elif status == 'offline':
            OnStatusHooks.set_offline(user_id, user_name)

        environ.env.publish(data, external=True)
Ejemplo n.º 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
Ejemplo n.º 7
0
    def on_remove_room(self, activity: Activity) -> (bool, int, str):
        user_id = activity.actor.id
        room_id = activity.target.id

        if utils.is_owner(room_id, user_id):
            return True, None, None
        if utils.is_super_user(user_id):
            return True, None, None
        if utils.is_global_moderator(user_id) and utils.is_room_ephemeral(
                room_id):
            return True, None, None
        if utils.is_moderator(room_id,
                              user_id) and utils.is_room_ephemeral(room_id):
            return True, None, None

        channel_id = utils.get_channel_for_room(room_id)
        if utils.is_admin(channel_id, user_id):
            return True, None, None
        if utils.is_owner_channel(channel_id, user_id):
            return True, None, None

        return False, ECodes.NOT_ALLOWED, 'user %s is not allowed to remove the room' % str(
            user_id)
Ejemplo n.º 8
0
    def validate_acl_for_action(self,
                                activity: Activity,
                                target: str,
                                action: str,
                                target_acls: dict,
                                target_id: str = None,
                                object_type: str = None) -> (bool, str):
        all_acls = environ.env.config.get(ConfigKeys.ACL)

        if not hasattr(activity, 'target') or not hasattr(
                activity.target, 'object_type'):
            return False, 'target.objectType must not be none'
        if activity.target.object_type is None or len(
                activity.target.object_type.strip()) == 0:
            return False, 'target.objectType must not be none'

        if target_id is None:
            target_id = activity.target.id
        if object_type is None:
            object_type = activity.target.object_type

        # one-to-one is sending message that users private room, so target is room, but object_type would not be
        if target == ApiTargets.ROOM and object_type != 'room':
            return True, None

        user_id = activity.actor.id
        if target == 'room':
            channel_id = utils.get_channel_for_room(target_id)
        else:
            channel_id = activity.object.url

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

        # no acls for this target (room/channel) and action (join/kick/etc)
        if target not in all_acls or action not in all_acls[target] or len(
                all_acls[target][action]) == 0:
            return True, None  # 'no acl set that allows action "%s" for target type "%s"' % (action, target)

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

        if target == 'channel':
            pass
        elif target == 'room':
            if utils.is_owner(target_id, user_id):
                return True, None

        # no acls for this target and action
        if target_acls is None or len(target_acls) == 0:
            return True, None

        possible_acls = all_acls[target][action]
        for acl_rule, acl_values in possible_acls.items():
            if acl_rule != 'acls':
                continue
            for acl in acl_values:
                if acl not in target_acls.keys():
                    continue

                is_valid_func = all_acls['validation'][acl]['value']
                is_valid, msg = is_valid_func(activity, environ.env, acl,
                                              target_acls[acl])
                if not is_valid:
                    return False, 'acl "%s" did not validate for target acl "%s": %s' % (
                        acl, target_acls[acl], msg)

        return True, None
Ejemplo n.º 9
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
Ejemplo n.º 10
0
    def on_set_acl(self, activity: Activity) -> (bool, int, str):
        def _can_edit_acl(_target_id: str, _user_id: str) -> bool:
            object_type = activity.target.object_type
            is_for_channel = object_type == 'channel'

            if is_for_channel:
                if utils.is_owner_channel(_target_id, _user_id):
                    return True
                if utils.is_admin(_target_id, _user_id):
                    return True
            else:
                if utils.is_owner(_target_id, _user_id):
                    return True
                channel_id = None
                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(_target_id)
                if channel_id is not None and utils.is_owner_channel(
                        channel_id, _user_id):
                    return True

            if utils.is_super_user(_user_id) or utils.is_global_moderator(
                    _user_id):
                return True
            return False

        user_id = activity.actor.id
        target_id = activity.target.id
        object_type = activity.target.object_type

        if object_type is None or len(object_type.strip()) == 0:
            return False, ECodes.INVALID_TARGET_TYPE, 'empty object_type, must be one of [channel, room]'

        if object_type not in ['channel', 'room']:
            return False, ECodes.INVALID_TARGET_TYPE, \
                   'invalid object_type "%s", must be one of [channel, room]' % object_type

        if not _can_edit_acl(target_id, user_id):
            return False, ECodes.NOT_ALLOWED, 'user is not allowed to change acls on the target'

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

        # validate all acls before actually changing anything
        acls = activity.object.attachments
        all_available_acls_types = environ.env.config.get(
            ConfigKeys.ACL)['available']['acls']
        for acl in acls:
            if acl.object_type not in all_available_acls_types:
                return False, ECodes.INVALID_ACL_TYPE, 'invalid acl type "%s"' % acl.object_type

            if acl.summary is None or acl.summary not in ApiActions.all_api_actions:
                return False, ECodes.INVALID_ACL_ACTION, 'invalid api action "%s"' % acl.summary

            is_valid, error_msg = validation.acl.is_acl_valid(
                acl.object_type, acl.content)
            if not is_valid:
                return False, ECodes.INVALID_ACL_VALUE, 'invalid acl value "%s" for type "%s": %s' % \
                       (acl.content, acl.object_type, error_msg)

        return True, None, None