Example #1
0
def channels_join(token, channel_id):
    """
        Given a channel_id of a channel that the authorised user can join, adds them to that channel
        Parameters:  
            token      (str)
            channel_id (int)
        Returns:
            {}
    """
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description="Target channel is not a valid channel")
    # Check whether channel is private or not. Raise AccessError if it is
    if not selected_channel.visibility == "public":
        raise AccessError(description="Target channel isn't public")

    new_membership = MemberOf(
        user=user,
        channel=selected_channel,
        is_owner=False
    )
    db.session.add(new_membership)
    db.session.commit()
    return {}
Example #2
0
def channels_leave(token, channel_id):
    """
        Removes the caller from the specified channel.
        Parameters:  
            token      (str)
            channel_id (str)
        Returns: 
            {}
    """
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    channels_list = Channel.query.all()
    selected_channel = select_channel(channel_id)
    # Check if Channel ID is not a valid channel
    if not selected_channel:
        raise InputError(description="Channel ID is not a valid channel")
    # Check user if the user is not currently a member of the selected channel
    if not is_user_member(user, selected_channel):
        raise AccessError(description="You are not a member of this channel")

    membership = MemberOf.query.filter_by(user_id=user.id, channel_id=selected_channel.id).first()
    db.session.delete(membership)
    db.session.commit()
    # If the user attempting to leave is the owner... Pass down ownership to someone else? Or delete channel
    # TODO:
    # If there is no members left in channel, delete channel
    # TODO:
    return {}
Example #3
0
def channels_messages(token, channel_id, start, limit=50):
    """
        Given a channel that the user is a member of, returns up to a specified maximum
        number of messages from a starting index. Messages are ordered in ascending
        recency, so the message at index 0 is the most recent.
        Parameters:
            token      (str)
            channel_id (int)
            start      (int)
            limit      (int)
        Returns:
            { messages, exhausted }
        Where:
            messages: list of message dictionary: { message_id, user_id, message, time_created, is_author }
            exhausted: bool indicating whether the there any more messages left to fetch 
    """
    # check parameters are all valid and raise exception if they aren't
    # add user_id, associated first name and last name into channel_id dictionary (or storage)
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description="Channel ID is not a valid channel")
    if is_user_member(user, selected_channel) is False:
        raise AccessError(description="You are not a member of this channel")

    # Loop through 50 message dictionaries of list starting from start index
    messsages_list = selected_channel.messages_sent
    messsages_list.sort(key=(lambda x: x.time_created))
    
    payload = {
        "messages": [],
        "exhausted": True
    }
    
    if not messsages_list:
        # printColour("Results: {}".format(payload), colour="blue")
        return payload
    if start >= len(messsages_list):
        raise InputError(description="Starting index is greater than or equal to the total number of messages in the channel")
    if not (start + limit >= len(messsages_list)):
        messages["exhausted"] = False
    
    for i, message in enumerate(messsages_list[start :], start=1):
        payload["messages"].append({
            "message_id": message.id,
            "user_id": message.user_id,
            "message": message.message,
            "time_created": message.time_created.timestamp(),
            "is_author": message.user_id == user.id
        })
        if i == limit:
            break
    # printColour("Results: {}".format(payload), colour="blue")
    return payload
Example #4
0
def channels_details(token, channel_id):
    """
        Given a Channel with ID channel_id that the authorised user is
        part of, provide basic details about the channel
        Parameters:
            token       (str)
            channel_id  (channel_id)
        Returns:
            { name, description, visibility, channel_img_url, owner_members, all_members }    (dict)
        Where:
            owner_members: [{ user_id, username, email, profile_img_url }, ...]  (list of user objects)
            all_members: [{ user_id, username, email, profile_img_url }, ...]    (list of user objects)
    """
    verify_token(token)
    user = get_user_from_token(token)
    if not user:
        raise AccessError(description="Invalid Token")
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description=f"{channel_id} doesn't point to a valid channel")
    # Raise exception when the user is not a member of the channel with the given channel_id
    if not is_user_member(user, selected_channel):
        raise AccessError(description="You are not a member of this channel")

    channel_owners = []
    channel_members = []
    # Joining User with MemberOf, then filtering for users associated with the selected channel 
    # TODO: Possible optimisation -> swap the ordering of the filtering
    memberships = db.session.query(User, MemberOf, Channel).outerjoin(MemberOf, MemberOf.user_id==User.id).outerjoin(Channel, Channel.id==MemberOf.channel_id).filter_by(id=channel_id).all()    
    for each_membership in memberships:
        curr_member = each_membership[0]
        member_data = {
            "user_id": curr_member.id,
            "username": curr_member.username,
            "profile_img_url": curr_member.bio.profile_img_url,
            "email": curr_member.email
        }
        channel_members.append(member_data)
        if each_membership[1].is_owner:
            channel_owners.append(member_data)
    
    details = {
        "name": selected_channel.name,
        "description": selected_channel.description,
        "visibility": selected_channel.visibility,
        "channel_img_url": selected_channel.channel_img_url,
        "channel_cover_img_url": selected_channel.channel_cover_img_url,
        "owner_members": channel_owners,
        "all_members": channel_members
    }
    # printColour("Results: {}".format(details), colour="blue")
    return details
Example #5
0
def message_send(token, channel_id, message):
    """
        Sends a message to the selected channel.
        Parameters:
            token      (str)
            channel_id (int)
            message    (str)
        Returns: 
            { message_id }
    """
    verify_token(token)
    if not message:
        raise InputError("Message can't be empty")
    if len(message) > 1000:
        raise InputError("Message is over 1000 characters")
    user = get_user_from_token(token)
    selected_channel = select_channel(channel_id)
    # Raise error if the user is not a part of the channel they are trying to message in
    if not is_user_member(user, selected_channel):
        raise AccessError(
            description="User is not a member of channel with channel_id")

    sent_message = Message(channel=selected_channel,
                           user=user,
                           message=message)
    db.session.add(sent_message)
    db.session.commit()
    return {"message_id": sent_message.id}
Example #6
0
def channels_invite(token, channel_id, user_id):
    """
        Invites a user, with the given user_id, to join the channel with ID channel_id.
        Once invited, the user is added to the channel immediately.
        Parameters:
            token       (str)
            channel_id  (int)
            user_id     (int)
        Returns: 
            {}          (dict)
    """
    printColour("RECEIVED CHANNEL ID: {}".format(channel_id), colour="blue")
    printColour("RECEIVED USER ID: {}".format(user_id), colour="blue")
    verify_token(token)
    calling_user = get_user_from_token(token)
    if not calling_user:
        raise AccessError(description="Invalid Token")
    
    # If channel_id is invalid, raise input error
    selected_channel = select_channel(channel_id)
    if not selected_channel:
        raise InputError(description="Target channel is not a valid channel that the user is part of.")
    # Check the authorised user is not already a member of the channel
    if not is_user_member(calling_user, selected_channel):
        raise AccessError(description="You are not a member of this channel")

    invited_user = User.query.filter_by(id=user_id).first() 
    # Check that the user exists (ie. the user_id is valid)
    if not invited_user:
        raise InputError(description="Target user is not a valid user")
    # Check if invited_user is already a member
    if is_user_member(invited_user, selected_channel):
        raise InputError(description="{} is already a member".format(invited_user.username))
    # Granting membership
    new_membership = MemberOf(
        user=invited_user,
        channel=selected_channel,
        is_owner=False
    )
    printColour("Trying to add user {} to channel {}".format(new_membership.user.id, new_membership.channel.id), colour="blue")
    
    db.session.add(new_membership)
    db.session.commit()
    return {}
Example #7
0
def auth_logout(token):
    """
        Given a valid token, verifies it and logs out the associated user.
        Parameters:
            token  (str)
        Returns: {
            is_success(True/False)  bool
        }
    """
    if verify_token(token):
        return {"is_success": True}
    else:
        raise AccessError(description="Logout failed. Token is invalid")
Example #8
0
def verify_token(token):
    """
        Given a token, checks the database and returns true if the token exists.
        If the token doesn't exist, then return false.
        Unfortunately, we need to check ourselves whether that user_id associated with
        the token has access rights, etc.
        Parameters:
            token   str
        Returns:
            True/False
    """
    try:
        jwt.decode(token, os.getenv("SECRET_MESSAGE"), algorithms=["HS256"])
        return True
    except jwt.DecodeError:
        # Token is invalid!
        raise AccessError(description="Token is invalid! {}".format(token))
Example #9
0
def message_remove(token, message_id):
    """
        Removes a message from the list of messages
        Returns: { old_message }  
    """
    verify_token(token)
    message_obj = Message.query.filter_by(id=message_id).first()
    if not message_obj:
        raise InputError("Message doesn't exist")
    calling_user = get_user_from_token(token)
    if calling_user.id != message_obj.user_id:
        raise AccessError("You can't modify someone else's message")

    # Removes message and saves changes
    db.session.delete(message_obj)
    db.session.commit()
    return {"old_message": message_obj.message}
Example #10
0
def get_user_from_token(token):
    """
        Given a token, checks if it exists. If it does, return the user data structure
        associated with the token. If not, return None
        Parameters:
            token       str
        Returns: {
            user_id             int
            username            str
            email               str
            password(hashed)    str
            permission_id       int
        }
    """
    decoded_token = jwt.decode(token, os.getenv("SECRET_MESSAGE"), algorithms=["HS256"])
    if not decoded_token:
        raise AccessError(description="Invalid token supplied")
    return User.query.filter_by(id=decoded_token["user_id"]).first()
Example #11
0
def message_edit(token, message_id, message):
    """
        Edits an existing message. Deletes it if the new message is empty
    """
    verify_token(token)
    if len(message) > 1000:
        raise InputError("Message is over 1000 characters")
    if not message:
        raise InputError("New message can't be empty")
    user = get_user_from_token(token)
    message_obj = Message.query.filter_by(id=message_id).first()
    is_user_owner = user_is_owner(token, message_obj.channel_id)
    is_user_admin = user_is_admin(token)
    if message_obj.user_id == user.id or is_user_admin or is_user_owner:
        printColour("Editing message from '{}' to '{}'".format(
            message_obj.message, message),
                    colour="red_1")
        message_obj = Message.query.filter_by(id=message_id).first()
        message_obj.message = message
        db.session.commit()
    else:
        printColour("Not permitted to edit message", colour="red_1")
        raise AccessError("You are not authorised to edit this message")
    return {}