Example #1
0
def logout(**params):
    # logout should accept POST only (don't do logout via GET, to avoid pre-fetching issues in browsers)
    if request.method == "POST":
        # get the session token from the cookie
        session_token = request.cookies.get("my-web-app-session")

        # get the hash of the session token
        token_hash = hashlib.sha256(str.encode(session_token)).hexdigest()

        # delete the session token from the User object
        User.delete_session(user=params["user"],
                            token_hash_five_chars=token_hash[:5])

        # prepare the response
        response = make_response(redirect(url_for("public.main.index")))

        # on localhost don't make the cookie secure and http-only (but on production it should be)
        cookie_secure_httponly = False
        if not is_local():
            cookie_secure_httponly = True

        # set the session cookie to an empty value (similar to deleting the cookie)
        response.set_cookie(key="my-web-app-session",
                            value="",
                            secure=cookie_secure_httponly,
                            httponly=cookie_secure_httponly)
        return response
Example #2
0
def validate_magic_login_link(token, **params):
    if request.method == "GET":
        success, result = User.validate_magic_login_token(magic_token=token,
                                                          request=request)

        if success:
            # result is session token, store it in a cookie
            # prepare a response and then store the token in a cookie
            response = make_response(
                redirect(url_for("profile.main.sessions_list")))

            # on localhost don't make the cookie secure and http-only (but on production it should be)
            cookie_secure_httponly = False
            if not is_local():
                cookie_secure_httponly = True

            # store the token in a cookie
            response.set_cookie(key="my-web-app-session",
                                value=result,
                                secure=cookie_secure_httponly,
                                httponly=cookie_secure_httponly)
            return response
        else:
            # result is an error message
            return abort(403, description=result)
def send_email(recipient_email, email_template, email_params, email_subject, sender_email=None, sender_name=None,
               unsubscribe_group=None, attachment_content_b64=None, attachment_filename=None, attachment_filetype=None):
    if not sender_email:
        sender_email = os.environ.get("MY_APP_EMAIL")  # set this in app.yaml

    if not sender_name:
        sender_name = os.environ.get("MY_APP_NAME")  # set this in app.yaml

    # send web app URL data by default to every email template
    if is_local():
        email_params["app_root_url"] = "http://localhost:8080"
    else:
        email_params["app_root_url"] = os.environ.get("MY_APP_URL")  # set this in app.yaml

    email_params["my_app_name"] = os.environ.get("MY_APP_NAME")

    # render the email HTML body
    email_body = render_template_with_translations(email_template, **email_params)

    # params sent to the background task
    payload = {"recipient_email": recipient_email, "email_subject": email_subject, "sender_email": sender_email,
               "email_body": email_body, "unsubscribe_group": unsubscribe_group, "sender_name": sender_name,
               "attachment_content_b64": attachment_content_b64, "attachment_filename": attachment_filename,
               "attachment_filetype": attachment_filetype}

    run_background_task(relative_path=url_for("tasks.send_email_task.send_email_via_sendgrid"),
                        payload=payload, queue="email", project=os.environ.get("GOOGLE_CLOUD_PROJECT"),
                        location=os.environ.get("MY_GAE_REGION"))
Example #4
0
def reset_password_enter_email(**params):
    if request.method == "GET":
        return render_template_with_translations(
            "public/auth/reset_password_enter_email.html", **params)

    elif request.method == "POST":
        email_address = request.form.get("reset-password-email")

        locale = get_locale(
        )  # get the language that the user currently uses on the website
        success, message = User.password_reset_link_send(
            email_address=email_address, locale=locale)

        if success:
            # Delete the current session cookie (if it exists)
            response = make_response(
                render_template_with_translations(
                    "public/auth/reset_password_link_sent.html", **params))

            # on localhost don't make the cookie secure and http-only (but on production it should be)
            cookie_secure_httponly = False
            if not is_local():
                cookie_secure_httponly = True

            # set the session cookie value to an empty value which effectively "deletes" it
            response.set_cookie(key="my-web-app-session",
                                value="",
                                secure=cookie_secure_httponly,
                                httponly=cookie_secure_httponly)
            return response
        else:
            return abort(403, description=message)
Example #5
0
def login_via_password(**params):
    if request.method == "GET":
        return render_template_with_translations(
            "public/auth/login_password.html", **params)

    elif request.method == "POST":
        email_address = request.form.get("login-email")
        password = request.form.get("login-password")

        success, result = User.validate_password_login(
            email_address=email_address, password=password, request=request)

        if success:
            # result is session token, store it in a cookie
            # prepare a response and then store the token in a cookie
            response = make_response(
                redirect(url_for("profile.main.my_details")))

            # on localhost don't make the cookie secure and http-only (but on production it should be)
            cookie_secure_httponly = False
            if not is_local():
                cookie_secure_httponly = True

            # store the token in a cookie
            response.set_cookie(key="my-web-app-session",
                                value=result,
                                secure=cookie_secure_httponly,
                                httponly=cookie_secure_httponly)
            return response
        else:
            # result is an error message
            return abort(403, description=result)
Example #6
0
def change_email_link_validate(token, **params):
    # when user changes their own email address and confirms the change by clicking the link received via email, this
    # is the handler that does the token validation process
    if request.method == "GET":
        success, result = User.validate_change_email_token(token=token,
                                                           request=request)

        if success:
            # result is session token, store it in a cookie
            # prepare a response and then store the token in a cookie
            response = make_response(
                redirect(url_for("profile.main.my_details")))

            # on localhost don't make the cookie secure and http-only (but on production it should be)
            cookie_secure_httponly = False
            if not is_local():
                cookie_secure_httponly = True

            # store the token in a cookie
            response.set_cookie(key="my-web-app-session",
                                value=result,
                                secure=cookie_secure_httponly,
                                httponly=cookie_secure_httponly)
            return response
        else:
            # result is an error message
            return abort(403, description=result)
Example #7
0
 def _test_set_password_reset_token(cls, user, token):
     """FOR TESTING PURPOSES ONLY!"""
     with client.context():
         if is_local():
             user.password_reset_token_hash = hashlib.sha256(
                 str.encode(token)).hexdigest()
             user.password_reset_token_expired = datetime.datetime.now(
             ) + datetime.timedelta(hours=3)
             user.put()
Example #8
0
 def _test_mark_email_verified(cls, user):
     """
     FOR TESTING PURPOSES ONLY!
     :param user:
     :return:
     """
     with client.context():
         if is_local():
             user.email_address_verified = True
             user.put()
Example #9
0
 def _test_change_deleted_date(cls, user, new_date):
     """
     FOR TESTING PURPOSES ONLY!
     :param user:
     :param new_date:
     :return:
     """
     with client.context():
         if is_local():
             user.deleted_date = new_date
             user.put()
Example #10
0
    def fetch_suspended(cls,
                        email_address_verified=True,
                        limit=None,
                        cursor=None):
        with client.context():
            users, next_cursor, more = cls.query(
                cls.email_address_verified == email_address_verified,
                cls.suspended == True).fetch_page(limit, start_cursor=cursor)

            if is_local():
                # this fixes the pagination bug which returns more=True even if less users than limit or if next_cursor
                # is the same as the cursor. This happens on localhost only.
                if limit and len(users) < limit:
                    return users, None, False

            try:
                return users, next_cursor.urlsafe().decode(), more
            except AttributeError as e:  # if there's no next_cursor, an AttributeError will occur
                return users, None, False
Example #11
0
def run_background_task(relative_path,
                        payload,
                        project=None,
                        queue=None,
                        location=None):
    if is_local():
        if os.environ.get(
                "TESTING") != "yes":  # pytest has issues with running requests
            requests.post("http://localhost:8080{relative_path}".format(
                relative_path=relative_path),
                          data=json.dumps(payload).encode(),
                          headers={"Content-type": "application/octet-stream"})
    else:
        # production
        if not project:
            project = os.environ.get(
                "GOOGLE_CLOUD_PROJECT"
            )  # this is a default environment variable on GAE

        if not queue:
            queue = "default"

        if not location:
            location = os.environ.get("MY_GAE_REGION")

        # make sure you have Cloud Tasks API enabled via the Google Cloud Console
        client = tasks_v2.CloudTasksClient()

        # Construct the fully qualified queue name.
        parent = client.queue_path(project, location, queue)

        task = {
            'app_engine_http_request': {
                'http_method': 'POST',
                'relative_uri': relative_path,
                'body': json.dumps(payload).encode(),
            }
        }

        client.create_task(parent, task)
Example #12
0
def load_fake_data():
    # handler to load fake data for localhost development usage only
    if is_local():
        result, user_1, message = User.create(
            email_address="user_{}@my.webapp".format(1),
            admin=False,
            first_name="Jim",
            last_name="Jones")
        result, user_2, message = User.create(
            email_address="user_{}@my.webapp".format(2),
            admin=True,
            first_name="Betty",
            last_name="Beam")
        result, user_3, message = User.create(
            email_address="user_{}@my.webapp".format(3),
            admin=False,
            first_name="Cindy",
            last_name="Crawford")
        result, user_4, message = User.create(
            email_address="user_{}@my.webapp".format(4),
            admin=False,
            first_name="Damian",
            last_name="Dante")
        result, user_5, message = User.create(
            email_address="user_{}@my.webapp".format(5),
            admin=False,
            first_name="Erica",
            last_name="Enter")
        result, user_6, message = User.create(
            email_address="user_{}@my.webapp".format(6),
            admin=False,
            first_name="Fatima",
            last_name="Fowles")
        result, user_7, message = User.create(
            email_address="user_{}@my.webapp".format(7),
            admin=False,
            first_name="George",
            last_name="Garrett")
        result, user_8, message = User.create(
            email_address="user_{}@my.webapp".format(8),
            admin=True,
            first_name="Harriet",
            last_name="Ham")
        result, user_9, message = User.create(
            email_address="user_{}@my.webapp".format(9),
            admin=False,
            first_name="Ian",
            last_name="Ilich")
        result, user_10, message = User.create(
            email_address="user_{}@my.webapp".format(10),
            admin=False,
            first_name="Jane",
            last_name="James")
        result, user_11, message = User.create(
            email_address="user_{}@my.webapp".format(11),
            admin=False,
            first_name="Ken",
            last_name="Klingon")
        result, user_12, message = User.create(
            email_address="user_{}@my.webapp".format(12),
            admin=False,
            first_name="Lana",
            last_name="Lubbards")
        result, user_13, message = User.create(
            email_address="user_{}@my.webapp".format(13),
            admin=False,
            first_name="Matt",
            last_name="Morata")
        result, user_14, message = User.create(
            email_address="user_{}@my.webapp".format(14),
            admin=False,
            first_name="Nika",
            last_name="Norante")
        result, user_15, message = User.create(
            email_address="user_{}@my.webapp".format(15),
            admin=False,
            first_name="Omar",
            last_name="Orange")
        result, user_16, message = User.create(
            email_address="user_{}@my.webapp".format(15),
            admin=False,
            first_name="Peter",
            last_name="Pan")

        # mark most of emails as verified
        User._test_mark_email_verified(user=user_1)
        User._test_mark_email_verified(user=user_2)
        User._test_mark_email_verified(user=user_3)
        User._test_mark_email_verified(user=user_4)
        User._test_mark_email_verified(user=user_5)
        User._test_mark_email_verified(user=user_6)
        User._test_mark_email_verified(user=user_7)
        User._test_mark_email_verified(user=user_8)
        User._test_mark_email_verified(user=user_9)
        User._test_mark_email_verified(user=user_10)
        User._test_mark_email_verified(user=user_11)
        User._test_mark_email_verified(user=user_13)
        User._test_mark_email_verified(user=user_14)
        User._test_mark_email_verified(user=user_16)

    return redirect(url_for("public.main.index"))
Example #13
0
                 methods=["GET"])
app.add_url_rule(rule="/profile/session/delete", endpoint="profile.main.session_delete",
                 view_func=profile_main.session_delete, methods=["POST"])

# PROFILE auth
app.add_url_rule(rule="/logout", endpoint="profile.auth.logout", view_func=logout, methods=["POST"])


# ADMIN URLS
app.add_url_rule(rule="/admin/users", endpoint="admin.users.users_list", view_func=users.users_list,
                 methods=["GET", "POST"])
app.add_url_rule(rule="/admin/user/<user_id>", endpoint="admin.users.user_details", view_func=users.user_details,
                 methods=["GET"])

# CRON JOBS
app.add_url_rule(rule="/cron/remove-deleted-users", view_func=remove_deleted_users_cron, methods=["GET"])

# TASKS
app.add_url_rule(rule="/tasks/send-email", endpoint="tasks.send_email_task.send_email_via_sendgrid",
                 view_func=send_email_via_sendgrid, methods=["POST"])

# LOAD FAKE DATA (localhost only!)
if is_local():
    app.add_url_rule(rule="/load-fake-data", view_func=load_fake_data, methods=["GET"])

if __name__ == '__main__':
    if is_local():
        app.run(port=8080, host="localhost", debug=True)  # localhost
    else:
        app.run(debug=False)  # production
def send_email_via_sendgrid():
    """A background task that sends an email via SendGrid."""
    data = json.loads(request.get_data(as_text=True))

    recipient_email = data.get("recipient_email")
    sender_email = data.get("sender_email")
    sender_name = data.get("sender_name")
    email_subject = data.get("email_subject")
    email_body = data.get("email_body")
    unsubscribe_group = data.get("unsubscribe_group")
    attachment_content_b64 = data.get("attachment_content_b64")
    attachment_filename = data.get("attachment_filename")
    attachment_filetype = data.get("attachment_filetype")

    if is_local():
        # localhost (not really sending the email)
        logging.warning(
            "SEND EMAIL: Not really sending email because we're on localhost.")
        logging.warning("Recipient: {}".format(recipient_email))
        logging.warning("Sender: {0}, {1}".format(sender_name, sender_email))
        logging.warning("Subject: {}".format(email_subject))
        logging.warning("Body: {}".format(email_body))

        return "{sender_email} {email_subject}".format(
            sender_email=sender_email, email_subject=email_subject)
    else:
        # production (sending the email via SendGrid)
        if request.headers.get("X-AppEngine-QueueName"):
            # If the request has this header (X-AppEngine-QueueName), then it really came from Google Cloud Tasks.
            # Third-party requests that contain headers started with X are stripped of these headers once they hit GAE
            # servers. That's why no one can fake these headers.

            # SendGrid setup
            sg = SendGridAPIClient(api_key=AppSettings.get().sendgrid_api_key)

            # Set up email message
            email_message = Mail(from_email=mail.Email(email=sender_email,
                                                       name=sender_name),
                                 to_emails=recipient_email,
                                 subject=email_subject,
                                 html_content=email_body)

            if attachment_content_b64 and attachment_content_b64 is not None and attachment_content_b64 != "":
                attachment = Attachment()
                attachment.content = attachment_content_b64
                attachment.type = "text/{}".format(attachment_filetype)
                attachment.filename = attachment_filename
                attachment.disposition = "attachment"

                email_message.add_attachment(attachment)

            # Unsubscribe group (ASM)
            if unsubscribe_group:
                try:
                    email_message.asm(Asm(group_id=int(unsubscribe_group)))
                except Exception as e:
                    pass

            try:
                response = sg.send(email_message)
                logging.info(response.status_code)
                logging.info(response.body)
                logging.info(response.headers)
            except Exception as e:
                logging.error(str(e))

        return "true"