def on_invite(self, activity: Activity) -> (bool, int, str): if not hasattr(activity.actor, 'url'): return False, ECodes.MISSING_ACTOR_URL, 'need invite room uuid in actor.url' invite_room = activity.actor.url if not hasattr(activity, 'target') or not hasattr( activity.target, 'id'): return False, ECodes.MISSING_TARGET_ID, 'no target.id (uuid of user to invite)' try: activity.target.display_name = utils.get_user_name_for( activity.target.id) except NoSuchUserException: return False, ECodes.NO_SUCH_USER, 'no such user for target.id (uuid of user to invite)' try: channel_id = utils.get_channel_for_room(invite_room) except (NoSuchRoomException, NoChannelFoundException): return False, ECodes.NO_SUCH_ROOM, 'no room/channel found for actor.url room uuid' if not utils.room_exists(channel_id, invite_room): return False, ECodes.NO_SUCH_ROOM, 'room actor.url does not exist' if not hasattr(activity, 'object'): activity.object = DefObject(dict()) activity.object.url = channel_id activity.object.display_name = utils.get_channel_name(channel_id) return True, None, None
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
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
def on_request_admin(self, activity: Activity) -> (bool, int, str): activity.actor = Actor({ 'id': str(environ.env.session.get(SessionKeys.user_id.value)), 'displayName': environ.env.session.get(SessionKeys.user_name.value) }) room_id = activity.target.id channel_id = utils.get_channel_for_room(room_id) admin_room_id = utils.get_admin_room() if admin_room_id is None or len(admin_room_id.strip()) == 0: logger.error('no admin room found for channel "%s"' % channel_id) return False, ECodes.NO_ADMIN_ROOM_FOUND, 'no admin room for this channel' return True, None, None
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
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)
def on_whisper(self, activity: Activity) -> (bool, int, str): if not hasattr(activity, 'target') or not hasattr( activity.target, 'id'): return False, ECodes.MISSING_TARGET_ID, 'no target.id (user uuid to whisper to)' if not hasattr(activity, 'actor') or not hasattr(activity.actor, 'id'): return False, ECodes.MISSING_ACTOR_ID, 'no actor.id (id of user who is whispering)' if not hasattr(activity, 'actor') or not hasattr( activity.actor, 'url'): return False, ECodes.MISSING_ACTOR_URL, 'no actor.url (room uuid to whisper in)' if not hasattr(activity, 'object') or not hasattr( activity.object, 'content'): return False, ECodes.MISSING_OBJECT_CONTENT, 'no object.content (message to whisper)' if not utils.is_base64(activity.object.content): return False, ECodes.NOT_BASE64, 'object.content needs to be base64 encoded' try: activity.object.url = utils.get_channel_for_room( activity.actor.url) except (NoSuchChannelException, NoChannelFoundException): return False, ECodes.NO_SUCH_ROOM, 'no room found for actor.url (room uuid to whisper in)' try: activity.object.display_name = utils.get_channel_name( activity.object.url) except (NoSuchChannelException, NoChannelFoundException): return False, ECodes.NO_SUCH_CHANNEL, 'no channel found for actor.url (room uuid to whisper in)' channel_acls = utils.get_acls_in_channel_for_action( activity.object.url, ApiActions.WHISPER) is_valid, msg = validation.acl.validate_acl_for_action( activity, ApiTargets.CHANNEL, ApiActions.WHISPER, channel_acls) if not is_valid: return False, ECodes.NOT_ALLOWED, msg return True, None, None
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
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
def on_message(self, activity: Activity) -> (bool, int, str): room_id = activity.target.id user_id = activity.actor.id object_type = activity.target.object_type message = activity.object.content from_room_id = None if hasattr(activity.actor, 'url'): from_room_id = activity.actor.url if message is None or len(message.strip()) == 0: return False, ECodes.EMPTY_MESSAGE, 'empty message body' if not utils.is_base64(message): return False, ECodes.NOT_BASE64, 'invalid message content, not base64 encoded' if room_id is None or room_id == '': return False, ECodes.MISSING_TARGET_ID, 'no room id specified when sending message' if object_type not in ['room', 'private']: return False, ECodes.INVALID_TARGET_TYPE, \ 'invalid object_type "%s", must be one of [room, private]' % object_type if object_type == 'room': 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(room_id) if channel_id is None or channel_id == '': return False, ECodes.MISSING_OBJECT_URL, 'no channel id specified when sending message' activity.object.url = channel_id activity.object.display_name = utils.get_channel_name(channel_id) if not utils.channel_exists(channel_id): return False, ECodes.NO_SUCH_CHANNEL, 'channel %s does not exists' % channel_id if not utils.room_exists(channel_id, room_id): return False, ECodes.NO_SUCH_ROOM, 'target room %s does not exist' % room_id if from_room_id is not None: if from_room_id != room_id and not utils.room_exists( channel_id, from_room_id): return False, ECodes.NO_SUCH_ROOM, 'origin room %s does not exist' % from_room_id if not utils.is_user_in_room(user_id, room_id): logger.warning('user "%s" is not in room "%s' % (user_id, room_id)) if from_room_id is None: return False, ECodes.USER_NOT_IN_ROOM, 'user is not in target room' if not utils.is_user_in_room(user_id, from_room_id): return False, ECodes.USER_NOT_IN_ROOM, 'user is not in origin room, cannot send message from there' if not utils.can_send_cross_room(activity, from_room_id, room_id): return False, ECodes.NOT_ALLOWED, \ 'user not allowed to send cross-room msg from %s to %s' % (from_room_id, room_id) elif object_type == 'private': 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: try: channel_id = utils.get_channel_for_room(room_id) except NoSuchRoomException: # TODO: ignore for now, but capture so we can track; a user room won't exist, try to emit anyway environ.env.capture_exception(sys.exc_info()) return True, False, False if not utils.channel_exists(channel_id): return False, ECodes.NO_SUCH_CHANNEL, 'channel %s does not exists' % channel_id if not utils.room_exists(channel_id, room_id): return False, ECodes.NO_SUCH_ROOM, 'target room %s does not exist' % room_id return True, None, None