How store a base64 image into user session in Flask - python

Good day. I'm starting with Flask in Python. Everything goes fine, but I have a problem with the session. The idea is to store the base64 avatar of the user into his session after he logged in. Then, in the layout just access to his avatar by <img src="data:image/png;base64,{{session.user.photo}}" />.
The problem comes when I login, put the base64 into the session and redirect to /home. Inside /home route, I have a simple access control, if the session hasn't user key, redirect to login again. The problem is after set the session and redirect to home, the session is empty in /home, and never render the template.
#root.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
if 'user' in session:
return redirect('/home')
else:
return render_template('login.html', message = None)
else:
username = request.form['username']
pwd = request.form['password']
user = User.where('username', username).where('pwd', pwd).get().first()
if(user == None):
return render_template('login.html', message = 'Usuario o contraseƱa incorrecta')
else:
session['user'] = {
'id': user.id,
'username': user.username,
'photo': user.photo
}
return redirect('/home')
#root.route('/home')
def home():
if 'user' in session: # THIS IS FALSE, SESSION IS EMPTY
return render_template('home.html')
else:
return redirect(url_for('root.login', message = None))
Flask only accept small data in session? thanks.

I am sorry you shouldn't store a big image in the session.
A good way to achieve it is to store unique image id, and call url with this unique ID. Like this:
<img src = "//myview?id=513654534343543543" />

Related

Is there any way that I can create a login system using prisma-client-py, flask-login & flask?

I am attempting to create a control panel only using prisma-client-python, flask-login & flask as the primary webserver. I have successfully managed to create the routes, as well as built the registration screen & have also granted prisma access to the database. When I try to use the route,
Here is my code for the login route:
`
from flask import Blueprint, request, render_template
from prisma.models import User
from flask_login import login_user
login_blueprint = Blueprint('login', __name__ , template_folder='../pages/')
#login_blueprint.route('/', methods=['GET','POST'])
def list_create():
if request.method == 'GET':
return render_template('login.html')
if request.method == 'POST':
data = request.form
if data is None:
return
email = data.get('email')
password = data.get('password')
if email is None or password is None:
return {"error": "You need to provide email and password"}
user = User.prisma().find_many(where={'email': email, 'password': password},)
print(user)
return login_user(user)
`
Here is my code for the registration route:
`
from flask import Blueprint, request, render_template
from prisma.models import User
user_blueprint = Blueprint('register', __name__ , template_folder='../pages/')
#user_blueprint.route('/', methods=['GET','POST'])
def list_create():
if request.method == 'POST':
data = request.form
if data is None:
return
name = data.get('username')
email = data.get('email')
password = data.get('password')
if name is None or email is None:
return {"error": "You need to provide name and email"}
user = User.prisma().create(data={'email': email, 'username': name, 'password': password})
return dict(user)
return render_template('register.html')
`
here is my prisma schema:
`
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator db {
provider = "prisma-client-py"
interface = "sync"
}
model User {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
email String #unique
password String
username String?
admin Boolean #default(false)
is_active Boolean #default(false)
}
`
& here is my main app.py route.
`
from flask import Flask
from prisma import Prisma, register
from routes.register import user_blueprint
from prisma.models import User
from routes.login import login_blueprint
# from routes.post import post_blueprint
db = Prisma()
db.connect()
register(db)
app = Flask(__name__)
#app.route('/', methods=['GET'])
def index():
return {
"ping": "pong"
}
app.register_blueprint(user_blueprint, url_prefix='/register')
app.register_blueprint(login_blueprint, url_prefix='/login')
if __name__ == "__main__":
app.run(debug=True, port=5000, threaded=True)
`
I had tried using login_user(user) as stated in code examples using it. But I keep getting the following error:
AttributeError: 'list' object has no attribute 'is_active'
Does anyone have any idea for what I should to resolve this problem & allow the user to login?
The login route (for which the function is also oddly called list_create) queries the database using find_many - which will return a list of items, not a single item. Flask's login() expects a single user, not a list. The exception makes sense, as the login function is provided with a list of user(s), not a user object.
A simple fix can be:
#login_blueprint.route('/', methods=['GET','POST'])
def list_create():
...
user = User.prisma().find_many(where={'email': email, 'password': password},)
if user and len(user) == 1:
return login_user(user[0])
return {"error": "Authentication error, or user does not exist"}
Alternatively, you can try using find_unique as per Prisma's documentation.

Pytest Flask Request Referrer

I'm currently testing my Flask application using Pytest and ran into a problem with a POST request and a redirect. Let me explain a bit more.
A user wants to register for our new site, but must confirm they have an account with a different site. Once they confirm the credentials of the other account, they are taken to the register page. They can only hit the register page if coming from the confirmation page else they are redirected back to the home page.
I want to test this functionality and can successfully make a POST request to the confirmation page. If I don't specify follow_redirects=True and print the response data, I get the following HTML:
Redirecting...
Redirecting...
You should be redirected automatically to target URL: /register?emp_id=1. If not click the link.
Great! Exactly what I'm looking for! I want to be redirected to the registration page.
Now when I do specify follow_redirects=True and print out the response data, I expected the register page HTML to return. The response data instead returns the home page HTML.
I further investigated where the problem was. As I mentioned before, the only way you can hit the registration page is from the confirmation page. I took a look at the request.referrer attribute in the view during the test and it will return None. I attempted setting the Referrer header content in the test's POST request, but to no luck.
Here is the code I'm working with:
views.py
#app.route('/confirm', methods=['GET', 'POST'])
def confirm_bpm():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = BPMLoginForm()
if form.validate_on_submit():
bpm_user = BPMUser.query\
.filter(and_(BPMUser.group_name == form.group_name.data,
BPMUser.user_name == form.username.data,
BPMUser.password == encrypt(form.password.data)))\
.first()
if not bpm_user:
flash('BPM user account does not exist!')
url = url_for('confirm_bpm')
return redirect(url)
if bpm_user.user_level != 3:
flash('Only L3 Users can register through this portal.')
url = url_for('confirm_bpm')
return redirect(url)
url = url_for('register', emp_id=bpm_user.emp_id)
return redirect(url)
return render_template('login/confirm_bpm.html', form=form)
#app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
if request.method == 'GET' and\
request.referrer != request.url_root + 'confirm':
return redirect(url_for('index'))
emp_id = request.args.get('emp_id')
emp_id_exists = User.query.filter_by(emp_id=emp_id).first()
if emp_id_exists:
flash('User is already registered!')
return redirect(url_for('login'))
form = RegistrationForm()
if form.validate_on_submit():
new_user = User(login_type=form.login_type.data, login=form.login.data,
emp_id=emp_id)
new_user.set_password(form.password.data)
db.session.add(new_user)
db.session.commit()
flash('Registration successful!')
return redirect(url_for('login'))
return render_template('login/register.html', form=form)
TESTS
base.py
from config import TestConfig
from app import app, db
#pytest.fixture
def client():
"""
Initializes test requests for each individual test. The test request
keeps track of cookies.
"""
app.config.from_object(TestConfig)
client = app.test_client()
ctx = app.app_context()
ctx.push()
yield client
ctx.pop()
def confirm_bpm_login(client, group_name, username, password):
"""
POST to /confirm
"""
return client.post('/confirm', data=dict(
group_name=group_name,
username=username,
password=password,
submit=True
), follow_redirects=True)
test_auth.py
from app import db
from app.models import BPMCompany, BPMEmployee, User, BPMUser
from tests.base import client, db_data, login, confirm_bpm_login
def test_registration_page_from_confirm(client, db_data):
"""
Test registration page by HTTP GET request from "/confirm" url.
Should cause redirect to registration page.
!!! FAILING !!!
Reason: The POST to /confirm will redirect us to /register?emp_id=1,
but it will return the index.html because in the register view,
request.referrer does not recognize the POST is coming from /confirm_bpm
"""
bpm_user = BPMUser.query.filter_by(id=1).first()
rv = confirm_bpm_login(client, bpm_user.group_name,
bpm_user.user_name, 'jsmith01')
assert b'Register' in rv.data
The db_data parameter is a fixture in base.py that just populates the DB with the necessary data for the registration flow to work.
My goal is to test a complete registration flow without having to separate the confirmation and registration into two tests.
The Flask test client does not appear to add the Referer header when it follows redirects.
What you can do is implement your own version of following redirects. Maybe something like this:
def confirm_bpm_login(client, group_name, username, password):
"""
POST to /confirm
"""
response = client.post('/confirm', data=dict(
group_name=group_name,
username=username,
password=password,
submit=True
), follow_redirects=False)
if 300 <= response.status_code < 400:
response = client.get(response.headers['Location'], headers={
"Referer": 'http://localhost/confirm'
})
return response
Please test this, I wrote it from memory and may need some minor adjustments.

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.

flask redirect not working

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.

In Flask, set a cookie and then re-direct user

It seems like in Flask, cookies are set by modifying the response object directly.
How can I return a response object, but also redirect a user to a different page upon successful login? I'd like to specifically redirect the user instead of rendering a different page, in case the user hits REFRESH.
Here's my current code, which simply displays the same page, login.html:
#app.route('/login', methods=['POST', 'GET'])
def login():
errors = []
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
#Check the user's e-mail
try:
u = User(email)
except UserError, e:
errors.append(e)
else:
#Check the user's password
if not u.authenticatePassword(password):
errors.append(('password','Invalid password'))
return render_template('login.html',error=errors)
#Set the session
s = Session()
s.user_id = u.user_id
s.ip = request.remote_addr
#Try to set the cookie
if s.setSession():
response = make_response( render_template('login.html',error=errors))
response.set_cookie('session_id', s.session_id)
return response
return render_template('login.html',error=errors)
You should change your code to something like:
from flask import make_response
if s.setSession():
response = make_response(redirect('/home'))
response.set_cookie('session_id', s.session_id)
return response

Categories