#flask_login.login_required not working properly - python-3.x

I am using flask_login for implementing auth in my flask application
Here are the unauthorized_handler and login_required enabled method
#login_manager.unauthorized_handler
def unauthorized_handler():
return redirect(url_for('login'))
# use decorators to link the function to a url
#app.route('/profile', methods=['GET'])
#flask_login.login_required
def profile():
return render_template('profile.html')
I am using firebase as my backend service
#app.route('/login', methods=['GET', 'POST'])
def login():
auth = firebase.auth()
if request.method == 'POST':
try:
user = auth.sign_in_with_email_and_password(request.form['email'], request.form['password'])
if user != None:
return redirect(url_for('profile'))
except requests.exceptions.HTTPError as e:
response = e.args[0].response
error = response.json()['error']['code']
return redirect(url_for('home'))
return render_template('login.html')
The problem is that after I login(which is successfull) the app is automatically redirected to the /login url instead of /profle.
I tried turning my debug mode off and on still not working.

It can be a case of a double redirect happening, where you first get redirected to profile, and then the login_required decorator kicks in redirecting you back to login, because from the point of view of flask_login you are not logged in yet.
You might have retrieved user credentials from firebse, but with flask_login you have to also call login_user function. (Also see Login Example of flask login's page)

Related

Flask login/register form, error on the logic

I want to make a login/register form on my website, for that I found this script I reproduced from a tutorial, I adapted it but it still has an error.
If I login in the register form it's logging me, if I login in the login form, the webpage is reloading. I don't know why I have this issue but please help me!
#app.route('/', methods=['GET', 'POST'])
#app.route('/access', methods=['GET', 'POST'])
def access():
loginForm = LoginForm()
registerForm = RegisterForm()
if registerForm.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(registerForm.password.data)
new_user = User(username=registerForm.username.data, password=hashed_password)
login_user(new_user)
db.session.add(new_user)
db.session.commit()
return redirect(url_for('dashboard'))
elif loginForm.validate_on_submit():
user = User.query.filter_by(username=loginForm.username.data).first()
if user:
if bcrypt.check_password_hash(user.password, loginForm.password.data):
login_user(user)
db.session.add(user)
db.session.commit()
return redirect(url_for('home'))
return render_template('access.html', loginform=loginForm, registerform=registerForm)
You are adding your users with your login form when you use the db.session.add() and db.session.commit() functions.
This is what your login page should look like:
elif loginForm.validate_on_submit():
user = User.query.filter_by(username=loginForm.username.data).first()
if user and bcrypt.check_password_hash(user.password, loginForm.password.data):
login_user(user)
return redirect(url_for('home'))
i don't know if it works, i have an other error :
ValueError: invalid literal for int() with base 10: 'None'
there is my code :
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

How to redirect a page from one blueprint view to another?

I am using Flask along with the Blueprint module. In my application, I am trying to redirect the page to page/home after a successful login (user/login) using LDAP but the redirect takes forever without throwing any error.
I tried a couple of different variations of redirect(url_for('page.home')), redirect(url_for('page/home.html')). But each of these commands do not redirect. I am not sure what I am doing wrong. Kindly help.
Folder structure:
user/views.py:
from flask import Flask, Blueprint, request, render_template, redirect, url_for, session
from ldap3 import Server, Connection, ALL, NTLM
from snakeeyes.blueprints.page.views import page
import config.settings as p
user = Blueprint('user', __name__, template_folder='templates')
user.secret_key = 'dev key'
# #user.route('/')
# #user.route('/login')
# def login():
# return render_template('user/login.html')
def connect_ldap(username, password):
if not username or not password:
return False
# try:
# from ldap3 import Server, Connection, ALL, NTLM
# except ImportError as importException:
# print("LDAP3 import not found, run 'sudo pip install ldap3 && sudo pip3 install ldap3'")
# print(importException)
# return False
# define the server
server = Server('us01ds', port=389, get_info=ALL)
# define the connection
user = 'uid=%s,ou=people,ou=users,dc=global,dc=COMPANY,dc=com' % username
conn = Connection(server, user, password, auto_bind=True)
# perform the Bind operation
if not conn.bind():
print('error in bind', conn.result)
return False
else:
return True
#user.route('/', methods=['GET', 'POST'])
#user.route('/login/', methods=['GET', 'POST'])
def login():
# global username
# username = None
# If POST, redirect to dashboard
if request.method == 'POST':
username = request.form['username'].encode('utf8').decode("utf-8")
password = request.form['password'].encode('utf8').decode("utf-8")
# Try to login using ldap
test = connect_ldap(username, password)
# Invalid credentials
if not test:
return render_template(
'login.html',
isinvalid='is-invalid',
error='Username or Password is incorrect'
)
else:
# session['user_id'] = request.form['username']
print('redict to home page')
return redirect(url_for('page.home'))
# If GET, render the login page
else:
return render_template('user/login.html')
page/views.py:
from flask import Blueprint, render_template
page = Blueprint('page', __name__, template_folder='templates')
#page.route('/home')
def home():
return render_template('page/home.html')
#page.route('/terms')
def terms():
return render_template('page/terms.html')
#page.route('/privacy')
def privacy():
return render_template('page/privacy.html')
I found a fix for this problem.
In order to better facilitate the generation of URLs that make use of an HTTPS URL
scheme this patch adds a parameter with this specific purpose in mind. To
achieve this we explicitly pass in a param, _scheme='https', and then set the
url_scheme attribute of our MapAdapter instance appropriately.
Importantly, _external=True must be set in order for this to work properly.
As such, failure to do so results in a ValueError being raised.
So, I just replace return redirect(url_for('page.home')) => return redirect(url_for('page.home', _external=True, _scheme='https'))
Reference: https://github.com/pallets/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7

flask session not working if I use mkdtemp() for SESSION_FILE_DIR

I have a flask application in what I'm trying to create a session but only for the admin routes, so anywhere else can be accessible except admin routes, the thing is in my localhost everything works fine, but, in the live server, the session get created when I log in, but when I try to check if the session exist calling session.get("user_id"), it doesn't. Is like if the session is not created persistent. I have been reading for 10 hours today and I finally found why is not working, but I really don't undestand why it happens. The problem i'm facing is located in app.config["SESSION_FILE_DIR"] = mkdtemp(), if I use this in the localhost works, but not in the live server, and if I omite that line, then it works in the live server. Here is my code:
from flask import Flask, flash, redirect, render_template, request, session, jsonify, url_for
from flask_session import Session
from functools import wraps
from werkzeug.security import check_password_hash, generate_password_hash
from tempfile import mkdtemp # used to create a temp directory
from helpers import login_required
import os
# Configure application as flask app
app = Flask(__name__)
# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.secret_key = 'superkey'
# function to login admin
#app.route("/login", methods=["GET", "POST"])
def login():
"""Log admin in"""
# Forget any user_id
session.clear()
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# Ensure username was submitted
if not request.form.get("username"):
flash ("must provide username")
return render_template('/login.html')
# Ensure password was submitted
elif not request.form.get("password"):
flash ("must provide password")
return render_template('/login.html')
# Query database for username
username = request.form.get("username")
username = str.lower(username)
db = dbConnection()
db.execute("SELECT * FROM identities WHERE user_name = ?", [username])
rows = db.fetchall()
# Ensure username exists and password is correct
if len(rows) == 1:
uid, name, pass_hash = rows[0]
if len(rows) != 1 or not check_password_hash(pass_hash, request.form.get("password")):
flash ("invalid username and/or password")
return render_template('/login.html')
# Remember which user has logged in
session["user_id"] = uid
# Redirect user to home page
return redirect("/adminForms")
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("login.html")
# function as decorator to ensure the user was logged in before
# can go to a protected page
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
# if session don't return a value, then the user needs to log in
if session.get("user_id") is None:
return redirect("/login")
return f(*args, **kwargs)
return decorated_function

Unexpected AssertionError: single test not using logged in user from previous step

I am following the tutorial by http://www.patricksoftwareblog.com/flask-tutorial/, which I believe is based on https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world. Great stuff for a beginner.
I am getting different results when testing my code through frontend manually (which works fine) v.s. through pytest.
My test tries to show the "groups" endpoint which requires a login (standard #login_required decorator).
I initially test the user getting a login page ("Knock knock") when trying to get the endpoint without a login. This works manually and through pytest.
I login a user. If I inspect the response from the login I can clearly see a "Welcome back Pete!" success message.
My second assert receives a response from URL /login?next=%2Fgroups indicating the /groups endpoint is called without a login/authentication preceding it and the assert fails. Testing this manually works as expected. Why is that single test not using the same user/session combination in the next step(s)?
Test with the problem is the first snippet below:
def test_groups(app):
assert b'Knock knock' in get(app, "/groups").data
login(app, "pete#testmail.com", "pete123")
assert b'Test group 1' in get(app, "/groups").data
My "get" function for reference:
def get(app, endpoint: str):
return app.test_client().get(endpoint, follow_redirects=True)
My "login" function for reference:
def login(app, email="testuser#testmail.com", password="testing"):
return app.test_client().post('/login', data=dict(email=email, password=password), follow_redirects=True)
The app (from a conftest fixture imported in the test module by #pytest.mark.usefixtures('app')) for reference:
#pytest.fixture
def app():
"""An application for the tests."""
_app = create_app(DevConfig)
ctx = _app.test_request_context()
ctx.push()
yield _app
ctx.pop()
The login route for reference:
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.is_correct_password(form.password.data):
user.authenticated = True
user.last_login = user.current_login
user.current_login = datetime.now()
user.insert_user()
login_user(user)
flash(f'Welcome back {user.name}!', 'success')
return redirect(url_for('our_awesome_group.index'))
else:
flash('Incorrect credentials! Did you already register?', 'error')
else:
flash_errors(form)
return render_template('login.html', form=form)
The groups route for reference:
#app.route('/groups')
#login_required
def groups():
groups_and_users = dict()
my_group_uuids = Membership.list_groups_per_user(current_user)
my_groups = [Group.query.filter_by(uuid=group).first() for group in my_group_uuids]
for group in my_groups:
user_uuids_in_group = Membership.list_users_per_group(group)
users_in_group = [User.query.filter_by(uuid=user).first() for user in user_uuids_in_group]
groups_and_users[group] = users_in_group
return render_template('groups.html', groups_and_users=groups_and_users)
Im going to sum up the comments I made that gave the answer on how to solve this issue.
When creating a test app using Pytest and Flask there are a few different ways to go about it.
The suggested way to create a test client with proper app context is to use something like:
#pytest.fixture
def client():
""" Creates the app from testconfig, activates test client and context then makes the db and allows the test client
to be used """
app = create_app(TestConfig)
client = app.test_client()
ctx = app.app_context()
ctx.push()
db.create_all()
yield client
db.session.close()
db.drop_all()
ctx.pop()
That creates the client while pushing the app context so you can register things like your database and create the tables to the test client.
The second way is show in OP's question where use app.test_request context
#pytest.fixture
def app():
"""An application for the tests."""
_app = create_app(DevConfig)
ctx = _app.test_request_context()
ctx.push()
yield _app
ctx.pop()
and then create the test client in another pytest fixture
#pytest.fixture
def client(app):
return app.test_client()
Creating a test client allows you to use various testing features and gives access to flask requests with the proper app context.

How to logout in Python Bottle?

I have the following test snippet:
def check(username, password):
if username == "b" and password == "password":
return True
return False
#route('/logout')
#route('/logout', method="POST")
def logout():
# template with a logout button
# this does redirect successfully, but this shouldn't happen
redirect('/after-login')
#route('/after-login')
#auth_basic(check)
def after_login():
return "hello"
#route('/login')
#route('/login', method="POST")
def login():
return template("views/login/login_page")
username = post_get('username')
password = post_get('password')
I'm attempting to log out of the system, but I haven't been able to find any resources on how to do this. Basically, I tried dir(response) and dir(request) and haven't found any functions that appears to set the session off (mostly attempting to reset cookies), unless I close the browser.
Issued the same problem. Well, The decision I found in docs and used is response.delete_cookie('<cookiename>')
So every time I enter the page with setting any cookies, first I delete all possibble to change cookies.
You want to log out of HTTP Basic Auth, which is not really what it was designed for. But there does seem to be a way: return an HTTP 401.
from bottle import abort
#route('/logout', method=["GET", "POST"])
def logout():
abort(401, "You're no longer logged in")
Does this work?

Resources