def get(self, session_token, session):
        """
        Gets an AdvitoUser by session_token.
        :param session_token: Token given by user to query by.
        :param session: Database session. Not to be confused with session_token.
        :return: AdvitoUser instance
        """

        # Gets id of user with given session token
        user_session = session \
            .query(AdvitoUserSession) \
            .filter_by(session_token=session_token) \
            .first()
        if user_session is None:
            raise NotFoundError("Could not find session token.")

        # Gets user using id found
        user_id = user_session.advito_user_id
        user = session \
            .query(AdvitoUser) \
            .filter_by(id=user_id) \
            .first()
        if user is None:
            raise NotFoundError(
                "Session token was found, but no associated user was found. This should not happen."
            )

        # Done
        return user
    def reset_password_end(self, access_token, new_password, session):
        """
        Finishes process of resetting the user's password
        :param access_token: Access token sent to the user.
        :param new_password: New password to set for user
        """

        # Gets user with email
        user_and_token = session \
            .query(AccessToken, AdvitoUser) \
            .join(AdvitoUser) \
            .filter(AccessToken.token == access_token) \
            .first()
        if user_and_token is None:
            raise NotFoundError("Access token not found")

        # Gets token and validates it
        token = user_and_token[0]
        if not token.is_active or datetime.now() > token.token_expiration:
            raise TokenExpirationError("Access token expired")

        # Gets user, hashes/salts new password and stores it alongside the salt that was used
        user = user_and_token[1]
        salt_and_hash = salt_hash(new_password)
        user.pwd = salt_and_hash[0]
        user.user_salt = salt_and_hash[1]

        # Expires token
        token.is_active = False
    def update_any(self, user, session):
        """
        Updates an AdvitoUser in the database.
        :param user: AdvitoUser object to update in the db. Assumes ID is present.
        :param session: SQLAlchemy session used for db operations.
        """

        user_serialized = {
            "username": user.username,
            "email": user.username,
            "name_last": user.name_last,
            "name_first": user.name_first,
            "phone": user.phone,
            "address": user.address,
            "profile_picture_path": user.profile_picture_path,
            "is_enabled": user.is_enabled
        }

        # Updates user in db
        row_count = session \
            .query(AdvitoUser) \
            .filter(AdvitoUser.id == user.id) \
            .update(user_serialized)

        # Validates that a change occurred
        if row_count == 0:
            raise NotFoundError("Could not find user with specified id " +
                                str(user.id))
    def get_by_username(self, username, session):
        """
        Gets an AdvitoUser by username
        :param username: Username of the user to get
        :param session: SQLAlchemy session used for db operations.
        :return: AdvitoUser instance.
        """

        user = session \
            .query(AdvitoUser) \
            .filter_by(username=username) \
            .first()
        if user is None:
            raise NotFoundError("Could not find user '{}'".format(username))
        return user
    def get_authentication_info(self, session_token, session):
        """
        Gets an AdvitoUser.
        :param session_token: Token given by user to query by.
        :param session: Database session. Not to be confused with session_token.
        """

        # Gets token from db
        db_token = session \
            .query(AdvitoUserSession) \
            .filter(AdvitoUserSession.session_end == None) \
            .filter(AdvitoUserSession.session_token == session_token) \
            .first()

        # Checks that there was a session that was not expired that matches given token
        if db_token is None:
            raise InvalidSessionError("No session found")
        if datetime.now() >= db_token.session_expiration:
            raise ExpiredSessionError("Session expired")

        # Gets users alongside their roles
        user_role_pairs = session \
            .query(AdvitoApplicationRole, AdvitoUser) \
            .join(AdvitoUserRoleLink) \
            .join(AdvitoUser) \
            .filter(AdvitoUser.id == db_token.advito_user_id) \
            .all()
        if len(user_role_pairs) == 0:
            raise NotFoundError("User not found for session token")

        # Formats user info
        user = None
        roles = []
        for pair in user_role_pairs:
            if user is None:
                user = pair[1]
            roles.append(pair[0])

        # Returns as a tuple
        return (user, roles)
    def reset_password_start(self, session_token, session):
        """
        Begins process of resetting the user's password
        :param email: Email of user to reset password for.
        :return: Access token that should be sent in email.
        """

        # Gets user info
        user = session \
            .query(AdvitoUser) \
            .join(AdvitoUserSession) \
            .filter(AdvitoUserSession.session_token == session_token) \
            .first()
        if user is None:
            raise NotFoundError("User/token not found.")

        # Gets existing access token associated with user. Normally, it won't exist
        old_access_token = session \
            .query(AccessToken) \
            .filter(AccessToken.advito_user_id == user.id) \
            .filter(AccessToken.is_active == True) \
            .first()
        if old_access_token is not None:
            old_access_token.is_active = False

        # Creates access token
        token_str = base64.b64encode(
            secrets.token_bytes(16)).decode(encoding='UTF-8')
        expiration = datetime.now() + timedelta(
            hours=self.reset_password_duration_hours)
        token = AccessToken(advito_user_id=user.id,
                            token_type="FLYING/NORMAL",
                            token=token_str,
                            token_expiration=expiration)

        # Inserts into db and returns tuple of token and email.
        session.add(token)
        return (token_str, user.email)