def channel_removeowner(token, channel_id, u_id): """ Remove the ownwer of channel with user with valid channel ownership. """ # Get info for error checking database = DataBase() channel = database.get_channel_info(channel_id) channel_member = database.get_account_info(token) # Check for errors if not channel or u_id not in channel['owners_id']: raise InputError('Channel does not exist or user is not an owner') if (channel_member['u_id'] not in channel['owners_id'] and channel_member['permissions'] != OWNER_PERMISSIONS): raise AccessError('Not an owner of this channel') # Update database channel['owners_id'].remove(u_id) database.close() return {}
def channel_join(token, channel_id): """ Add valid user to channel. """ # Get info from database for error checking database = DataBase() channel = database.get_channel_info(channel_id) joining_member = database.get_account_info(token) # Error checking if not channel: raise InputError('Channel does not exist') if joining_member['permissions'] != OWNER_PERMISSIONS: if not channel['is_public']: raise AccessError('Channel is private') # Update database channel['members_id'].append(joining_member['u_id']) database.close() return {}
def channel_addowner(token, channel_id, u_id): """ Add channel ownwers for user with valid channel ownership. """ # Get info for error checking database = DataBase() channel = database.get_channel_info(channel_id) channel_member = database.get_account_info(token) # Error checking if not channel or u_id in channel['owners_id']: raise InputError('Channel does not exist or user is not an owner') if channel_member['permissions'] != OWNER_PERMISSIONS: if channel_member['u_id'] not in channel['owners_id']: raise AccessError('Not an owner of the channel') # Update database channel['owners_id'].append(u_id) database.close() return {}
def channels_list(token): ''' Provide a list of all channels (and their associated details) that the auth user is part of Input: token (str) Output: dict ''' auth_user = user_with_token(token) # Error check if auth_user is None: raise AccessError('Invalid token') return { 'channels': [{ 'channel_id': channel.get_channel_id(), 'name': channel.get_name(), } for channel in data['channels'] if auth_user in channel.get_all_members()], }
def users_all(token): ''' Lists all users on the slackr Args: token (str) Raises: AccessError if token is invalid Returns: a dictionary containing a list of all users and their associated details - u_id, email, name_first, name_last, handle_str ''' # verify the token is valid if verify_token(token) is False: raise AccessError(description="invalid token") # return a dictionary which contains one key, "users", which is itself a list of dictionaries # containing types u_id, email, name_first, name_last, handle_str data = get_store() return {"users": data.users.all()}
def c_listall(): """ This is a flask wrapper for the channels_list_all function Parameters: No parameters Returns: (dictionary): A dictionary which contains the key called channels and is a list of channels and their associated details """ token = request.args.get('token') if not token_check(token): raise AccessError(description="Invalid token") return_dict = channels_list_all(token) return dumps(return_dict)
def channel_addowner(token, channel_id, u_id): """ Function that adds target user as owner. If global owner, then bypass check of caller being an owner or target being member. Parameters: token (str): A unique token to determine authorisation channel_id (int): A unique identifier of channels u_id (int): A unique identidier to indicate the target of this function Returns: {} """ # Check that token is valid caller_id = authenticate_token(token) caller = valid_user_id(caller_id) target = valid_user_id(u_id) # Check that channel_id is valid channel = valid_channel_id(channel_id) # Check that the caller is a member and an owner if caller.u_id not in [ user['u_id'] for user in channel.channel_details()['owner_members'] ]: raise AccessError(description="Caller is not an owner / member") # Check that the target is a member (If global owner, make member first) if not channel.existing_member(target): if target.permission_id == 1: channel.new_member(target) else: raise InputError(description="Target is not a member") # Check that not targeted at an owner if target.u_id in [ user['u_id'] for user in channel.channel_details()['owner_members'] ]: raise InputError(description="Target is already an owner") # If reached, here then successful channel.new_owner(target) return {}
def check_message_access(user_id, msg_check): """ Checks if the given message_id is valid for editing or removing; It is considered valid when either of these are true: - request is sent by the user who sent the message - request is made by the channel owner where the message is located - request is made by the flockr owner Parameters: user_id (int): the unique identification number of the person requesting the command msg_check (dict): a dictionary containing the keys is_message_valid (Bool), sent_msg (dict), msg_index (int) and ch_index (int) Return: {} """ # Get the data needed from msg_check sender_u_id = msg_check['sent_msg'].u_id ch_index = msg_check['ch_index'] # Check if command is requested by the same user who sent the message is_user = False if user_id == sender_u_id: is_user = True # Check if command is forcibly requested by admin (channel owner) is_owner = False check_owner_uid = [ user.u_id for user in data.channels[ch_index].owner_members ] if user_id in check_owner_uid: is_owner = True # Check if command is forcibly requested by flockr owner is_flockr_owner = False perm_status = data.users[user_id].permission_id if perm_status == 1: is_flockr_owner = True # Raise AccessError is neither of the conditions are True if not is_owner and not is_user and not is_flockr_owner: raise AccessError(description='Invalid authentication') return {}
def channel_invite(token, channel_id, u_id): """ Invite registered users to channels. """ # Open database and gather channel info and member info database = DataBase() channel = database.get_channel_info(channel_id) channel_member = database.get_account_info(token) add_member = database.get_info_from_id(u_id) # Error testing if not channel or not add_member: raise InputError('Channel doesn\'t exist or member doesn\'t exist') if channel_member['permissions'] != OWNER_PERMISSIONS: if channel_member['u_id'] not in channel['members_id']: raise AccessError('Not a member in the channel') # Update database channel['members_id'].append(u_id) database.close() return {}
def channel_details(token, channel_id): """ Obtain channel details from database with a valid token. """ # Open database amd gather important channel info for error checking database = DataBase() channel = database.get_channel_info(channel_id) channel_member = database.get_account_info(token) # Error checking if not channel: raise InputError('Channel does not exist') if channel_member['permissions'] != OWNER_PERMISSIONS: if channel_member['u_id'] not in channel['members_id']: raise AccessError('Not a member in the channel') result = {} result['name'] = channel['name'] result['owner_members'] = make_user_list(channel['owners_id']) result['all_members'] = make_user_list(channel['members_id']) return result
def pin(payload): # pylint: disable=R1711 'testing functionability for message pin' user = get_user_from('token', payload['token']) message = find_message(payload['message_id']) channel = get_channel(message['channel_id']) if message['is_pinned'] is True: raise InputError(description='Message is already pinned') if not test_in_channel(user['u_id'], channel): raise AccessError( description='You do not have permission to pin this message') if not check_owner(user, channel): raise InputError(description='You do not have permission') message['is_pinned'] = True return
def standup_start(token, channel_id, length): ''' This function would start a standup in given channel. Standup would last X second and X is equal to length. After time is up, what is send during standup would be buffered in one message and be sent to given channel. It would return the finish_time. Args: param1(str): request user's token param2(int): the id of target channel param3(int): the time would standup last (in second) Returns: It would return a dict with one tuple 'time_finish' and its value is an integer (unix timestamp) Raises: InputError: 1. Channel ID is not a valid channel 2. An active standup is currently running in this channel AccessError: given token is invalid ''' user = get_user_from_token(token) channel = get_channel_from_id(channel_id) # access error when given token is invalid if user is None: raise AccessError(description='Invalid token') # input error when given channel_id is invalid if channel is None: raise InputError(description='Invalid channel_id') # input error when an active standup is currently runing in this channel if standup_active(token, channel_id)['is_active'] is True: raise InputError(description='Standup has started') # reset data for standup channel['time_standupend'] = int(time.time()) + length channel['standup_msg'] = '' # set standup_end timer = threading.Timer(length, standup_end, args=[user, channel]) timer.start() return {'time_finish': int(time.time()) + length}
def user_profile_sethandle(token, handle_str): ''' Updates the authorised user's handle. Args: token (str): of the user authorising this action handle_str (str): user's new handle Raises: AccessError: if token not valid InputError: if handle_str not between 2 and 20 characters inclusive if handle_str not unique to user ''' # verify the token is valid if verify_token(token) is False: raise AccessError(description="Invalid token") # verify the new handle_str is of the correct length if len(handle_str) < MIN_HANDLE_LEN: raise InputError( description= "handle_str too short - it must be between 2 and 20 characters inclusive" ) if len(handle_str) > MAX_HANDLE_LEN: raise InputError( description= "handle_str too long - - it must be between 2 and 20 characters inclusive" ) u_id = get_tokens()[token] data = get_store() # verify the new handle_str is unique # allow the "change" if the authorised user's new handle_str is identical # to their old one. if data.users.get_handle( u_id) != handle_str and not data.users.handle_unique(handle_str): raise InputError(description="new handle_str not unique to this user") # change the handle_str in the database data.users.set_handle(u_id, handle_str)
def message_remove(token, message_id): """ Remove message in channel """ database = DataBase() user_id = database.get_account_info(token)['u_id'] channel_id = str(message_id) channel_id = NO_CHANNELS - int(channel_id[0:5]) channel = database.get_channel_info(channel_id) channel_msgs = database.get_channel_messages(channel_id) message_user_id = int(str(message_id)[10:]) # Check users allowed to delete messages if user_id not in channel['owners_id'] and user_id != message_user_id: raise AccessError('Not a member of channel') # Check if message exists to delete in list message_index = find_message_index(message_id, channel_msgs) channel_msgs.pop(message_index) database.close() return {}
def message_unpin(token, message_id): ''' Given a message within a channel, remove it's mark as unpinned Parameters: token - The user's token that was generated from their user id message_id - The id of the message to be unpinnned Returns: An empty dictionary Errors: InputError: Message_id is not a valid message Message with ID message_id is already unpinned AccessError: The authorised user is not a member of the channel that the message is within The authorised user is not an owner ''' check_token(token) data = get_data() if not find_id_in_list(data['messages'], message_id, 'message_id'): raise InputError(description="Message does not exist") for message in data['messages']: if message['message_id'] == message_id: msg = message if not msg['is_pinned']: raise InputError(description="This message is not pinned") u_id = get_user_from_token(token) user = data['users'][u_id - 1] for channel in data['channels']: if not find_id_in_list(channel['all_members'], u_id, 'u_id'): raise AccessError( description='The authorised user is not in this channel') else: curr_channel = channel if find_id_in_list(curr_channel['owner_members'], u_id, 'u_id') or user['permission_id'] == 1: msg['is_pinned'] = False return {} raise InputError( description="The authorised user does not have owner priveledge \ to unpin message in this channel")
def search(token, query_str): ''' Given a query string, return a collection of messages in all of the channels that the user has joined that match the query Input: token (str), query_str (str) Output: dict where 'messages' maps to a list of dictionaries containing messages where query_str is a substring of the message content ''' # Error check auth_user = user_with_token(token) if auth_user is None: # Invalid token raise AccessError('Invalid token') messages = [] for channel in data['channels']: # Channels the auth_user is a member of if auth_user in channel.get_all_members(): for message in channel.get_messages(): # query_str is a substring of message if query_str in message.get_message(): messages.append(message) return { 'messages': [ { 'message_id': message.get_message_id(), 'u_id': message.get_sender().get_u_id(), 'time_created': message.get_time_created(), 'message': message.get_message(), 'reacts': [ { 'react_id': react.get_react_id(), 'u_ids': [reactor.get_u_id() for reactor in react.get_reactors()], 'is_this_user_reacted': auth_user in react.get_reactors(), } for react in message.get_reacts() ], 'is_pinned': message.get_is_pinned(), } for message in messages ], }
def comments_add(u_id, cocktail, message, time): """ Given a u_id, sessionKey, cocktail and message, add the information about this comment to the database Parameters ---------- u_id :: int sessionKey :: int cocktail :: str message :: str Returns ------- Dict 'comment_id' :: int Raises ------ AccessError Invalid u_id or not logged in InputError Empty or long message ( > 1000 char) """ if u_id > len(data.users): raise AccessError() elif len(message) <= 0 or len(message) > 1000: raise InputError comment_index = len(data.comments) # create a new entry for comment data.comments.append({}) data.comments[comment_index]['drink'] = cocktail data.comments[comment_index]['comment_id'] = comment_index data.comments[comment_index]['message'] = message # data.comments[comment_index]['u_id'] = u_id data.comments[comment_index]['u_id'] = u_id data.comments[comment_index]['ldTime'] = time return { 'comment_id': comment_index, }
def channel_removeowner(token, channel_id, u_id): """ When there is only one owner in the channel and this owner is removed, there should be another user in this room randomly became the owner. """ # Generate the channel which match the channel_id user_who_remove_others_uid = auth_get_current_user_id_from_token(token) channel = get_channel_from_id(channel_id) if u_id not in channel["owner_members_id"]: raise InputError("User is not a owner, can not be removed") if (user_who_remove_others_uid not in channel["owner_members_id"] and database["users"][user_who_remove_others_uid]["is_admin"] is False): raise AccessError("User is not authorized") if len(channel["all_members_id"]) == 1: # If a owner are the only member of a channel, when this owner # remove owner himself, he will automatically leave the channel # we can't use channel_leave, because we want to remove u_id, not u_id # of token (token might be an admin's token) channel["all_members_id"].remove(u_id) channel["owner_members_id"].remove(u_id) else: # If the owner is the only owner in the channel if len(channel["owner_members_id"]) == 1: # Generate a member of channel to become the owner owner_iterator = iter( [user for user in channel["all_members_id"] if user != u_id]) next_owner_uid = next(owner_iterator, None) # We assume we will always find a member in the channel # to become owner since length of channel["all_members_id"] # is bigger than 2 assert next_owner_uid != None channel["owner_members_id"].append(next_owner_uid) channel["owner_members_id"].remove(u_id) else: channel["owner_members_id"].remove(u_id)
def channel_messages(token, channel_id, start, CHANNELS, USERS, MESSAGES): channel_is_valid(channel_id, CHANNELS) index = position_in_user_data(token, USERS) u_id = USERS[index]['u_id'] if u_id not in CHANNELS[int(channel_id) - 1]['users']: raise AccessError( "Authorised user is not a member of channel with channel_id") if int(start) > len(MESSAGES) and MESSAGES: raise InputError( "start is greater than the total number of messages in the channel" ) return_messages = [] if MESSAGES: reversedmsg = reversed(MESSAGES) for message in reversedmsg: if int(message['in_channel']) == int(channel_id): reactbool = False if u_id in message['reacts'][0]['u_ids']: reactbool = True return_messages.append({ 'message_id': message['message_id'], 'u_id': message['u_id'], 'message': message['message'], 'time_created': message['time_created'], 'reacts': [{ 'react_id': 1, 'u_ids': message['reacts'][0]['u_ids'], 'is_this_user_reacted': reactbool }], 'is_pinned': message['is_pinned'] }) return dumps({'messages': return_messages, 'start': int(start), 'end': -1})
def channel_leave(token, channel_id, users, channels): u_id = 0 channel_id = int(channel_id) for user in users: if user['token'] == token: u_id = user['u_id'] break else: raise InputError(description="Invalid Token, Channel Leave") for channel in channels: if channel['id'] == channel_id: if u_id in channel['users']: channel['users'].remove(u_id) if u_id in channel['owners']: channel['owners'].remove(u_id) return dumps({}) raise AccessError(description="You are not in this channel.") raise InputError(description="Channel does not exist. Channel_leave")
def permission_change(payload): #what does it mean for permision id to not refer to permission id? ''' changes the permision of a authorised uid We must also update their permissions in all channels they have joined ''' if validate_uid(payload['u_id']) is False: raise InputError(description='Invalid u_id') owner = get_user_token(payload['token']) change_user = get_user_from('u_id', payload['u_id']) if owner['permission_id'] != 1: raise AccessError(description='The authorised user is not an owner') change_user['permission_id'] = payload['permission_id'] '''
def c_list(): """ This is a flask wrapper for the channel_list function Parameters: No parameters Returns: (dictionary): This dictionary contains a list of all the channels that the user is part of and their associated details """ token = request.args.get('token') if not token_check(token): raise AccessError(description="Invalid token") return_dict = channel_list(token) return dumps(return_dict)
def remove(payload): # pylint: disable=R1711 ''' Function to remove a message from a channel ''' user = get_user_from('token', payload['token']) messages = get_messages_store() message = find_message(payload['message_id']) channel = get_channel(message['channel_id']) if message['u_id'] != user['u_id']: if not check_owner(user, channel): raise AccessError(description='You do not have permission') messages.remove(message) channel['messages'].remove(message) return
def find_channel_with_message(message_id, u_id): """ Will go through every channel and if message_in_channel returns a channel it will return it otherwise it raises an Input Error Parameters: message_id(string): The id of the message being searched for user_id(string): The id of the user searching for the message it will return an access error if the user is not an owner of the channel or creator of the message Returns: channel(channel dictionary): If the channel was found it will return the channel dictionary """ channel_id = data.find_channel(message_id) message = data.get_message(channel_id, message_id) if message["u_id"] == u_id or data.check_channel_owner(channel_id, u_id): return channel_id raise AccessError(description="User is not creator or owner")
def channel_invite(token, channel_id, u_id): inviter_user_id = auth_get_current_user_id_from_token(token) try: database["users"][u_id] except KeyError: raise InputError(f"{u_id} is an invalid user id") channel = get_channel_from_id(channel_id) if inviter_user_id not in channel["all_members_id"]: raise AccessError( f"user {inviter_user_id} not authorized to invite you to this channel" ) if u_id not in channel["all_members_id"]: channel["all_members_id"].append(u_id) return {}
def channels_list(token: str) -> dict: """ Provide a list of all channels (and their associated details) that the authorised user is part of :param token: user's token :return: dictionary of list of dictionary with keys 'channel_id' and 'name' which includes the channels that the user is a part of """ dictionary = { 'channels': [], } token_operation = TokenJwt() # check if the token is valid if check_valid(token) is False: raise AccessError(description='error occurred: token is not valid') # get user's u_id from token u_id = token_operation.get_uid(token) # get info in database db_connect = DbConnector() db_connect.cursor() sql = ''' SELECT channel_id, name FROM project.channel_data c INNER JOIN project.user_data u ON u.u_id = ANY (c.member) WHERE u.u_id = (%s); ''' value = (u_id, ) db_connect.execute(sql, value) ret = db_connect.fetchall() for channel in ret: dictionary["channels"].append({ 'channel_id': channel[0], 'name': channel[1] }) db_connect.close() return dictionary
def user_profile_setname(token, name_first, name_last): """ Changes and sets users first and last name. Parameters: token(str): A string that validates the users actions while they are logged in name_first(str): The new first name of user name_last(string): The new last name of user Returns: None """ # raise InputError if any of the inputs is empty if token == '': raise InputError("token input left empty") if name_first == '': raise InputError("name_first left empty") if name_last == '': raise InputError("name_last left empty") # raise InputError if any of the inputs is an invalid type if not isinstance(token, str): raise AccessError("token of wrong data type") if not isinstance(name_first, str): raise InputError("name_first of wrong data type") if not isinstance(name_last, str): raise InputError("name_last of wrong data type") # name_first is not between 1 and 50 characters or # name_last is not between 1 and 50 characters if (len(name_first) > 50 or len(name_first) < 1): raise InputError("name_first length is out of range") if (len(name_last) > 50 or len(name_last) < 1): raise InputError("name_last length is out of range") # check that token is authorised tok = authenticate_token(token) user = data.users[tok] user.name_first = name_first user.name_last = name_last return {}
def standup_start(token, channel_id, length): token_operation = TokenJwt() # check if the token is valid if check_valid(token) is False: raise AccessError(description='error occurred: token is not valid') # check channel_id is not valid db_connect = DbConnector() db_connect.cursor() sql = "SELECT channel_id FROM project.channel_data WHERE channel_id=(%s)" value = (channel_id, ) db_connect.execute(sql, value) ret = db_connect.fetchone() if ret is None: raise InputError( description='error occured: Channel ID is not a valid channel') curr_time = int(time.time()) exist_active = standup_active(token, channel_id)["is_active"] if exist_active is True: raise InputError(description='error occurred:\ An active standup is currently running in this channel' ) finish_time = curr_time + length # get id from token u_id = token_operation.get_uid(token) sql = "INSERT INTO project.active_data (standup_uid, channel_id, time_finish) VALUES (%s,%s,%s)" value = (u_id, channel_id, int(finish_time)) db_connect.execute(sql, value) # time_dict = { # 'standup_uid': u_id, # 'channel_id': channel_id, # 'time_finish': int(finish_time), # 'message': "" # } # ACTIVE_DATA.append(time_dict) time1 = threading.Timer(length, send_standup_message, [channel_id]) time1.start() return {'time_finish': int(finish_time)}
def channel_removeowner(token, channel_id, u_id): ''' Remove user from owner list. Parameters: token - channel_id - u_id - Returns: {} Errors: InputError: Invalid channel id. User is not already an owner of the channel. AccessError: The authorised person is not an owner of channel or owner of slackr. ''' check_token(token) data = get_data() if not find_id_in_list(data['channels'], channel_id, 'channel_id'): raise InputError(description='Channel does not exist') current_channel = data['channels'][channel_id - 1] if not find_id_in_list(current_channel['owner_members'], u_id, 'u_id'): raise InputError(description='User is not an owner of the channel') person_u_id = get_user_from_token(token) person = get_user_data(person_u_id) person_is_owner = find_id_in_list(current_channel['owner_members'], person_u_id, 'u_id') person_in_channel = find_id_in_list(current_channel['all_members'], person_u_id, 'u_id') if person_is_owner or (person_in_channel and person['permission_id'] == 1): target = get_user_data(u_id) user_info = { 'u_id': target['u_id'], 'name_first': target['name_first'], 'name_last': target['name_last'], } data['channels'][channel_id - 1]['owner_members'].remove(user_info) return {} raise AccessError(description='The authorised person has no permission')
def user_permission(token, u_id, permission_id): '''Sets a user's permissions to new permission_id.''' database = get_data() # checks if the token given is valid email = check_token(token) check_user = False valid_ids = [1, 2] if permission_id not in valid_ids: raise InputError('Invalid permission_id') # checks if u_id and token refers to a valid user for user in database["users"]: if user["email"] == email: if user["permissions"]["global"] == 2: raise AccessError(description='User is not an owner') if user["u_id"] == u_id: user["permissions"]["global"] = permission_id store_data(database) return {} raise InputError(description='u_id does not refer to a valid user')