def auth_passwordreset_reset(reset_code: str, new_password: str) -> dict: ''' Given a reset code for a user, set that user's new password to the password provided. :param reset_code: generated random code sent to user :param new_password: user's new password to be reset :return: ''' # check if new password is valid if len(new_password) < 6: raise InputError(description='error occurred: Password entered is less ' 'than 6 characters long') # check if reset code match db_connect = DbConnector() db_connect.cursor() sql = "SELECT u_id FROM project.user_data WHERE reset_code=(%s)" value = (reset_code,) db_connect.execute(sql, value) ret = db_connect.fetchone() if ret is None: raise InputError(description='error occurred: Reset code entered does not match') # use for sql query u_id = ret[0] # UPDATE new password and remove the reset code from database password = hashlib.sha256(new_password.encode()).hexdigest() sql = "UPDATE project.user_data set password=(%s), reset_code = (%s) WHERE u_id=(%s)" value = (password, None, u_id) db_connect.execute(sql, value) # close database connection db_connect.close() return {}
def channel_details(token, channel_id): #InputError: channel_id does not refer to a valid channel. if not is_valid_channel(channel_id): raise InputError() #get authorised user id u_id_caller = data['tok_uid'][token] #AccessError: the authorised user is not already a member of the channel if not is_channel_member(u_id_caller, channel_id): raise AccessError() channel_info = get_channel_info(channel_id) return channel_info
def channels_create(token, name, is_public): ''' Input: token, name, is_public status Output: {channel_id}, creates a new channel with the input name and public / private ''' # verify the user if verify_token(token) is False: raise AccessError(description='Invalid token') # get database data = get_store() # getting user id from token u_id = get_tokens()[token] # check if name is valid if not isinstance(name, str) or len(name) > 20: raise InputError(description='Invalid channel name') # check if is_public is correct type if not isinstance(is_public, bool): raise InputError(description='Invalid channel status') # creating new channel details = name, is_public new_id = data.add_channel(u_id, details) # CREATING HANGMAN BOT hbot = create_hbot(new_id) # Inform the first user of option to disable hangman game message_send(hbot['token'], new_id, "For owners and admins: \ \nType '/disable game' to disable the hangman game. \nType '/enable game' to re-enable it.") return { 'channel_id': new_id }
def auth_login(email, password): password = hashlib.sha256(password.encode()).hexdigest() flag = 0 for user in data['users']: if user['email'] == email: found_user = user flag = 1 if user['token'] != "False": raise InputError('You are already logged in') if flag == 0: raise InputError('Email has not been registered previously') if password != found_user['password']: raise InputError('Password entered is not correct') token = jwt.encode({'u_id': found_user['u_id']}, 'jekfwbdkbwkf', algorithm='HS256').decode('utf-8') user['token'] = token return { 'u_id': found_user['u_id'], 'token': token, }
def user_profile_setemail(token, email): """Update the authorised user's email address""" # Check token is valid. if not isinstance(token, str): raise AccessError('Token is invalid') if not auth.check_token_exists(token): raise AccessError("Invalid token") # Check valid email. if not auth.check_valid_email(email): raise InputError("This is not a valid email") # Check email exists. if auth.check_element_exists(search_type="email", element=email): raise InputError("This email already exists") # Retrieve user_id user_id = get_user_id(token) # Retrieve user. user = get_user_profile(user_id) # Set new email. user["email"] = email return {}
def check_valid_react_id(react_id): """ Given a react_id, checks if the react_id is a valid React ID in the system Parameters: react_id (int): the unique id of a 'react' that will be reacted to Return: {} """ # List of valid react_id that is applicable in the system valid_react_id = [1] # Raise an InputError if the react_id is invalid else do nothing if react_id not in valid_react_id: raise InputError(description="react_id is not a valid React ID")
def register(): # Request information data = request.get_json() email = data["email"] password = data["password"] name_first = data["name_first"] name_last = data["name_last"] if not email_check(email): raise InputError(description="Email not valid") if email_dupe_check(email): raise InputError(description="Email already used") if len(password) < 6: raise InputError(description="Password less than 6 characters") if len(name_first) < 1 or len(name_first) > 50: raise InputError(description="First name is invalid") if len(name_last) < 1 or len(name_last) > 50: raise InputError(description="Last name is invalid") auth = auth_register(email, password, name_first, name_last) auth_token = auth['token'] auth_uid = auth['u_id'] return dumps({'token': auth_token, 'u_id': auth_uid})
def message_react(token, message_id, react_id): if not helper.check_valid_id(react_id): raise InputError('react_id is not a valid React ID') user_data = auth.get_data() u_id = helper.get_id(token, user_data) channel_data = get_data() react = helper.react_struct(react_id,u_id) for channel_id in channel_data: for message in channel_data[channel_id]['messages']: if message_id == message['message_id']: storage.add_react(channel_id, message_id,react) return {}
def message_unreact(token, message_id, react_id): '''Given a message within a channel the authorised user is part of, remove a "react" to that particular messageGiven a message within a channel the authorised user is part of, remove a "react" to that particular message''' DATA = getData() msg = get_message_from_messageID(DATA, message_id) ch = get_channel_from_msgID(DATA, message_id) userID = verify_token(token) # InputError: # Message_id is not a valid message within a channel that the authorised user has joined if not check_valid_msg(message_id): raise InputError('Invalid message ID') if not check_user_in_channel(userID, ch): raise InputError("User not in message's channel") # React_id is not a valid React ID if react_id != 1: raise InputError('Invalid react ID') # Message with ID message_id does not contain an active React with ID react_id reacted = False for r in msg['reacts']: if r['react_id'] == react_id: for users in r['u_ids']: if users == userID: reacted = True r['u_ids'].remove(users) if not reacted: raise InputError('No such react in given message') # Non-exception: remove react #for r in msg['reacts']: # if r['react_id'] == react_id: # msg['reacts'].remove(r) update_database(DATA) return {}
def channels_create(token, name, is_public): ''' Creates a new channel with that name that is either a public or private channel Input: token (str), name (str), is_public (boolean) Output: dict ''' auth_user = user_with_token(token) # Error check if auth_user is None: raise AccessError('Invalid token') elif len(name) > 20: raise InputError('Name longer than 20 characters') elif not name: raise InputError('Empty name') elif name.isspace(): raise InputError('Whitespace name') # Register new_channel = Channel(auth_user, name, is_public) data['channels'].append(new_channel) return { 'channel_id': new_channel.get_channel_id(), }
def user_profile_sethandle(token, handle_str): """ Changes and sets user's handle_str. Parameters: token(str): A string that validates the users actions while they are logged in handle_str(str): The handle the user wants to change to Returns: None """ # raise InputError if any of the inputs is empty if token == '': raise InputError("Token input left empty") if handle_str == '': raise InputError("Handle_str input left empty") # raise InputError if any of the inputs is an invalid type if not isinstance(token, str): raise AccessError("Invalid Token") if not isinstance(handle_str, str): raise InputError("Invalid Email") # raises an error if the handle is not of the required length if len(handle_str) < 3: raise InputError("handle string too short") elif len(handle_str) > 20: raise InputError("handle string too long") # check that token is authorised tok = authenticate_token(token) # check that handle_str is not being used by another user existing_handle(handle_str) user = data.users[tok] user.handle_str = handle_str return {}
def channel_join(token, channel_id): '''Given a channel_id, adds the authorised user to the channel''' database = get_data() # Find channel dict with given channel_id for channel in database["channels"]: if channel["channel_id"] == channel_id: # Check if channel is private if channel["is_public"] is False: raise AccessError(description="Channel is private") # Find user from token email = find_token(token) for user in database["users"]: if user["email"] == email: for member in channel["members"]: if user["u_id"] == member: raise InputError( description= 'User is already a member of the channel') channel["members"].append(user["u_id"]) with open('data_store.json', 'w') as FILE: json.dump(database, FILE) return {} raise AccessError(description="Invalid token") raise InputError(description="Invalid channel_id")
def user_permission_change_fn(token, u_id, permission_id, users): for user in users: if user['token'] == token: if user['permission_id'] != 1: raise AccessError( description='Must be an owner to change permission') break else: raise AccessError(description='Invalid Token') for user in users: if user['u_id'] == u_id: user['permission_id'] = permission_id return raise InputError(description='Invalid u_id')
def c_removeowner(): """ This is a flask wrapper for the channel_removeowner function Parameters: No parameters Returns: (dictionary): Empty dictionary """ data = request.get_json() token = data['token'] channel_id = int(data['channel_id']) u_id = int(data['u_id']) if not channel_check(channel_id): raise InputError(description="Channel is invalid") if not check_if_user_in_channel_owner_uid(u_id, channel_id): raise InputError(description="User not owner of channel") if not check_if_user_in_channel_owner(token, channel_id): raise AccessError(description="User not owner of channel") out = channel_removeowner(token, channel_id, u_id) return dumps(out)
def message_pin(token, message_id): """ Given a message within a channel, mark it as "pinned" to be given special display treatment by the frontend. Parameters: token (str): Authorisation hash for user message_id (int): Messages' message ID Returns: {} (dict): Empty dictionary """ if not isinstance(token, str): raise AccessError('Token is invalid') # Input Errors: # 1. Message_id is not a valid message message_list = list(filter(lambda msg: msg["message_id"] == message_id, \ data["messages"])) if len(message_list) == 0: raise InputError("Message does not exist") # 2. Message ID is already pinned if message_list[0]["is_pinned"] is True: raise InputError("Message already pinned") if auth.check_token_exists(token): auth_user = get_user_id(token) else: raise AccessError("User does not exist.") # Access Errors: # 1. Unauthorised user channel_id = message_list[0]["channel_id"] channel_data = get_channel(channel_id) if auth_user not in channel_data["owner_members"]: raise AccessError("User is not authorised") message_list[0]["is_pinned"] = True return {}
def auth_passwordreset_reset(server_data, reset_code, new_password): """ Given a reset code for a user set that user's new password to the password provided input: - reset_code (string) - new_password(string) output: - {} """ # reset_code is not a valid reset_code if not server_data.does_reset_code_exist(reset_code): raise InputError("Reset_code is invalid") # reset_code is valid, reset password else: # check if the new_password is invalid or not if len(new_password) < 6: raise InputError("Passowrd entered is not a valid password") else: user = server_data.get_user_by_reset_code(reset_code) user.password = new_password return {}
def message_pin(token, message_id): ''' Given a message within a channel, mark it as "pinned" to be given special display treatment by the frontend ''' u_id = get_u_id(token) channel_id = find_channel(message_id) message = find_message(channel_id, message_id) m_id = message_exists(message_id) auth_user = user_in_channel(u_id, channel_id) # Input errors for invalid / already pinned messages if not m_id: raise InputError("Invalid message ID") if message['is_pinned']: raise InputError("Message is already pinned") # Access error for non-owners / unauthorised users if not auth_user: raise AccessError("User is not a member of the channel") message['is_pinned'] = True return {}
def channels_listall(token, users, channels): return_list = [] for user in users: if token == user['token']: break else: raise InputError(description="Invalid Token, channels_listall") for channel in channels: return_list.append({ 'channel_id': channel['id'], 'name': channel['channel_name'] }) return dumps({'channels': return_list})
def message_unpin(token, message_id): ''' Given a message within a channel, remove its mark as unpinned ''' u_id = get_u_id(token) channel_id = find_channel(message_id) message = find_message(channel_id, message_id) m_id = message_exists(message_id) auth_user = user_in_channel(u_id, channel_id) # Input errors for invalid / already unpinned messages if not m_id: raise InputError("Invalid message ID") if not message['is_pinned']: raise InputError("Message is already unpinned") # Access error for non-owners / unauthorised users if not auth_user: raise AccessError("User is not a member of the channel") message['is_pinned'] = False return {}
def admin_userpermission_change(token, u_id, permission_id): # InputError: user id is invalid if not is_valid_uid(u_id): raise InputError(description='User with u_id does not exist') # InputError: permission_id is invalid (not 1 or 2) if permission_id != 1 and permission_id != 2: raise InputError(description='permission_id must be 1 or 2') # get authorised user's id u_id_caller = data['tok_uid'][token] # AccessError: authorised user is not a global owner (permission_id is 2) for user in data['users']: if user['u_id'] == u_id_caller: print(user['permission_id']) if user['permission_id'] == 2: raise AccessError(description='Regular users cannot alter global permissions') # else, update user's permission_id for user in data['users']: if user['u_id'] == u_id: user['permission_id'] = permission_id
def unreact(payload): ''' Function to remove a react from a message ''' global REACT_IDS # pylint: disable=W0603 user = get_user_from('token', payload['token']) message = find_message(payload['message_id']) channel = get_channel(message['channel_id']) if int(payload['react_id']) not in REACT_IDS: raise InputError(description='Unable to react with react_id ' + str(payload['react_id'])) if not test_in_channel(user['u_id'], channel): raise InputError(description='Unable to react as you are ' + 'not a part of that channel') for i in message['reacts']: if i['react_id'] == payload['react_id']: # this react is already present in the message # just remove u_id if user['u_id'] not in i['u_ids']: raise InputError(description='Attempting to uncreact ' + 'a message you have not reacted to') i['u_ids'].remove(user['u_id']) if len(i['u_ids']) == 0: # pylint: disable=C1801 # no one else has reacted, so remove react message['reacts'].remove(i) print(message['reacts']) return # unable to find react of react_id in messages raise InputError(description='Message with ID message_id ' + 'does not contain an active react with with ID react_id')
def check_is_reacted_already(channel_id, message_id, react_id, u_id): """ Checks if user has already reacted to message Parameters: channel_id(int): id of channel message_id(int):id of message react_id(int): id of react u_id(int): id of user Returns: Raises error if user has already reacted to message If it is valid it returns nothing """ if data.check_user_already_reacted(channel_id, message_id, react_id, u_id) is True: raise InputError(description='User has already reacted to this message')
def user_handle(): """ This is a flask wrapper for the user/profile/sethandle function Parameters: No parameters Returns: (dictionary): Empty dictionary """ data = request.get_json() token = data['token'] set_handle = data['handle_str'] if not token_check(token): raise InputError(description="Invalid token") if (len(set_handle) <= 2 or len(set_handle) >= 20): raise InputError(description="Invalid handle") if handle_check(set_handle): raise InputError(description="Handle already in use") user_profile_sethandle(token, set_handle) return dumps({})
def check_existing_handle(handle_str): """ Determine whether supplied handle already exists Parameters: handle_str(string): New handle Returns: Raises an error if handle already exists Returns nothing if handle doesn't exist """ if data.get_user_with({ "handle_str" : handle_str }) is not None: raise InputError(description = "Handle already in use") return
def check_guesser_not_creator(u_id, channel_id): """ Checks whether a user is guessing their own word Parameters: u_id(int) : An identifier for users channel_id(int): An identifier for channels Returns: InputError if the guesser also started the hangman session Nothing if the guesser and starter are different users """ status_message = data.get_hangman_status_message(channel_id) if status_message['u_id'] == u_id: raise InputError(description='Users can not guess their own word')
def check_valid_reset_code(reset_code): """ Determines who reset code belongs to Parameters: reset_code(str): A hash used for resetting a user's password Returns: User dictionary of whoever has the reset_code Raises an error if reset_code doesn't belong to a user """ user = data.get_user_with({'reset_code' : reset_code}) if user: return user raise InputError(description='Reset code is not valid')
def user_profile_setemail(token, email): ''' Updates the authorised user's email address. Args: token (str): of the user authorising this action email (str): user's new email address Raises: AccessError: if token invalid InputError: if email invalid as determined by is_valid_email module if email is already being used by another user ''' # verify that the token is valid if verify_token(token) is False: raise AccessError(description="Invalid token") # InputError if email not valid if is_valid_email(email) is False: raise InputError(description="Input is not a valid email") u_id = get_tokens()[token] data = get_store() # InputError if email not unique # Allow if the user is simply changing their email to their current email # again. if data.users.email_used(email): raise InputError( description="this email is already being used by another user") # Change the user's email in the STORE databas if the above hurdles are # passed data.users.set_email(u_id, email)
def user_profile_crop_image(token, img_url, x_start, y_start, x_end, y_end): """Given a URL of an image on the internet, crops the image within bounds (x_start, y_start) and (x_end, y_end). Position (0,0) is the top left.""" # Ensure token is valid - error checking in function user_id = auth_get_current_user_id_from_token(token) # Returns (filename, headers) if successful try: urllib_request.urlretrieve(img_url, "static/" + str(user_id) + ".jpg") except: raise InputError("img_url returns an HTTP status other than 200") original_img = Image.open("static/" + str(user_id) + ".jpg") original_width, original_height = original_img.size # Ensure image in correct format if original_img.format != "JPG" and original_img.format != "JPEG": raise InputError("Image uploaded is not a JPG") # Basic error checking for given coordinates if x_start < 0 or y_start < 0 or x_end < x_start or y_end < y_start: raise InputError("given dimensions incorrectly formatted") # Error checking that coordinates are within original image if x_end > original_width or y_end > original_height: raise InputError("Dimensions out of bounds from original image") cropped = original_img.crop((x_start, y_start, x_end, y_end)) cropped.save("static/" + str(user_id) + ".jpg") database["users"][user_id]["profile_img_url"] = ( get_host_url() + "static/" + str(user_id) + ".jpg" ) return {}
def message_pin(token, message_id): """Given a message within a channel, mark it as "pinned" to be given special display treatment by the frontend""" # Check token is valid - error checking in function user_id = auth_get_current_user_id_from_token(token) # Check message exists in database message_exists = False for ch in database["channels"].values(): if message_id in ch["messages"]: channel_id_for_message = ch["id"] message_exists = True if message_exists == False: raise InputError # Check message is not already pinned channel_msg = database["channels"][channel_id_for_message]["messages"][ message_id] if channel_msg["is_pinned"] == True: raise InputError( f"message with message id {message_id} is already pinned") # Check user is in channel within which the message exists if (database["users"][user_id]["is_admin"] is False and user_id not in database["channels"][channel_id_for_message]["all_members_id"]): raise AccessError( "user must be part of the channel to remove his/her message (see assumptions.md)" ) # Pin message if user is authorised - i.e. user is either a flockr owner # or the owner of the channel if (user_id in database["channels"][channel_id_for_message]["owner_members_id"] or database["users"][user_id]["is_admin"]): user_id_is_owner = True else: user_id_is_owner = False for msg in database["channels"][channel_id_for_message]["messages"].values( ): if msg["message_id"] == message_id and (user_id_is_owner): msg["is_pinned"] = True return {} # User not authorised to pin message raise AccessError
def check_stop_permission(u_id, channel_id): """ Checks if user has permission to use '/hangman stop' Parameters: u_id(int): Identifier used for users channel_id(int): Identifier used for channels Returns: Nothing if user has permission Raises InputError if user does not have permission """ channel = data.get_channel_info(channel_id) hang_info = data.get_hangman_info(channel_id) if u_id not in channel['owners'] and hang_info['u_id'] != u_id: # pragma: no cover raise InputError(description="User does not have permission to use command")