def channel_details(token, channel_id): ''' Give the details of a channel ------- token: string Requester token channel_id: int id of the channel -------- ValueError when: channel_id does not refer to a valid channel that the authorised user is part of. AccessError when: token does not refer to a valid user which is part of the channel -------- Returns a dictionnary containing the name of the channel and info about the users and owners ''' type_check_token(token) type_check_channel_id(channel_id) check_channel_exists(channel_id) check_user_in_channel_access_error(auth_check_token(token), channel_id, 'The requester user is not in channel') channel_name = store.get('channel_data', 'channel_id', channel_id)[0]['name'] channel_owners = store.get('channel_data', 'channel_id', channel_id)[0]['owners'] channel_members = store.get('channel_data', 'channel_id', channel_id)[0]['members'] return {'name': channel_name, 'owner_members': channel_owners, 'all_members': channel_members}
def admin_userpermission_change(token, u_id, permission_id): """ Given a user ID, set their permissions to new permissions described by permission_id. """ if not isinstance(u_id, int): raise ValueError("u_id is not an int") if not isinstance(permission_id, int): raise ValueError("permission_id is not an int") if permission_id < 1 or permission_id > 3: raise ValueError("permission_id is not valid") # Check requesting user's permissions req_u_id = auth_check_token(token) req_user = store.get("users", "u_id", req_u_id)[0] req_perm = req_user.get("permission_id") if req_perm == 3: raise AccessError("requesting user is not an owner or admin") if req_perm == 2 and permission_id == 1: raise AccessError("admins cannot make users owners") # Check target user results = store.get("users", "u_id", u_id) if len(results) != 1: raise ValueError(f"user with u_id {u_id} does not exist") target = results[0] target_perm = target.get("permission_id") if req_perm == 2 and target_perm == 1: raise AccessError("admins cannot change owners' permissions") # Execute permission change index = store.update("users", "permission_id", permission_id, "u_id", u_id) if index == 0: raise ValueError("Invalid user ID") return {}
def create_member_from_u_id(u_id): ''' Given a u_id, this function returns it under the 'member' form. ''' member = { 'u_id' : u_id, 'name_first' : store.get('users', 'u_id', u_id)[0]['name_first'], 'name_last' : store.get('users', 'u_id', u_id)[0]['name_last'] } return member
def validate_user(token, update_id, input_type: str, pin=False, \ react_channel_id="tmp", react_message_id="tmp"): ''' Function determined if the user has sufficient permissions to perform functionality specified in arguments''' u_id = auth_check_token(token) if input_type == "channel": user_data = store.get("users", "u_id", u_id)[0] for channel in user_data["channels"]: if channel["channel_id"] == update_id: return raise AccessError( "User is not part of channel or channel does not exist") if input_type == "message": message_data = get_message_data_from_m_id(update_id) if u_id == message_data["u_id"]: return channel_data = get_channel_data_from_m_id(update_id) for owner in channel_data["owners"]: if owner["u_id"] == u_id: return if is_admin(token): return raise AccessError("User doesn't have permission to alter message") if input_type == "react": # Not sure the values a react id should have hence jsut return message_data = store.get("channel_data", "channel_id", react_channel_id)[0]["messages"] for message in message_data: if message["message_id"] == react_message_id: for react in message["reacts"]: for uid in react["u_ids"]: if uid["u_id"] == u_id: if pin: return raise ValueError("User already reacted") if pin: raise ValueError return if input_type == "pin": message = get_message_data_from_m_id(update_id) if not is_admin(token): raise ValueError("User not admin, must be admin to pin") if message["is_pinned"] and pin: raise ValueError("Message already pinned") if not message["is_pinned"] and not pin: raise ValueError("Message is not pinned") return raise ValueError( "Incorrect type to validate: use 'channel', 'message' or 'react'")
def auth_check_token(token): """ Given a token, verify that it is a valid token, with a valid session ID, and return the u_id of the token owner """ # Decode token try: payload = jwt.decode(token, HMAC_SECRET, algorithms=['HS256']) except: # invalid token raise AccessError("Error parsing & verifying token") # Check if session exists in store results = store.get("sessions", "jwt_id", payload.get("jti")) if not results: raise AccessError("Invalid session token") # Check that session belongs to this user # multiple sessions may share a session ID, so we iterate through # this is pretty unlikely though, so we comment this out # sess = None # for index in results: # if index.get("u_id") == payload.get("uid"): # sess = index # if sess == None: # raise AccessError("Invalid session token") return results[0].get("u_id")
def get_channel_id_from_message_id(message_id): ''' Helper function to get channel_id from message id''' channel_data = store.get("channel_data") for channel in channel_data: for message in channel["messages"]: if message["message_id"] == message_id: return channel["channel_id"] raise ValueError("Message ID doesn't exist")
def get_channel(channel_id): ''' This function gets channel storage value from channel_id. ''' return { 'channel_id' : channel_id, 'name' : store.get('channel_data', 'channel_id', channel_id)[0]['name'] }
def channel_removeowner(token, channel_id, u_id): ''' Change an owner into an user in the channel ------- token: string Changer token channel_id: int id of the channel u_id: int id of the owner who will become user -------- ValueError when: channel_id does not refer to a valid channel that the authorised user is part of. u_id does not refer to a valid user which is part of the channel u_id is already an owner of the channel token does not refer to a valid user AccessError when: the authorised user is not an owner of the slackr, or an owner of this channel -------- Returns {} ''' type_check_token(token) type_check_channel_id(channel_id) type_check_u_id(u_id) check_channel_exists(channel_id) if not owner_in_channel(u_id, channel_id): raise ValueError('The user with ID u_id is not an owner of the channel') requester_u_id = auth_check_token(token) req_perm = store.get('users', 'u_id', requester_u_id)[0]['permission_id'] if req_perm == 3 and not user_in_channel(requester_u_id, channel_id): raise ValueError('The requester is not part of the channel and is also not an admin/owner') if req_perm == 3 and not owner_in_channel(requester_u_id, channel_id): raise AccessError('Requester has not the right to add an owner') if nb_of_channel_owners(channel_id) == 1: raise ValueError('The requested u_id is the only owner of the channel') channel_owners = store.get('channel_data', 'channel_id', channel_id)[0]['owners'] remove_member_from_list(u_id, channel_owners) store.update('channel_data', 'owners', channel_owners, 'channel_id', channel_id) return {}
def get_message_data_from_m_id(message_id): ''' Helper function to get message data from message_id''' channel_data = store.get("channel_data") for channel in channel_data: for message in channel["messages"]: if message["message_id"] == message_id: return message raise ValueError("message id doesn't exist")
def standup_end(token, channel_id): ''' Prints executive summary as a message and closes standup''' standup_data = store.get("standup", "channel_id", channel_id)[0] standup_data["is_active"] = False index = standup_data["standup_id"] summary = standup_data["summaries"][index] print(f"Summary: {summary}") if summary != "Standup summary:\n": message_send(token, channel_id, summary)
def get_next_message_id(): ''' Helper function to get next message id to be assigned''' try: message_id = store.get("next_id", "type", "message_id")[0]["value"] store.update("next_id", "value", message_id + 1, "type", "message_id") except ValueError: message_id = 0 store.insert("next_id", {"type": "message_id", "value": 1}) return message_id
def channel_leave(token, channel_id): ''' Leaves a channel ------- token: string leaver token channel_id: int id of the channel -------- ValueError when: channel_id does not refer to a valid channel that the authorised user is part of. token does not refer to a valid user which is part of the channel -------- Returns {} ''' type_check_token(token) type_check_channel_id(channel_id) check_channel_exists(channel_id) req_u_id = auth_check_token(token) check_user_in_channel_value_error(req_u_id, channel_id, 'User is not in channel') if nb_of_channel_members(channel_id) == 1: raise ValueError('You can\'t quit a channel with 1 member') if nb_of_channel_owners(channel_id) == 1 and owner_in_channel(req_u_id, channel_id): raise ValueError('You are the only owner of the channel') members_before = store.get("channel_data", "channel_id", channel_id)[0].get("members") remove_member_from_list(req_u_id, members_before) store.update("channel_data", "members", members_before, "channel_id", channel_id) owners_before = store.get("channel_data", "channel_id", channel_id)[0].get("owners") try: remove_member_from_list(req_u_id, owners_before) store.update("channel_data", "owners", owners_before, "channel_id", channel_id) except ValueError: pass channels_before = store.get("users", "u_id", req_u_id)[0].get("channels") remove_channel_from_list(channel_id, channels_before) store.update("users", "channels", channels_before, "u_id", req_u_id) return {}
def channel_addowner(token, channel_id, u_id): ''' Change a user into an owner in the channel ------- token: string Changer token channel_id: int id of the channel u_id: int id of the user who will become owner -------- ValueError when: channel_id does not refer to a valid channel that the authorised user is part of. u_id does not refer to a valid user which is part of the channel u_id is already an owner of the channel token does not refer to a valid user AccessError when the authorised user is not already a member of the channel -------- Returns {} ''' type_check_token(token) type_check_channel_id(channel_id) type_check_u_id(u_id) check_channel_exists(channel_id) if owner_in_channel(u_id, channel_id): raise ValueError('The user with ID u_id is already an owner of the channel') requester_u_id = auth_check_token(token) req_perm = store.get('users', 'u_id', requester_u_id)[0]['permission_id'] if req_perm == 3 and not user_in_channel(requester_u_id, channel_id): ValueError('Requester is not part of the channel, and is a normal user in the slackr') if req_perm == 3 and not owner_in_channel(requester_u_id, channel_id): raise AccessError('Requester has not the right to add an owner') channel_owners = store.get('channel_data', 'channel_id', channel_id)[0]['owners'] channel_owners.append(create_member_from_u_id(u_id)) store.update('channel_data', 'owners', channel_owners, 'channel_id', channel_id) return {}
def standup_send(token, channel_id, message): ''' Sends a standup message and a normal message''' message_send(token, channel_id, message) standup_data = store.get("standup", "channel_id", channel_id)[0] if not standup_data["is_active"]: raise AccessError summaries = standup_data["summaries"] summaries[standup_data["standup_id"]] += get_name_from_token(token) + ' - ' + message + '\n' store.update("standup", "summaries", summaries, "channel_id", channel_id) return {}
def channel_exist(channel_id): ''' This function finds if the channel_id exists or not. ''' channel_list = store.get('channel_data') channel_exist_bool = False for channel in channel_list: if channel_id == channel['channel_id']: channel_exist_bool = True return channel_exist_bool
def owner_in_channel(u_id, channel_id): ''' This function finds if the u_id is an owner of the channel or not. ''' members = store.get('channel_data', 'channel_id', channel_id)[0]['owners'] is_in_channel = False for member in members: if u_id == member['u_id']: is_in_channel = True return is_in_channel
def channel_invite(token, channel_id, u_id): ''' Invite a user in the channel ------- token: string Inviter token channel_id: int id of the channel u_id: int id of the user invited -------- ValueError when: channel_id does not refer to a valid channel that the authorised user is part of. u_id does not refer to a valid user which is part of the channel token does not refer to a valid user AccessError when the authorised user is not already a member of the channel -------- Returns {} ''' type_check_token(token) type_check_channel_id(channel_id) type_check_u_id(u_id) check_channel_exists(channel_id) check_user_not_in_channel_value_error(u_id, channel_id, 'u_id is already part of the channel') check_user_in_channel_access_error(auth_check_token(token), channel_id, 'The token is not a member of channel') channel_members = store.get('channel_data', 'channel_id', channel_id)[0]['members'] new_member = create_member_from_u_id(u_id) channel_members.append(new_member) store.update('channel_data', 'members', channel_members, 'channel_id', channel_id) user_channels = store.get('users', 'u_id', u_id)[0]['channels'] user_channels.append(get_channel(channel_id)) store.update('users', 'channels', user_channels, 'u_id', u_id) return {}
def channel_join(token, channel_id): ''' Join a channel ------- token: string joiner token channel_id: int id of the channel -------- ValueError when: channel_id does not refer to a valid channel that the authorised user is part of. token does not refer to a valid user which is not part of the channel AccessError whenchannel_id refers to a channel that is private - when the authorised user is not an admins -------- Returns {} ''' type_check_token(token) type_check_channel_id(channel_id) check_channel_exists(channel_id) req_u_id = auth_check_token(token) check_user_not_in_channel_value_error(req_u_id, channel_id, 'token is already in channel') req_permission = store.get('users', 'u_id', req_u_id)[0]['permission_id'] channel_is_public = store.get('channel_data', 'channel_id', channel_id)[0]['is_public'] if (not channel_is_public) and (req_permission == 3): raise AccessError('The user is not an admin and he tries to access a private channel') member = create_member_from_u_id(req_u_id) new_list_members = store.get('channel_data', 'channel_id', channel_id)[0]['members'] new_list_members.append(member) store.update('channel_data', 'members', new_list_members, 'channel_id', channel_id) user_channels = store.get('users', 'u_id', req_u_id)[0]['channels'] user_channels.append(get_channel(channel_id)) store.update('users', 'channels', user_channels, 'u_id', req_u_id) return {}
def channels_create(token, name, is_public): ''' Create a new channel -------- token: string token of the requester name: string name of the channel is_public: bool boolean indicating if the channel will be public or private \u207b------- ValueError when: name is more than 20 characters long -------- return: channel_id ''' type_check_token(token) if not isinstance(name, str): raise ValueError('Name is not a string') if not isinstance(is_public, bool): raise ValueError('is_public is not a boolean') if not isinstance(name, str): raise ValueError('Name is not a string') if len(name) > 20: raise ValueError('Name is more than 20 characters long') if len(name) <= 0: raise ValueError('Name is invalid') channel_id = store.n_elems('channel_data') + 1 u_id = auth_check_token(token) member = create_member_from_u_id(u_id) store.insert('channel_data', { 'channel_id': channel_id, 'name': name, 'messages': [], 'members': [member], 'owners': [member], 'is_public': is_public }) store.insert('channels', { 'channel_id': channel_id, 'name' : name }) channels = store.get('users', 'u_id', u_id)[0]['channels'] channels.append({ 'channel_id': channel_id, 'name' : name }) store.update('users', 'channels', channels, 'u_id', u_id) return {'channel_id': channel_id}
def channel_messages(token, channel_id, start): ''' Give a list of me # Checking for a valid channel_id. ssages of a channel, starting from an index ------- token: string Requester token channel_id: int id of the channel start: int starting index of the channel -------- ValueError when: channel_id does not refer to a valid channel that the authorised user is part of. token does not refer to a valid user start is either greater or equal to total number of messages in the channel, or is negative AccessError when: Authorised user is not a member of channel with channel_id -------- Returns - start and end index (-1 if it reached the end) - and all of the messages between the 2 index ''' type_check_token(token) type_check_channel_id(channel_id) if not isinstance(start, int): raise ValueError('start is not an int') check_channel_exists(channel_id) check_user_in_channel_access_error(auth_check_token(token), channel_id, 'requester is not a member of the channel') messages = store.get('channel_data', 'channel_id', channel_id)[0]['messages'] if (start < 0 or start > len(messages)): raise ValueError('Start index is not correctly indexed') start_index = - (start + 1) end_return = -1 returned_messages = [] if start + 50 <= len(messages): end_return = start + 49 returned_messages = messages[start_index: - (start + 51):-1] else: returned_messages = messages[start_index::-1] return { 'messages': returned_messages, 'start' : start, 'end': end_return }
def user_profile(u_id): results = store.get("users", "u_id", u_id) if len(results) != 1: raise ValueError("u_id is not valid") user = results[0] return { "email": user.get("email"), "name_first": user.get("name_first"), "name_last": user.get("name_last"), "handle_str": user.get("handle_str"), "profile_img_url": user.get("profile_img_url") }
def user_all(): # We need to filter the dicts we get from the store, so we only return # relevant, non-sensitive information all_users = store.get("users") filtered_users = [{ "u_id": i.get("u_id"), "email": i.get("email"), "name_first": i.get("name_first"), "name_last": i.get("name_last"), "handle_str": i.get("handle_str"), "profile_img_url": i.get("profile_img_url") } for i in all_users] return {"users": filtered_users}
def channels_list(token): ''' Given a token, list all the channel the token is part of -------- token: string token of the requester -------- ValueError when: The token doesn't refer to a proper user -------- return: list of all the channels the token is part of ''' type_check_token(token) return {"channels": store.get("users", "u_id", auth_check_token(token))[0]["channels"]}
def standup_active(token, channel_id): ''' Returns if there is an active standup in the channel''' validate_user(token, channel_id, "channel") active = False end_time = None try: standup_data = store.get("standup", "channel_id", channel_id)[0] if standup_data["is_active"]: active = True end_time = standup_data["end"] # pylint: disable=broad-except except Exception: pass return {"is_active":active, "time_finish":convert_time(end_time)}
def user_profile_setemail(email, caller_u_id=None): # check email validity if not is_valid_email(email): raise ValueError(f"{email} is not a valid email") # check if user with email exists results = store.get("users", "email", email) if len(results) > 0: raise ValueError(f"User with email {email} already exists") # update email in store store.update("users", "email", email, "u_id", caller_u_id) # != 1: # raise Exception("Error updating email") return {}
def channels_listall(token): ''' Given a token, list all the channel -------- token: string token of the requester -------- ValueError when: The token doesn't refer to a proper user -------- return: list of all the channels ''' type_check_token(token) if store.n_elems('channel_data') == 0: return {'channels': []} return {'channels': store.get('channels')}
def user_profile_sethandle(handle_str, caller_u_id=None): # check handle length requirements if len(handle_str) < 3 or len(handle_str) > 20: raise ValueError("handle must be between 3 and 20 characters long") # check if user with handle exists results = store.get("users", "handle_str", handle_str) if len(results) > 0: raise ValueError(f"user with handle {handle_str} already exists") # update handle in store store.update("users", "handle_str", handle_str, "u_id", caller_u_id) # != 1: # raise Exception("error updating handle") return {}
def auth_passwordreset_reset(reset_code, new_password): """ Given a reset code for a user, set that user's new password to the password provided TODO stub """ # Check if reset code is valid res = store.get("resets", "reset_code", reset_code) if not res: raise ValueError("invalid reset code") reset = res[0] # Change password if len(new_password) < 6: raise ValueError("new password must be at least 6 characters") pwhash = hashlib.sha256(new_password.encode("utf-8")).hexdigest() store.update("users", "password", pwhash, "u_id", reset.get("u_id")) # Remove reset obj from store (after successful reset, just in case user screws up) store.remove("resets", "reset_code", reset_code) return {}
def standup_start(token, channel_id, standup_length=15*60): ''' Begin standup in channel, calls end standup after the time has expired''' validate_user(token, channel_id, "channel") except_message = "Already currently active standup" try: standup_data = store.get("standup", "channel_id", channel_id)[0] if standup_data["is_active"]: raise ValueError(except_message) store.update("standup", "is_active", True, "channel_id", channel_id) new_message_index = standup_data["standup_id"] store.update("standup", "standup_id", new_message_index+1, "channel_id", channel_id) summaries = standup_data["summaries"] summaries.append("Standup summary:\n") store.update("standup", "summaries", summaries, "channel_id", channel_id) except (ValueError) as ex: if ex.args[0] == except_message: raise ValueError(ex) store.insert("standup", {"channel_id": channel_id, "is_active": True,\ "standup_id": 0, "summaries": ["Standup summary:\n"]}) end = datetime.now()+timedelta(seconds=standup_length) my_timer(end, standup_end, (token, channel_id)) store.update("standup", "end", end, "channel_id", channel_id) return {"time_finish" : convert_time(end)}
def gen_request(): global MAIN_APP if MAIN_APP == None: # should never happen raise ValueError() pass mail = Mail(MAIN_APP) email = request.form.get("email") # Check if email exists results = store.get("users", "email", email) if len(results) == 0: raise ValueError("Email does not belong to any user") # this should get caught by our handler user = results[0] # Generate a reset code # it's highly unlikely we'll have a clash :)))) code = "".join([RESET_CHARS[random.randint(0, len(RESET_CHARS) - 1)] for i in range(16)]) auth_passwordreset_addcode(code, user.get("u_id")) # Send email msg = Message("Your slackr password reset", sender=SLACKR_ADDR, recipients=[email]) msg.body = f"""Hi {user.get("name_first")}, Someone requested a password reset for your email on slackr. Your reset code is: {code} If you did not request this, please contact our very existant support team. """ mail.send(msg) return {}