I'm using flask. Here's my login function:
#app.route("/", methods=["GET", "POST"])
def login():
if request.method == "POST" and "imei" in request.form and "password" in request.form:
imei = request.form["imei"]
password = request.form["password"]
worked, name = checkDB(imei, password)
if worked:
uid = hashlib.sha256(imei.encode('utf-8')).hexdigest()
u = User(imei, name, password, uid)
USERS[uid] = u
login_user(u)
#return request.args.get("next")
return redirect(url_for("analyzer")) #THIS DOESENT WORK
else:
return redirect(url_for("login") + "?failure=true")
elif request.method == "GET" and request.args.get("failure"):
return render_template("auth.html", fail="true")
else:
return render_template("auth.html", fail="false")
When the line attempts to trigger (the one labeled THIS DOESENT WORK), it redirects to: /?next=%2Fwebike%2Fanalyzer.
The analyzer is simple:
#app.route('/analyzer', methods=['GET'])
#login_required
def analyzer():
return render_template('index.html')
What am I doing wrong? If the username and password are wrong, everything works as intended.
The login didn't work properly. You successfully redirect to /analyzer, but that redirects back to your login method because of the #login_required decorator, appending the analyzer method url as the next parameter.
u = User(imei, name, password, uid)
Here, you're not performing a lookup, you're making a new User object. Instead of making a new object, you'll need to get the User from your database and pass that to the login_user method. Documentation for the method here.
Related
I'm trying to make a redirection after user login on flask.
#app.route('/login', methods=['GET', 'POST'])
def login():
if g.user:
return redirect(url_for('index'))
if request.method == 'POST':
user = request.form['Editbox1']
passwd = request.form['Editbox2']
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
return 'Invalid credentials'
else:
session['user_id'] = request.form['Editbox1']
return redirect(url_for('entry_page'), session['user_id'])
return render_template('index.html')
This one can't autoredirect, browser just show message:
You should be redirected automatically to target URL: /entry. If not click the link.
Clicking manually works fine.
If i don't pass variable 'session' autoredirecting works. How i can fix this problem?
You have to put the parameters for the url_for() directly after the url function you are referring to, and give the parameter a name that you use in the next function call, like this:
return redirect(url_for('entry_page', user_uid=session['user_uid']))
So your complete code would look like this:
#app.route('/login', methods=['GET', 'POST'])
def login():
if g.user:
return redirect(url_for('index'))
if request.method == 'POST':
user = request.form['Editbox1']
passwd = request.form['Editbox2']
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
return 'Invalid credentials'
else:
session['user_id'] = request.form['Editbox1']
return redirect(url_for('entry_page', user_uid=session['user_id']))
return render_template('index.html')
I'm trying to find a way to update a dictionary (global) from within a function in a flask app.
I want to store information about users that create an account in a dictionary like this.
user {
'key': {'key1':'value1', 'key2':'value2'}
}
But I can't seem to be able to update the dict or list from within the function.
I've tried both
user['key']={'key1':'value1', 'key2':'value2'}
and
user.update({'key': {'key1':'value1', 'key2':'value2'}
methods of updating but it's not working.
This is the full code of this portion.
channel_list = []
users = {}
#app.route("/", methods=["GET", "POST"])
def index():
username = request.form.get("username")
firstname = request.form.get("firstname")
lastname = request.form.get("lastname")
if request.method == "GET":
if session.get("user_name"):
user_name = session.get("user_name")
get_user = users[user_name]
render_template("profile.html", user_name=get_user, users=users)
else:
return render_template("index.html")
if request.method == "POST":
session["user_name"] = username
newuser = {username: {"username": username, "firstname": firstname, "lastname": lastname}}
users.update(newuser)
get_user = users[username]
return render_template("profile.html", user=get_user, users=users)
#app.route("/signin", methods=["GET", "POST"])
def signin():
username = request.form.get("username")
if request.method == "GET":
if session.get("user_name"):
user_name = session.get("user_name")
get_user = users[user_name]
render_template("profile.html", user=get_user, users=users)
else:
return render_template("signin.html")
if request.method == "POST":
session["user_name"] = username
get_user = users[username]
return render_template("profile.html", user=get_user, users=users)
return render_template("signin.html")
I've figured out the problem from trying this in pure python and there, I'm also not able to update the dict without first running the function. But, how do I run each individual function (ex, index() or signin()) in flask, because I think this is the problem? I think this should be taken care of by app.run() but it's not working.
if __name__ == "__main__":
app.run()
socketio.run(app)
I keep getting "KeyError" each time because nothing is being inserted in the dict so there's nothing to select or access.
I'm trying to avoid using a DB and I really want to figure this out, I don't know what I'm doing wrong. The code works really well outside the flask app which is why it's frustrating.
The key error is being generated here due to this line:
if request.method == "GET":
...
get_user = users[user_name]
This is because when you start your Flask application, initially the 'users' dictionary is empty. So when you access either of the two routes for the first time using a 'GET' method (which is the default method when you type the URL into a browser), this line here would generate a KeyError as the dictionary is empty and the key "user_name" does not exist. A common way to fix this would be to use a try-except block and handle the Exception.
Also, both of the two functions seem to be doing the same thing, i.e. signing the user in. It would be better if you distinguished the two functions properly. I would recommend keeping all the sign-in logic in the 'signin' route and using the 'index' route to simply display the login page. You should also set the methods of the two routes properly. Only the 'signin' function is supposed to be accessed via a POST method.
I've a custom app written with flask and I'm trying to add an authentication decorator (d_auth), so that I don't have to check whether the user has authenticated or not within each routing function. The decorator works fine, but the problem is that url_for("index") fails after the user signs in. Here is my code for the decorator and the index routing function where I've added that decorator:
def d_auth(func):
wraps(func)
def decorated(*ags, **kgs):
#print("DECORATOR_RUNNING")
login_valid = (flask.session.get('auth_email') != None)
if not login_valid:
return redirect(url_for("login"))
else:
#func(*args, **kwargs)
return func(*ags, *kgs)
#pass
return decorated
#app.route("/", methods=["GET", "POST"])
#d_auth
def index():
creds = gdrive.get_gdrive_credentials(session['auth_user_id'])
if not creds:
info = "<h2 id='lblGAuthStatus' class='text-center text-danger'> <span class='glyphicon glyphicon-alert'></span> NOT Authenticated. <a href='/gdrive_auth'>Click here to Authenticate.</a></h2>"
elif creds.access_token_expired:
info = "<h2 id='lblGAuthStatus' class='text-center text-danger'> <span class='glyphicon glyphicon-alert'></span> Access Token EXPIRED. <a href='/gdrive_auth'>Click here to Authenticate.</a></h2>"
else:
info = "<h2 id='lblGAuthStatus' class='text-center text-success'> <span class='glyphicon glyphicon-ok'></span> Successfully Authenticated.</h2>"
return render_template('static/index.html', info=info)
What the decorator basically does is check whether the user has logged-in or not (not login_valid) and redirects them to login page if they haven't. This works perfectly. Problem is that once the user logs in and the login page tries to redirect them to the index page back again, it throws this error:
werkzeug.routing.BuildError: Could not build url for endpoint 'index'. Did you mean 'view' instead?
Here is the code for the /login route:
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == 'GET':
return render_template("static/login.html")
elif request.method == 'POST':
email = request.form['email']
password = request.form['password']
conn, cursor = db.opendb()
cursor.execute("select id, is_admin, first_name, last_name from user where email=? and password=?", (email, password))
row = cursor.fetchone()
if row == None:
return render_template("static/login.html", error="Invalid Credentials")
else:
session['auth_user_id'] = str(row['id'])
session['auth_email'] = email
session['auth_first_name'] = row['first_name']
session['auth_last_name'] = row['last_name']
session['auth_is_admin'] = row['is_admin']
return redirect(url_for("index"))
On that last line, url_for("index") is being called and that's where the error is coming. I know I can workaround with url_for("/") instead which is working, but I want to fix this permanently so that something else may not stop working in my relatively large code-base.
I just found an answer to my question here. Turns out that I've to wrap a decorator function using the #wraps(func), not simply wraps(func) like I'd done. Wonder why it didn't throw an error!
In short:
By only using the Flask micro-framework (and its dependencies) can we perform an internal redirect from one route to another?
For example:
User submits the registration form (both username and password) to #app.route('/register', methods=['POST'])
If the registration is successful, Flask internally does an HTTP POST to #app.route('/login', methods['POST']) passing the username and password
Process and log in the user
Details:
I am building a REST API using Flask and the Flask-JWT extension. More specifically I'm implementing the login and registration.
Login works perfectly and returns a JSON object with a token.
Following is my (login) authentication handler (i.e. /auth (POST request) - Default Flask-JWT authentication URL rule):
#jwt.authentication_handler
def authenticate(username, password):
user = User.query.filter_by(username=username).first()
if user and user.verify_password(password):
return user
return None
A successful login returns:
{
"token": "<jwt-token>"
}
Following is my registration route:
#app.route('/register', methods=['PUT'])
def register():
username = request.form.get('username')
password = request.form.get('password')
if username is None or password is None:
abort(400) # missing parameters
user = User.query.filter_by(username=username).first()
if user:
abort(400) # user exists
else:
user = User(user=user)
user.hash_password(password)
db.session.add(user)
db.session.commit()
# How do we generate a token?
# Perform an internal redirect to the login route?
return jsonify({'token': <jwt-token>}), 201
You should use the Post-Redirect-Get pattern.
from flask import Flask, redirect, request, render_template
app = Flask("the_flask_module")
#app.route('/', methods=["GET", "POST"])
def post_redirect_get():
if request.method == "GET":
return render_template("post_redirect_get.html")
else:
# Use said data.
return redirect("target", code=303)
#app.route("/target")
def target():
return "I'm the redirected function"
app.run(host="0.0.0.0", port=5001)
And if you want to pass data to the target function (like that token) you can use the session object to store it
So that would break down something like
#app.route('/register', methods=['PUT'])
def register():
username = request.form.get('username')
password = request.form.get('password')
if username is None or password is None:
abort(400) # missing parameters
user = User.query.filter_by(username=username).first()
if user:
abort(400) # user exists
else:
user = User(user=user)
user.hash_password(password)
db.session.add(user)
db.session.commit()
# How do we generate a token?
redirect("login_success", code=307)
#app.route("login_success", methods=["GET", "POST"])
#jwt_required()
def login_success():
return "Redirected Success!"
Edit:
I haven't used Flask-JWT before and didn't know about the post requirement. But you can tell Flask to redirect with the current method used (rather than a get request) by passing the redirect function code=307.. Hopefully that solves your extended problem.
I have a survey form. After submitting the form, I'd like to handle saving the data then redirect to a "success" view. I'm using the following code right now, but it just stays on the current url, while I'd like to go to /success. How can I do this?
#app.route('/surveytest', methods=['GET', 'POST'])
def surveytest():
if request.method == 'GET':
return render_template('test.html', title='Survey Test', year=datetime.now().year, message='This is the survey page.')
elif request.method == 'POST':
name = request.form['name']
address = request.form['address']
phone = request.form['phone']
email = request.form['email']
company = request.form['company']
return render_template('success.html', name=name, address=address, phone = phone, email = email, company = company)
You have the right goal: it's good to redirect after handling form data. Rather than returning render_template again, use redirect instead.
from flask import redirect, url_for, survey_id
#app.route('/success/<int:result_id>')
def success(result_id):
# replace this with a query from whatever database you're using
result = get_result_from_database(result_id)
# access the result in the tempalte, for example {{ result.name }}
return render_template('success.html', result=result)
#app.route('/survey', methods=["GET", "POST"])
def survey():
if request.method == 'POST':
# replace this with an insert into whatever database you're using
result = store_result_in_database(request.args)
return redirect(url_for('success', result_id=result.id))
# don't need to test request.method == 'GET'
return render_template('survey.html')
The redirect will be handled by the user's browser, and the new page at the new url will be loaded, rather than rendering a different template at the same url.
Though I am not specifically answering your current question I found myself with a similar problem with getting the page to redirect after the submission button had been clicked. So I hope this solution could potentially work for others that find themselevs in a similar predicament.
This example uses Flask forms for handling forms and submissions.
from flast_wtf import FlaskForm
#app.route("/", methods=["GET", "POST"])
def home():
stock_form = StockForm()
tick = stock_form.text_field.data
if tick != None:
return redirect(f'/{tick}', code=302)
return render_template("home.html", template_form=stock_form, ticker=tick)
The if statement checks that the submission after being clicked has a value, then redirects to your chosen link. This code is a copy and paste from a badly programmed stock price lookup.