Passing POST call values using url_for in Flask - python

I am new to Flask and have been working on an existing app for its login module. It has to be delivered asap. The login credentials validates by Active Directory authentication, which is working as expected. I am referring this flask link which says
url_for('main', page=2): return the internal URL /?page=2. All optional keyword arguments are treated as GET parameters.
My index page is loaded from here (working perfectly):
#app.route("/", methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
userName = request.form['username']
pwd = request.form['password']
try:
ldapobject = auth.bind(userName,pwd)
return redirect(url_for('scheduler')) ----> NEED TO PASS USER AND PWD TO scheduler function using post here
except ldap.LDAPError, error_message:
error = 'Invalid Credentials. Please try again'
return render_template('login.html', error=error)
My scheduler function:
#app.route("/home", methods=['POST'])
def scheduler():
# some code here
# WANT TO HAVE USER CREDENTIALS IN THIS FUNCTION FROM login page
return render_template("scheduler.html", scheduler=form)
Problem: How to pass user's credentials to scheduler method as a POST call

If you want to persist user data between your application pages - you will need to store some session data on user side, e.g. cookies.
flask-login module (https://flask-login.readthedocs.org/en/) can do all the dirty work for you.
In short you need to create User class with several required fields and methods (https://flask-login.readthedocs.org/en/latest/#your-user-class) and specify several methods with decorators to load and check users that are being authenticated:
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app=app)
#login_manager.user_loader
def load_user(username):
return User(username)
#login_manager.request_loader
def load_from_request(request):
return User.auth_is_valid(request.authorization)
After that you can mark any routes that need user credentials with #login_requied decorator and access logged in user via current_user variable:
from flask_login import login_required, current_user
#app.route("/user_needed")
#login_required
def some_function():
print(current_user)

Related

Flask-login is able to log in user, but still AnonymousUserMixin for all subsequent requests

I am able to successfully login and authenticate the user and I can access their info with current_user from the login method. But then when I make another API call and try to access current_user, I get an error and current_user is of type AnonymousUserMixin.
I thought the issue may be with the load_user function, so I added a print statement but it seems as though it never gets called.
Is there anything wrong with the below setup that would prevent load_user from getting executed when an API call is made or cause the user's session to not persist?
# app.py
login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.user_loader
def load_user(user_id):
print("[LoadUser]")
return Users.query.get(user_id)
#app.route("/login")
def login():
user = Users(
id = unique_id, name=users_name, email=users_email
)
login_user(user, remember=True)
print(current_user.username) # Works with no issue
return
#app.route("/test")
def test():
print(current_user.username) # AttributeError: 'AnonymousUserMixin' object has no attribute 'username'
I noticed your test route does not protect against anonymous access. according to flask-login docs 0.7..current_user would then be anonymous... Which means if you lock the test route to logged in users only current_user will then be populated!
#app.route("/test")
#login_required
def test():
print(current_user.username)
https://flask-login.readthedocs.io/en/latest/

Problem with Flask Blueprints, canĀ“t remove the /home route, or app crashes

Hi there I'm creating a Flask web app and now I have to create more cruds, so I decided to modularize the app using Blueprints.
I have a Login function on main.py that allows me to enter the app interface:
app.route('/', methods=['GET', 'POST'])
def login():
# Output message if something goes wrong...
msg = ''
# Check if "username" and "password" POST requests exist (user submitted form)
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
# Create variables for easy access
username = request.form['username']
password = request.form['password']
# Check if account exists using MySQL
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute(
'SELECT * FROM accounts WHERE username = %s AND password = %s', (username, password,))
# Fetch one record and return result
account = cursor.fetchone()
# If account exists in accounts table in out database
if account:
# Create session data, we can access this data in other routes
session['loggedin'] = True
session['id'] = account['id']
session['username'] = account['username']
# Redirect to home page
return redirect(url_for('client.home'))
else:
# Account doesnt exist or username/password incorrect
msg = 'Incorrect username/password!'
# Show the login form with message (if any)
return render_template('index.html', msg=msg)
It redirects to this Blueprint:
from flask import Blueprint, render_template
from flask import render_template, request, redirect, url_for, session, flash
from flask_mysqldb import MySQL
import MySQLdb.cursors
import re
from extension import mysql
client = Blueprint('client', __name__,
static_folder="../static", template_folder="../templates")
#client.route('/')
def home():
if 'loggedin' in session:
cur = mysql.connection.cursor()
cur.execute('SELECT * FROM cliente')
data = cur.fetchall()
# User is loggedin show them the home page
return render_template('home.html', username=session['username'], cliente=data)
# User is not loggedin redirect to login page
return redirect(url_for('login'))
It works just fine, but with a condition. On my main.py I also have this:
#app.route('/home')
def home():
pass
And this is the problem, I don't know why I should keep this route on my main.py because if I delete it my app crashes and throws me this error:
werkzeug.routing.BuildError
werkzeug.routing.BuildError: Could not build url for endpoint 'home'. Did you mean 'client.home' instead?
I have no idea why does this happens.
Why should I keep this route? or What I'm doing wrong?
Could you please give me a hand?
I've trying to change the redirect to using multiple routes, but if I delete that /home route.. my app crashes anyway.
in url_for look for the name of the function ie url_for('function_name', parameters)
so to avoid the crash better to change the name of main.py home function to something else.
Solved: I had one ref to Home on other file: Layout.html.
Just removed the ref and it's solved

Implementing a login page with Flask

Does Flask have any built-in support for user login/logout functionality? I've found this add-on project, but it only seems to provide the pieces to build your own login system. It doesn't seem to be a complete system.
I'm new to Flask, and I'm coming from Django where this is all built-in, so I'm finding it a little baffling that this basic functionality is missing.
Using some incomplete examples I've found, I'm trying to implement an index page that redirects to a login page for anonymous users, and after a successful login, redirects page to the index page. This is what I currently have implemented as a "hello world" login/logout example:
#!/usr/bin/env python
import flask
from flask import Flask, Response, render_template
from flask.ext.login import LoginManager, UserMixin, login_required, login_user, logout_user
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
class User(UserMixin):
# proxy for a database of users
user_database = {
"JohnDoe": ("JohnDoe", "John"),
"JaneDoe": ("JaneDoe", "Jane"),
}
def __init__(self, username, password):
self.id = username
self.password = password
#property
def name(self):
return self.id
#classmethod
def get(cls, id):
ret = cls.user_database.get(id)
if ret is not None:
return cls(*ret)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
#login_manager.request_loader
def load_user(request):
token = request.headers.get('Authorization')
if token is None:
token = request.args.get('token')
if token is not None:
username,password = token.split(":") # naive token
user_entry = User.get(username)
if (user_entry is not None):
user = User(user_entry[0],user_entry[1])
if (user.password == password):
return user
return None
#app.route("/",methods=["GET"])
#login_manager.request_loader
def index():
if load_user(flask.request):
return render_template('index.html')
else:
return flask.redirect(flask.url_for('login'))
#return Response(response="Hello World!",status=200)
#app.route('/login', methods=['GET', 'POST'])
def login():
if flask.request.method == 'GET':
return '''
<form action='login' method='POST'>
<input type='text' name='email' id='email' placeholder='email'></input>
<input type='password' name='pw' id='pw' placeholder='password'></input>
<input type='submit' name='submit'></input>
</form>
'''
email = flask.request.form['email']
user = User.get(email)
if user and flask.request.form['pw'] == user.password:
login_user(user)
return flask.redirect(flask.url_for('index'))
return 'Bad login'
#app.route("/logout")
#login_required
def logout():
logout_user()
return flask.redirect(flask.url_for('index'))
if __name__ == '__main__':
app.config["SECRET_KEY"] = "ITSASECRET"
app.run(port=5000, debug=True)
However, it doesn't work, because even though it seems to login successfully, when it redirects to the index page, it can't lookup the user from the session and redirects back to the login page. What am I doing wrong?
Flask-Login is a very basic login manager that is built upon with a few other user management frameworks. I have been using Flask-User in production for over 1.5 years with about 30K users and have not had any problems with it (which uses flask-login under the hood). The maintainer is active and has responded to my issues in a timely manner.
It handles user login, registration, lost password, and even email confirmations if so desired. It comes with some pre-built forms if you don't want to mess with that but is easily customizable if you do.
Sounds like maybe when login_user gets called the login is not being persisted. I would make sure however you're doing your sessions is actually storing the login.

Internal Redirect in Flask

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.

#login_required trouble in flask app

I have created a blueprint that handles authenticating. This blue print uses Flask-Login. And has the following, as well as more code not shown.
In the blueprint I have the following:
from flask.ext.login import LoginManager
from flask.ext.login import UserMixin
from flask.ext.login import current_user
from flask.ext.login import login_required
from flask.ext.login import login_user
from flask.ext.login import logout_user
auth_print = Blueprint('auth_print', __name__)
login_manager = LoginManager()
login_manager.login_view = '/login'
class User(UserMixin):
user_store = {} # Stores the users that are already logged in.
def __init__(self, user_id):
self.user_store[user_id] = self # add the user to the user_store
self.username = user_id # the user_id is in fact the username
self.id = unicode(user_id)
def sign_out(self):
logout_user()
try:
del self.user_store[self.id]
except KeyError:
pass
#classmethod
def get(cls, user_id):
return cls.user_store.get(user_id)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
def get_current_user():
return current_user
#login_required
#auth_print.route('/')
def user():
return "Welcome, and thanks for logging in."
Then I have a small app I would like to add authentication to.
Small App
import the_above_module
app.register_blueprint(the_above_module.auth_print) # register the blueprint
#the_above_module.login_required
#app.route('/profile')
def protected():
name = the_above_module.get_current_user().username
return "Thank you for logging in."
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
Now I know the blueprint's #login_required is working because if I open the browser and go to localhost:8000/ I have to sign in.
However if I go to localhost:8000/profile the login_required decorator never gets triggered. I thus get an error because there is no current user.
Why would #login_required work in the blueprint and not in the app, even when im sure to maintain the same name spaces?
You have to change the order of the decorators. Quoting the Flask documentation:
So how would you use that decorator now? Apply it as innermost
decorator to a view function. When applying further decorators, always
remember that the route() decorator is the outermost:
#app.route('/secret_page')
#login_required
def secret_page():
pass
When we want the user not to access the private page or the page which requires login for that case flask provides decorators.
#app.route("/welcome")
#login_required # If the user is not logged in then it will redirected to unauthorized_handler
def welcome_page():
return """<h1> welcome user</h1>"""
#login_manager.unauthorized_handler # In unauthorized_handler we have a callback URL
def unauthorized_callback(): # In call back url we can specify where we want to
return redirect(url_for('login')) # redirect the user in my case it is login page!
I hope your problem is solved !!!
#login_manager.unauthorized_handler
def unauthorized_callback():
return redirect(url_for('website.index'))

Categories