How to configure Flask Mail properly - python

I am trying to follow this tutorial. When I try to submit the contact form, which should trigger the email, I get an internal server error. The error log says:
RuntimeError: The curent application was not configured with Flask-Mail
The instructions say to use from flask.ext.mail to import but I've seen that it might be from flask_mail now. I've also tried changing the mail port from 465 to 587. Neither of these changes have been fixed the problem. My most up to date code is:
from flask import Flask, render_template, request, flash
from forms import ContactForm
from flask_mail import Mail, Message
mail = Mail()
app = Flask(__name__)
app.secret_key = 'development key'
app.config["MAIL_SERVER"] = "smtp.gmail.com"
app.config["MAIL_PORT"] = 587
app.config["MAIL_USE_SSL"] = True
app.config["MAIL_USERNAME"] = 'contact_email#gmail.com' ## CHANGE THIS
app.config["MAIL_PASSWORD"] = 'password'
mail.init_app(app)
app = Flask(__name__)
app.secret_key = 'Oh Wow This Is A Super Secret Development Key'
#app.route('/')
def home():
return render_template('home.html')
#app.route('/about')
def about():
return render_template('about.html')
#app.route('/contact', methods=['GET', 'POST'])
def contact():
form = ContactForm()
if request.method == 'POST':
if form.validate() == False:
flash('All fields are required.')
return render_template('contact.html', form=form)
else:
msg = Message(form.subject.data, sender='contact_email#gmail.com', recipients=['recipient#gmail.com'])
msg.body = """
From: %s <%s>
%s
""" % (form.name.data, form.email.data, form.message.data)
mail.send(msg)
return render_template('contact.html', success=True)
elif request.method == 'GET':
return render_template('contact.html', form=form)
if __name__ == '__main__':
app.run(debug=True)

You created a second app (presumably by accident) after configuring the initial app. Right now the "first" app is configured and has the extension registered, but the "second" app is used to register the routes and call .run().
Remove the line after mail.init_app(app), the second app = Flask(__name__) that creates another app.

Related

Flask - learning how to structure my Flask application and failed to display any of my routes or views

I am not sure if its a circular import issue but havent got an error indicating so. How do i make my app display the routes!!!!!
Here is my application structure
My init.py file is where i have created my FLask application instance. I have tried to import views into it but it refuses to work
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = 'e14c80be9484a60c179678f7a0c5d6f1'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Here is my views.py file, i imported the app instance in it to get my routes working but in vain
from flask import render_template, url_for, flash, redirect
from crud_app import app
from crud_app.forms import RegistrationForm, LoginForm
#app.route('/')
#app.route('/index')
def index():
return render_template('index.html')
#app.route('/home')
def home():
return render_template('home.html', title='HomePage')
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
if form.email.data == 'andy#gmail.com' and form.password.data == 'password':
flash('Your LogIn was a Success')
return redirect(url_for('home'))
else:
flash('Login Unsuccessful, Please check your Email and Password ')
return render_template('login.html', title='loginPage', form=form)
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
flash(f'Welcome { form.username.data} ,your account has been created!')
return redirect(url_for('home'))
return render_template('register.html', title='RegisterPage', form=form)

Flask - MongoDB - Protect routes

I am new to Flask and I built a basic web app using Flask. This app will be used by a single user. The user must be connected in order to access any routes. What would be the easiest and most secure way to create new routes on my app and make sure that the user is logged in before they are able to access the page?
I added this route and I am able to access the page even if I am logged in.
#login_required
#app.route('/secret')
def secret():
return "hello world"
app.py
from flask import Flask, render_template, url_for, request, session, redirect
from flask_pymongo import PyMongo
import bcrypt
app = Flask(__name__)
app.config['MONGO_DBNAME'] = xxx'
app.config['MONGO_URI'] = 'xxxx'
mongo = PyMongo(app)
#app.route('/')
def index():
if 'username' in session:
return 'You are logged in as ' + session['username']
return render_template('index.html')
#app.route('/login', methods=['POST'])
def login():
users = mongo.db.users
login_user = users.find_one({'name' : request.form['username']})
if login_user:
if bcrypt.hashpw(request.form['pass'].encode('utf-8'), login_user['password']) == login_user['password']:
session['username'] = request.form['username']
return redirect(url_for('index'))
return 'Invalid username/password combination'
#app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'POST':
users = mongo.db.users
existing_user = users.find_one({'name' : request.form['username']})
if existing_user is None:
hashpass = bcrypt.hashpw(request.form['pass'].encode('utf-8'), bcrypt.gensalt())
users.insert({'name' : request.form['username'], 'password' : hashpass})
session['username'] = request.form['username']
return redirect(url_for('index'))
return 'That username already exists!'
return render_template('register.html')
#app.route('/logout')
def logout():
session.pop('username', None)
return render_template('index.html')
if __name__ == '__main__':
app.secret_key = 'mysecret'
app.run(debug=True, port='3500')
You can use the flask-login module for this. It handles user authentication, allowing routes to be protected with the #login_required decorator.
I recommend reading the documentation, as you need to provide a class with certain properties to represent your users and you need to implement a method that returns a user based on a given identifier.
When a user is logged in, the templates can access the current_user variable and it's properties (as defined by you; name, email etc).
Here is a simple example of the python code (I have not included the static files or templates).
from flask import Flask, render_template, request, url_for, request, redirect, abort
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from flask_pymongo import PyMongo
from flask_bcrypt import Bcrypt
from urllib.parse import urlparse, urljoin
import sys
# Import User classes
from user import User, Anonymous
# Create app
app = Flask(__name__)
# Configuration
app.config['MONGO_DBNAME'] = 'database_name'
app.config['MONGO_URI'] = 'mongo_database_uri'
app.secret_key = 'change this before production'
# Create Pymongo
mongo = PyMongo(app)
# Create Bcrypt
bc = Bcrypt(app)
# Create login manager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.anonymous_user = Anonymous
login_manager.login_view = "login"
# ROUTES
#app.route('/')
def index():
return render_template('index.html')
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
if current_user.is_authenticated:
return redirect(url_for('/index'))
return render_template('login.html')
users = mongo.db.users
user_data = users.find_one({'email': request.form['email']}, {'_id' : 0 })
if user_data:
if bc.check_password_hash(user_data['password'], request.form['pass']):
user = User(user_data['title'], user_data['first_name'], user_data['last_name'], user_data['email'], user_data['password'], user_data['id'])
login_user(user)
#Check for next argument (direct user to protected page they wanted)
next = request.args.get('next')
if not is_safe_url(next):
return abort(400)
return redirect(next or url_for('profile'))
return 'Invalid email or password'
#app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'POST':
users = mongo.db.users
existing_user = users.find_one({'email' : request.form['email']}, {'_id' : 0 })
if existing_user is None:
logout_user()
hashpass = bc.generate_password_hash(request.form['pass']).decode('utf-8')
new_user = User(request.form['title'], request.form['first_name'], request.form['last_name'], request.form['email'], hashpass)
login_user(new_user)
users.insert_one(new_user.dict())
return redirect(url_for('profile'))
return 'That email already exists!'
return render_template('register.html')
#app.route('/profile', methods=['GET'])
#login_required
def profile():
return render_template('profile.html')
#app.route('/list', methods=['GET'])
#login_required
def list():
#if current_user.id:
log(current_user.is_authenticated)
all_users = mongo.db.users.find({}, {'_id' : 0 })
return render_template('list.html', users = all_users)
#app.route('/logout', methods=['GET'])
#login_required
def logout():
logout_user()
return redirect(url_for('index'))
# Login Manager requirements
#login_manager.user_loader
def load_user(userid):
# Return user object or none
users = mongo.db.users
user = users.find_one({'id': userid}, {'_id' : 0 })
if user:
log(user)
return User(user['title'], user['first_name'], user['last_name'], user['email'], user['password'], user['id'])
return None
# Safe URL
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and \
ref_url.netloc == test_url.netloc
# Run app
if __name__ == '__main__':
app.run(debug=True)
I have a functioning basic template app using flask, flask-login and flask-pymongo at https://github.com/chriswilson1982/flask-mongo-app

Python3 bcrypt, pymongo, flask ValueError: Invalid salt

I am trying to create a website using flask, bcrypt and pymongo that allows you to register an account and login. Currently register is working but login is not. When I click login I get this error.
My code:
from flask import Flask, render_template, url_for, request, session, redirect
from flask_pymongo import PyMongo
import bcrypt
app = Flask(__name__)
app.config['MONGO_DBNAME'] = 'websitetest'
app.config['MONGO_URI'] = 'mongodb://localhost:27017'
mongo = PyMongo(app)
#app.route('/')
def index():
if 'username' in session:
return('You are logged in as ' + session['username'])
return render_template('index.html')
#app.route('/login', methods=['POST'])
def login():
users = mongo.db.users
login_user = users.find_one({'name': request.form['username']})
if login_user:
if bcrypt.hashpw(bytes(request.form['pass'], 'utf-8'), bytes(request.form['pass'], 'utf-8')) == bytes(request.form['pass'], 'utf-8'):
session['username'] = request.form['username']
return redirect(url_for('index'))
return 'Invalid username/password combination.'
#app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'POST':
users = mongo.db.users
existing_user = users.find_one({'name': request.form['username']})
if existing_user is None:
hashpass = bcrypt.hashpw(request.form['pass'].encode('utf-8'), bcrypt.gensalt())
users.insert({'name': request.form['username'], 'password': hashpass})
session['username'] = request.form['username']
return redirect(url_for('index'))
return('That username already exists!')
return render_template('register.html')
if __name__ == '__main__':
app.secret_key = 'mysecret'
app.run(debug=True)
Any help would be greatly appreciated. Thanks!
This line is not following the API description of bcrypt:
if bcrypt.hashpw(bytes(request.form['pass'], 'utf-8'), bytes(request.form['pass'], 'utf-8')) == bytes(request.form['pass'], 'utf-8'):
The docs say to compare like so:
if bcrypt.hashpw(password, hashed) == hashed:
hashed in your environment is represented by this line in your code:
hashpass = bcrypt.hashpw(request.form['pass'].encode('utf-8'), bcrypt.gensalt())
So you need to retrieve hashpass in some way so your code compares thusly:
if bcrypt.hashpw(bytes(request.form['pass'], 'utf-8'), hashpass) == hashpass:
Note that if you are using a more recent version (3x) of bcrypt, you should use:
bcrypt.checkpw(password, hashed):

Celery Flask-Mail async fails. Sends without async. SMTPSenderRefused

I've just started working with Celery on my latest project with work; I'm having a bit of trouble with executing tasks asynchronously.
All the code is taken from Miguel Grinbergs 'Using Celery with Flask'
When sending the mail without executing a task, it sends perfectly fine. Though when I attempt to send the mail delayed, it fails out giving me an error as follows.
smtplib.SMTPSenderRefused: (530, b'5.5.1 Authentication Required. Learn more at\n5.5.1 https://support.google.com/mail/answer/14257 h19sm960819igq.6 - gsmtp', 'email-removed#gmail.com')
Here's the code I'm using, in my Flask app.
import os
import time
from flask import Flask, request, render_template, session, flash, redirect, url_for, jsonify
from flask.ext.mail import Mail, Message
from celery import Celery
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-duper-secret'
# Celery Configuration
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
# Configuration for Flask-Mail
app.config['MAIL_SERVER'] = "smtp.gmail.com"
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USERNAME'] = 'email-removed#gmail.com'
app.config['MAIL_PASSWORD'] = 'password-removed'
app.config['MAIL_DEFAULT_SENDER'] = 'email-removed#gmail.com'
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
mail = Mail(app)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html', email=session.get('email', ''))
email = request.form['email']
session['email'] = email
msg = Message("Hello from Flask", recipients=[email])
msg.body = "This is a test message from the flask application!"
if request.form['submit'] == "Send":
send_async_email(msg)
flash('Sending email to {0}'.format(email))
else:
send_async_email.apply_async(args=[msg], countdown=20)
flash('An email to {0} will be sent in a minute.'.format(email))
return redirect(url_for('index'))
#celery.task()
def send_async_email(msg):
with app.app_context():
mail.send(msg)
if __name__ == "__main__":
app.run(debug=True)
I'd appreciate some insight, and perhaps an explanation on how to make this work, and why it isn't working.
I've also looked upon other threads here, and turned on insecure-application access for my google accounts, along with nulling the captcha as suggested in the errors returned URL.

Flask-Login and Heroku issues

I have a sample web application (flask with flask-login running on heroku) at this URL: http://twittaclone.herokuapp.com.
When I run it on my localhost the login functionality works fine. When I push to heroku it freaks out and does not allow users to login (it does allow user registration). Database modifications are being made.
Why would flask login not work on heroku?
app = Flask(__name__)
mysql = MySQL()
app.config['MYSQL_DATABASE_HOST'] = os.environ['MYSQL_DATABASE_HOST'] if 'MYSQL_DATABASE_HOST' in os.environ else config.MYSQL_DATABASE_HOST
app.config['MYSQL_DATABASE_PORT'] = os.environ['MYSQL_DATABASE_PORT'] if 'MYSQL_DATABASE_PORT' in os.environ else config.MYSQL_DATABASE_PORT
app.config['MYSQL_DATABASE_USER'] = os.environ['MYSQL_DATABASE_USER'] if 'MYSQL_DATABASE_USER' in os.environ else config.MYSQL_DATABASE_USER
app.config['MYSQL_DATABASE_PASSWORD'] = os.environ['MYSQL_DATABASE_PASSWORD'] if 'MYSQL_DATABASE_PASSWORD' in os.environ else config.MYSQL_DATABASE_PASSWORD
app.config['MYSQL_DATABASE_DB'] = os.environ['MYSQL_DATABASE_DB'] if 'MYSQL_DATABASE_DB' in os.environ else config.MYSQL_DATABASE_DB
mysql.init_app(app)
if 'SECRET_KEY' in os.environ: app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
else: app.config['SECRET_KEY'] = os.urandom(24)
def connect_db(): return mysql.connect()
###
# Routing for your application.
###
login_manager = LoginManager()
login_manager.login_view = "login"
#login_manager.user_loader
def load_user(username):
g.db = connect_db()
return get_user(username)
login_manager.init_app(app)
#app.route('/')
def home(): return render_template('home.html')
def connect_db(): return mysql.connect()
#app.before_request
def before_request():
g.user = current_user
g.db = connect_db()
#app.teardown_request
def tear_down(exception):
g.db.close()
#app.route('/main/')
#login_required
def main():
print("in main")
tweets, user = get_main()
follower_count, followee_count = get_follower_info(g.user.username)
return render_template('main.html', user=user, tweets=tweets, followercount = follower_count, followeecount = followee_count)
#app.route('/login/', methods=['GET', 'POST'])
def login():
"""Logs the user in."""
if request.method == 'GET':
if current_user is user_logged_in: logout_user()
error = None
if request.method == 'POST':
user = get_user(request.form['username'])
if user is None:
error = 'Invalid username'
elif not check_password_hash(user.password, request.form['password']):
error = 'Invalid password'
else:
flash('You were logged in')
login_user(user)
return redirect(url_for('main'))
return render_template('login.html', error=error)
Many years later but this solves your issue: Heroku gunicorn flask login is not working properly
Updated Procfile:
web: gunicorn app:app --preload
Documentation on preloading for gunicorn: https://docs.gunicorn.org/en/stable/settings.html
I'm pretty sure that Heroku uses PostgreSQL. Try referring to their documents https://devcenter.heroku.com/articles/heroku-postgresql

Categories