def get_user_ban_status(self, room_id: str, user_id: str) -> dict: channel_id = self.channel_for_room(room_id) global_ban = self.redis.hget(RedisKeys.banned_users(), user_id) channel_ban = self.redis.hget(RedisKeys.banned_users_channel(channel_id), user_id) room_ban = self.redis.hget(RedisKeys.banned_users(room_id), user_id) global_timestamp = '' channel_timestamp = '' room_timestamp = '' if global_ban is not None: global_ban = str(global_ban, 'utf-8') global_timestamp = global_ban.split('|', 2)[1] if channel_ban is not None: channel_ban = str(channel_ban, 'utf-8') channel_timestamp = channel_ban.split('|', 2)[1] if room_ban is not None: room_ban = str(room_ban, 'utf-8') room_timestamp = room_ban.split('|', 2)[1] return { 'global': global_timestamp, 'channel': channel_timestamp, 'room': room_timestamp }
def get_room_acls_for_action( self, action: str) -> Union[None, Dict[str, Dict[str, str]]]: key = RedisKeys.rooms_with_action(action) room_ids_bytes = self.redis.get(key) if room_ids_bytes is None: return None room_ids_str = str(room_ids_bytes, 'utf-8') room_ids = room_ids_str.split(',') if len(room_ids) == 0: return None room_acls = dict() for room_id in room_ids: key = RedisKeys.room_acls_for_action(room_id, action) acls = self.redis.hgetall(key) str_acls = dict() for acl_type, acl_value in acls.items(): str_acls[str(acl_type, 'utf-8')] = str(acl_value, 'utf-8') room_acls[room_id] = str_acls return room_acls
def create_admin_room(self) -> str: admin_room_id = self.get_admin_room() if admin_room_id is not None: return admin_room_id try: self.create_user('0', 'Admin') except UserExistsException: pass channel_id = str(uuid()) room_id = str(uuid()) self.create_channel('Admins', channel_id, '0') self.redis.set(RedisKeys.admin_room(), room_id) self.redis.hset(RedisKeys.room_name_for_id(), room_id, 'Admins') self.redis.hset(RedisKeys.rooms(channel_id), room_id, 'Admins') self.redis.hset(RedisKeys.channel_for_rooms(), room_id, channel_id) self.redis.sadd(RedisKeys.non_ephemeral_rooms(), room_id) acls = { RoleKeys.ADMIN: '', RoleKeys.SUPER_USER: '' } samechannel = { 'samechannel': '' } self.add_acls_in_channel_for_action(channel_id, ApiActions.LIST, acls) self.add_acls_in_channel_for_action(channel_id, ApiActions.JOIN, acls) self.add_acls_in_room_for_action(room_id, ApiActions.JOIN, acls) self.add_acls_in_room_for_action(room_id, ApiActions.LIST, acls) self.add_acls_in_room_for_action(room_id, ApiActions.CROSSROOM, samechannel) return room_id
def get_user_roles(self, user_id: str) -> dict: output = { 'global': list(), 'channel': dict(), 'room': dict() } checked_channels = set() rooms = self.redis.hgetall(RedisKeys.rooms_for_user(user_id)) global_roles = self.redis.hget(RedisKeys.global_roles(), user_id) if global_roles is not None: global_roles = str(global_roles, 'utf-8') output['global'] = [a for a in global_roles.split(',')] for room_id, _ in rooms.items(): room_id = str(room_id, 'utf-8') channel_id = self.channel_for_room(room_id) room_roles = self.redis.hget(RedisKeys.room_roles(room_id), user_id) if channel_id not in checked_channels: checked_channels.add(channel_id) channel_roles = self.redis.hget(RedisKeys.channel_roles(channel_id), user_id) if channel_roles is not None: channel_roles = str(channel_roles, 'utf-8') output['channel'][channel_id] = [a for a in channel_roles.split(',')] if room_roles is not None: room_roles = str(room_roles, 'utf-8') output['room'][room_id] = [a for a in room_roles.split(',')] return output
def _update_acks_with_status(self, message_ids: set, receiver_id: str, target_id: str, status: int): redis_key_user = RedisKeys.ack_for_user(receiver_id) redis_key_room = RedisKeys.ack_for_room(target_id) for message_id in message_ids: self.redis.hset(redis_key_user, message_id, str(status)) self.redis.sadd(redis_key_room, message_id)
def set_user_online(self, user_id: str) -> None: self.cache.set(RedisKeys.user_status(user_id), UserKeys.STATUS_AVAILABLE) self.redis.setbit(RedisKeys.online_bitmap(), int(user_id), 1) self.redis.sadd(RedisKeys.online_set(), int(user_id)) self.redis.sadd(RedisKeys.users_multi_cast(), user_id) self.redis.set(RedisKeys.user_status(user_id), UserKeys.STATUS_AVAILABLE)
def set_user_status_invisible(self, user_id: str) -> None: try: user_id_str = str(user_id).strip() self.cache.set(RedisKeys.user_status(user_id_str), UserKeys.STATUS_INVISIBLE) self.redis.set(RedisKeys.user_status(user_id_str), UserKeys.STATUS_INVISIBLE) except Exception as e: logger.error('could not set_user_status_invisible(): %s' % str(e)) logger.exception(traceback.format_exc())
def set_user_invisible(self, user_id: str) -> None: self.cache.set(RedisKeys.user_status(user_id), UserKeys.STATUS_INVISIBLE) self.redis.setbit(RedisKeys.online_bitmap(), int(user_id), 0) self.redis.srem(RedisKeys.online_set(), int(user_id)) self.redis.sadd(RedisKeys.users_multi_cast(), user_id) self.redis.set(RedisKeys.user_status(user_id), UserKeys.STATUS_INVISIBLE)
def create_room(self, room_id: str=None, room_name: str=None): if room_id is None: room_id = BaseTest.ROOM_ID if room_name is None: room_name = BaseTest.ROOM_NAME environ.env.storage.redis.hset(RedisKeys.rooms(BaseTest.CHANNEL_ID), room_id, room_name) environ.env.storage.redis.hset(RedisKeys.room_name_for_id(), room_id, room_name) environ.env.storage.redis.hset(RedisKeys.channel_for_rooms(), room_id, BaseTest.CHANNEL_ID)
def set_users_in_room(self, room_id: str, users: dict, is_super_user: bool) -> None: if is_super_user: key = RedisKeys.users_in_room_incl_invisible(room_id) else: key = RedisKeys.users_in_room_only_visible(room_id) self.cache.set(key, users, ttl=TEN_SECONDS + random.random() * TEN_SECONDS)
def reset_rooms_for_channel(self, channel_id: str) -> None: key_with_info = RedisKeys.rooms_for_channel_with_info(channel_id) key_without_info = RedisKeys.rooms_for_channel_without_info(channel_id) self.cache.delete(key_with_info) self.cache.delete(key_without_info) self.redis.delete(key_with_info) self.redis.delete(key_without_info)
def remove_channel_exists(self, channel_id: str) -> None: key = RedisKeys.channel_exists() cache_key = '%s-%s' % (key, channel_id) self.redis.hdel(key, channel_id) self.cache.delete(cache_key) key = RedisKeys.channels() cache_key = '%s-name-%s' % (key, channel_id) self.cache.delete(cache_key) self.redis.hdel(key, channel_id)
def set_sids_for_user(self, user_id: str, all_sids: list) -> None: key = RedisKeys.sid_for_user_id() all_sids = set(all_sids.copy()) sid_key = RedisKeys.user_id_for_sid() for sid in all_sids: self.redis.hset(sid_key, sid, user_id) all_sids = ','.join(list(set(all_sids))) self.redis.hset(key, user_id, all_sids)
def _add_room_role(self, role: str, room_id: str, user_id: str): self.get_room_name(room_id) roles = self.redis.hget(RedisKeys.room_roles(room_id), user_id) if roles is None: roles = role else: roles = set(str(roles, 'utf-8').split(',')) roles.add(role) roles = ','.join(roles) self.redis.hset(RedisKeys.room_roles(room_id), user_id, roles)
def _remove_room_role(self, role: str, room_id: str, user_id: str): roles = self.redis.hget(RedisKeys.room_roles(room_id), user_id) if roles is None: return roles = set(str(roles, 'utf-8').split(',')) if role not in roles: return roles.remove(role) roles = ','.join(roles) self.redis.hset(RedisKeys.room_roles(room_id), user_id, roles)
def is_banned_from_room(self, room_id: str, user_id: str) -> (bool, Union[str, None]): ban = self.redis.hget(RedisKeys.banned_users(room_id), user_id) is_banned, time = self._is_banned(ban) if not is_banned: return False, None now = datetime.utcnow() end = datetime.fromtimestamp(float(time)) if now > end: self.redis.hdel(RedisKeys.banned_users(), user_id) return False, None return True, time
def set_user_invisible(self, user_id: str) -> None: try: user_id_str = str(user_id).strip() user_id_int = int(float(user_id)) self.cache.set(RedisKeys.user_status(user_id_str), UserKeys.STATUS_INVISIBLE) self.redis.setbit(RedisKeys.online_bitmap(), user_id_int, 0) self.redis.srem(RedisKeys.online_set(), user_id_str) self.redis.sadd(RedisKeys.users_multi_cast(), user_id_str) self.redis.set(RedisKeys.user_status(user_id_str), UserKeys.STATUS_INVISIBLE) except Exception as e: logger.error('could not set_user_invisible(): %s' % str(e)) logger.exception(traceback.format_exc())
def set_user_offline(self, user_id: str) -> None: try: user_id_str = str(user_id).strip() user_id_int = int(float(user_id)) self.cache.set(RedisKeys.user_status(user_id_str), UserKeys.STATUS_UNAVAILABLE) self.redis.setbit(RedisKeys.online_bitmap(), user_id_int, 0) self.redis.srem(RedisKeys.online_set(), user_id_str) self.redis.srem(RedisKeys.users_multi_cast(), user_id_str) self.redis.set(RedisKeys.user_status(user_id_str), UserKeys.STATUS_UNAVAILABLE) except Exception as e: logger.error('could not set_user_offline(): %s' % str(e)) logger.exception(traceback.format_exc()) raise e # force catch from caller
def remove_room(self, channel_id: str, room_id: str) -> None: if self.env.cache.get_channel_exists(channel_id) is None: if not self.channel_exists(channel_id): raise NoSuchChannelException(channel_id) if not self.room_exists(channel_id, room_id): raise NoSuchRoomException(room_id) self.redis.srem(RedisKeys.non_ephemeral_rooms(), room_id) self.redis.hdel(RedisKeys.room_name_for_id(), room_id) self.redis.delete(RedisKeys.room_roles(room_id)) self.redis.hdel(RedisKeys.rooms(channel_id), room_id) self.redis.hdel(RedisKeys.channel_for_rooms(), room_id)
def set_room_acls_for_action(self, action: str, acls: Dict[str, Dict[str, str]]) -> None: for room_id, values in acls.items(): key = RedisKeys.room_acls_for_action(room_id, action) self.redis.hmset(key, values) self.redis.expire(key, TEN_MINUTES) room_ids = list(acls.keys()) key = RedisKeys.rooms_with_action(action) # avoid race condition that could happen if we used lists instead; after clearing a # list and before filling it with this updated list of room ids, another client # might be querying redis and getting 0 results room_ids_str = ','.join(room_ids) self.redis.set(key, room_ids_str) self.redis.expire(key, TEN_MINUTES)
def get_channels_with_sort(self): key = RedisKeys.channels_with_sort() channels = self.cache.get(key) if channels is not None: return channels raw_channels = self.redis.hgetall(key) clean_channels = dict() if raw_channels is None or len(raw_channels) == 0: return None for channel_id, channel_sort_channel_name in raw_channels.items(): try: channel_sort_channel_name = str(channel_sort_channel_name, 'utf8') channel_sort, channel_tags, channel_name = channel_sort_channel_name.split( '|', maxsplit=2) channel_sort = int(channel_sort) channel_id = str(channel_id, 'utf8') if channel_tags == 'None': channel_tags = '' clean_channels[channel_id] = (channel_name, channel_sort, channel_tags) except Exception as e: logger.error( 'invalid channel name in redis with key {}, value was "{}": {}' .format(key, channel_sort_channel_name, str(e))) self.cache.set(key, clean_channels, ttl=ONE_MINUTE) return clean_channels
def add_sid_for_user(self, user_id: str, sid: str) -> None: all_sids = self.get_sids_for_user(user_id) if all_sids is None: all_sids = set() else: all_sids = set(all_sids) all_sids.add(sid) key = RedisKeys.sid_for_user_id() sid_key = RedisKeys.user_id_for_sid() for sid in all_sids: self.redis.hset(sid_key, sid, user_id) all_sids = ','.join(list(set(all_sids))) self.redis.hset(key, user_id, all_sids)
def set_avatar_for(self, user_id: str, avatar_url: str, app_avatar_url: str, app_avatar_safe_url: str) -> None: key = RedisKeys.avatars() cache_key = '{}-{}'.format(key, user_id) urls = '|'.join([avatar_url, app_avatar_url, app_avatar_safe_url]) self.cache.set(cache_key, urls, ttl=THIRTY_SECONDS) self.redis.hset(key, user_id, urls)
def _set_rooms_for_channel_with_info(self, channel_id: str, rooms_infos: dict) -> None: """ rooms_with_n_users[room_id] = { 'name': all_rooms[room_id]['name'], 'sort_order': all_rooms[room_id]['sort_order'], 'ephemeral': all_rooms[room_id]['ephemeral'], 'admin': all_rooms[room_id]['admin'], 'users': len(visible_users) } room_sort, room_ephemeral, room_admin, room_users, room_name = room_info.split('|', maxsplit=4) """ key = RedisKeys.rooms_for_channel_with_info(channel_id) self.cache.set(key, rooms_infos, ttl=TEN_SECONDS) redis_rooms = dict() for room_id, room_info in rooms_infos.items(): r_value = '{}|{}|{}|{}|{}'.format( str(room_info['sort_order']), str(room_info.get('ephemeral', True)).lower(), str(room_info.get('admin', False)).lower(), str(room_info.get('users', 0)), room_info['name']) redis_rooms[room_id] = r_value if len(redis_rooms) > 0: self.redis.hmset(key, redis_rooms) self.redis.expire(key, ONE_MINUTE)
def _get_rooms_for_channel_without_info(self, channel_id: str) -> dict: """ room.uuid: { 'ephemeral': room.ephemeral, 'name': room.name } """ key = RedisKeys.rooms_for_channel_without_info(channel_id) rooms = self.cache.get(key) if rooms is not None: return rooms raw_rooms = self.redis.hgetall(key) if raw_rooms is None or len(raw_rooms) == 0: return None clean_rooms = dict() for room_id, room_info in raw_rooms.items(): room_id = str(room_id, 'utf8') room_info = str(room_info, 'utf8') room_ephemeral, room_name = room_info.split('|', maxsplit=1) if room_ephemeral.lower() in {'', 'true'}: room_ephemeral = True else: room_ephemeral = False clean_rooms[room_id] = { 'name': room_name, 'ephemeral': room_ephemeral } return clean_rooms
def get_all_permanent_rooms(self): key = RedisKeys.all_permanent_rooms() rooms = self.redis.get(key) if rooms is None or len(rooms) == 0: return None return str(rooms, 'utf-8').split(',')
def get_user_status(self, user_id: str): key = RedisKeys.user_status(user_id) status = self.redis.get(key) if status is None or status == '': return None return str(status, 'utf-8')
def get_banned_users(self) -> dict: all_channels = self.redis.hgetall(RedisKeys.channels()) def get_banned_users_all_channels() -> dict: output = dict() for channel_id, _ in all_channels.items(): channel_id = str(channel_id, 'utf-8') bans = { 'name': b64e(self.get_channel_name(channel_id)), 'users': self.get_banned_users_for_channel(channel_id) } if len(bans['users']) > 0: output[channel_id] = bans return output def get_banned_users_all_rooms() -> dict: output = dict() for channel_id, _ in all_channels.items(): channel_id = str(channel_id, 'utf-8') rooms_for_channel = self.redis.hgetall(RedisKeys.rooms(channel_id)) for room_id, _ in rooms_for_channel.items(): room_id = str(room_id, 'utf-8') bans = { 'name': b64e(self.get_room_name(room_id)), 'users': self.get_banned_users_for_room(room_id) } if len(bans['users']) > 0: output[room_id] = bans return output return { 'global': self.get_banned_users_global(), 'channels': get_banned_users_all_channels(), 'rooms': get_banned_users_all_rooms() }
def get_history(self, room_id: str, limit: int = 100): if limit is None: limit = -1 messages = self.redis.lrange(RedisKeys.room_history(room_id), 0, limit) cleaned_messages = list() for message_entry in messages: message_entry = str(message_entry, 'utf-8') msg_id, published, user_id, user_name, target_name, channel_id, channel_name, msg = \ message_entry.split(',', 7) cleaned_messages.append({ 'message_id': msg_id, 'from_user_id': user_id, 'from_user_name': b64d(user_name), 'target_id': room_id, 'target_name': b64d(target_name), 'body': b64d(msg), 'domain': 'room', 'channel_id': channel_id, 'channel_name': b64d(channel_name), 'timestamp': published, 'deleted': False }) return cleaned_messages
def ban_user_channel(self, user_id: str, ban_timestamp: str, ban_duration: str, channel_id: str, reason: str=None, banner_id: str=None): user_name = '' try: user_name = self.get_user_name(user_id) except NoSuchUserException: pass self.redis.hset(RedisKeys.banned_users_channel(channel_id), user_id, '%s|%s|%s' % (ban_duration, ban_timestamp, user_name))