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)
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')
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 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 ''
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())
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 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))
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')
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)
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')
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)
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')
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)
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())
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