Flask session and redis dont work with gunicorn - flask

Please i have a problem when i host my flask application on heroku. I explain to you. the code that I will present to you below works perfectly locally. but when I deploy it I manage to connect (on the connection page) but while I am redirected to the home page I am disconnected. However, I use redis (which works very well locally with the same url). I think the problem comes from gunicorn because it's the only parameter that changes between the local version on the browser. how to make the session persist please? my code here:
app.py
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://myurlofmydatabase"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SESSION_TYPE'] = "redis"
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_REDIS'] = redis.from_url(
"redis://default:password#redis-xxxxxxxx.com:10320")
app.config['SECRET_KEY'] = 'what_a_bad_secret_key'
app.config['SESSION_COOKIE_NAME'] = "session"
server_session = Session(app)
db = SQLAlchemy(app)
# cross_origin
# app.route('/login', methods=["POST"])
def login_user():
email = request.json["email"]
password = request.json["password"]
user = Users.query.filter_by(email=email).first()
if user is None:
return jsonify({"error": "error"}), 401
session["user_id"] = user.id
return jsonify({
"id": user.id
})
so, why gunicorn avoid to use flask session even with redis ? and how to fix it ?

Related

Unable to send emails through Flask-mail

I have been trying to create a web app which takes email address as an input through HTML form and sends a one time pin for further access to website.
I have 2 html files in my template folder (one for taking user's email address and other for OTP entering)
i have config.json file which stores my accountid and password through which i intend to send the OTP.
.py file
from flask import Flask, render_template, request
from random import randint
import json
from flask_mail import *
with open('config.json','r') as f:
params = json.load(f)['params']
mail = Mail(app)
otp = randint(100,999) #otp production
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = params['gmail-user']
app.config['MAIL_PASSWORD'] = params['gmail-password']
#app.route('/')
def home():
return(render_template('otpgive.html'))
#app.route('/getOTP', methods = ['POST'])
def getOTP(): #OTP send and Verify here
email_id = request.form['usermail']
msg = Message('OTP for the APPLICATION', sender = 'my_email', recipients = [email_id])
#my_email is the email through which i plan to send messages.
msg.body = "Greetings! Your email has been verified successfully. Kindly note your OTP is " + str(otp)
mail.send(msg)
return render_template('otpconfirm.html')
#app.route('/validateOTP', methods = ['POST'])
def validateOTP():
userotp = request.form['otp']
if (otp == int(userotp)):
return ("Email Verified Succcessfully ")
else:
return render_template('otpgive.html',msg = 'incorrect otp')
if __name__ == '__main__':
app.run(debug = False)
#app.run(host='0.0.0.0',port=5000, debug = True)
Solutions I tried but failed:
Tried disabling the firewall.
Tried setting the port number for 0.0.0.0
Tried debug = False
I was expecting it to work. i.e send emails to the users but it shows ConnectionError or InternalServerError
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
ConnectionRefusedError:
[WinError 10061] No connection could be made because the target machine actively refused it
I finally got the solution.
Since I was using G-Mail, I had to enable 2FA (2-Factor Auth) first on my profile, and then generate a new password for my app.
The password thus obtained was pasted in the config.json file instead of my
main profile password.
Used directions from this thread Less Secure option in gmail unavailable
Now Changes I made in my code:
Reverted back to ip: host='0.0.0.0', port=5000, debug = True
I kept firewall disabled as a precaution.
I repositioned mail = Mail(app) line to after all app.configs

How can I access to env variables from current_app? (RuntimeError: Working outside of application context)

(My english is not perfect, sorry)
I'm making an app using flask and flask-restx.
for changing between production and development envs,
I designed to access env variables to change environment(like server IP and Port)
finally, I found there is 'current_app', and wrote to access variables from DB model file. but an error message comes up. and then I searched this message in official document and wrote again following solution, but error message is still there.
could you help me?
RuntimeError: Working outside of application context.
here is my code
db.py
app = current_app
host = app.config['config_data']['host']
port = app.config['config_data']['port']
username = app.config['config_data']['username']
password = app.config['config_data']['password']
client = MongoClient(host=host, port=int(port), username=username, password=password)
return client
app.py
def create_app(test_config = None):
api = Api()
app = Flask(__name__)
CORS(app)
api.init_app(app)
app.config['config_data'] = app.config.get('DB_CONFIG')
with app.app_context():
from lib.db import getDb, DBHandler
getDb()
api.add_namespace(AuthControl, '/auth')
api.add_namespace(Board, '/board')
api.add_namespace(Manager, '/manager')
api.add_namespace(Admin, '/admin')
api.add_namespace(Loopback, '/loopback')
if test_config is None:
app.config.from_envvar('APP_CONFIG_FILE')
print("PRODUCTION RELEASE")
else:
app.config.from_envvar('APP_CONFIG_FILE')
print("DEVLEOPMENT")
return app
and here is execution code
#!/bin/sh
export APP_CONFIG_FILE="./config/prod_config.py"
export FLASK_ENV=production
gunicorn -b 0.0.0.0:5050 "app:create_app()"

Authentication with GitLab to a terminal

I have a terminal that served in webbrowser with wetty. I want to authenticate the user from gitlab to let user with interaction with the terminal(It is inside docker container. When user authenticated i ll allow him to see the containers terminal).
I am trying to do OAuth 2.0 but couldn't manage to achieve.
That is what i tried.
I created an application on gitlab.
Get the code and secret and make a http call with python script.
Script directed me to login and authentication page.
I tried to get code but failed(Their is no mistake on code i think)
Now the problem starts in here. I need to get the auth code from redirected url to gain access token but couldn't figure out. I used flask library for get the code.
from flask import Flask, abort, request
from uuid import uuid4
import requests
import requests.auth
import urllib2
import urllib
CLIENT_ID = "clientid"
CLIENT_SECRET = "clientsecret"
REDIRECT_URI = "https://UnrelevantFromGitlabLink.com/console"
def user_agent():
raise NotImplementedError()
def base_headers():
return {"User-Agent": user_agent()}
app = Flask(__name__)
#app.route('/')
def homepage():
text = 'Authenticate with gitlab'
return text % make_authorization_url()
def make_authorization_url():
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
save_created_state(state)
params = {"client_id": CLIENT_ID,
"response_type": "code",
"state": state,
"redirect_uri": REDIRECT_URI,
"scope": "api"}
url = "https://GitlapDomain/oauth/authorize?" + urllib.urlencode(params)
print get_redirected_url(url)
print(url)
return url
# Left as an exercise to the reader.
# You may want to store valid states in a database or memcache.
def save_created_state(state):
pass
def is_valid_state(state):
return True
#app.route('/console')
def reddit_callback():
print("-----------------")
error = request.args.get('error', '')
if error:
return "Error: " + error
state = request.args.get('state', '')
if not is_valid_state(state):
# Uh-oh, this request wasn't started by us!
abort(403)
code = request.args.get('code')
print(code.json())
access_token = get_token(code)
# Note: In most cases, you'll want to store the access token, in, say,
# a session for use in other parts of your web app.
return "Your gitlab username is: %s" % get_username(access_token)
def get_token(code):
client_auth = requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)
post_data = {"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI}
headers = base_headers()
response = requests.post("https://MyGitlabDomain/oauth/token",
auth=client_auth,
headers=headers,
data=post_data)
token_json = response.json()
return token_json["access_token"]
if __name__ == '__main__':
app.run(host="0.0.0.0",debug=True, port=65010)
I think my problem is on my redirect url. Because it is just an irrelevant link from GitLab and there is no API the I can make call.
If I can fire
#app.route('/console')
that line on Python my problem will probably will be solved.
I need to make correction on my Python script or different angle to solve my problem. Please help.
I was totally miss understand the concept of auth2. Main aim is to have access_token. When i corrected callback url as localhost it worked like charm.

flask-jwt-extended gives same token all the time for /login requests

jwt-flask-extended sends back same access token for any user always. I have integrated Flask with apache. Using Python 2.7.5, Operating System - Red Hat Enterprise Linux Server release 7.3 (Maipo). Find the code below.
app = Flask(__name__)
CORS(app)
#app.before_request
def log_request_info():
app.logger.debug('Headers: %s', request.headers)
app.logger.debug('Body: %s', request.get_data())
mysql = MySQL()
# MySQL configurations
app.config['MYSQL_DATABASE_USER'] = 'user'
app.config['MYSQL_DATABASE_PASSWORD'] = 'password'
app.config['MYSQL_DATABASE_DB'] = 'userdb'
app.config['MYSQL_DATABASE_HOST'] = 'mysql-host'
mysql.init_app(app)
# Setup the Flask-JWT-Extended extension
app.config['JWT_SECRET_KEY'] = 'Changeit' # Change this! if needed
app.config['JWT_EXPIRATION_DELTA'] = timedelta(seconds=28800)
jwt = JWTManager(app)
#app.route('/auth/token', methods=['POST'])
def login():
if not request.is_json:
return jsonify({"msg": "Missing JSON in request"}), 400
uid = request.json.get('uid', None)
username = request.json.get('username', None)
if not uid:
return jsonify({"msg": "Missing required parameter"}), 400
if not username:
return jsonify({"msg": "Missing required parameter"}), 400
# Identity can be any data that is json serializable
access_token = create_access_token(identity=uid)
return jsonify(access_token=access_token), 200
WSGIPassAuthorization On.
I added this directive to Apache mod wsgi config file. It started working as expected .
That's because you're using global variables which aren't thread safe.
Your access token variable should go inside a function or a method.
If you still want it globally accessible, you can use Werkzeug's local package along with Flask's g variable.
I recommend putting it in a method though.

Flask disable CSRF in unittest

In my projects __init__.py I have this:
app = Flask(__name__)
app.config.from_object('config')
CsrfProtect(app)
db = SQLAlchemy(app)
My development config file looks like:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
DEBUG = True
WTF_CSRF_ENABLED = True
SECRET_KEY = 'supersecretkey'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'project.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
And in my unittest setUp I have this:
from project import app, db
class ExampleTest(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.app = app.test_client()
db.create_all()
In theory, setting WTF_CSRF_ENABLED to False here should prevent CSRF for the unit tests, however I'm still getting CSRF errors if I do a POST while unit testing. I think it is because I have already called CsrfProtect(app) while WTF_CSRF_ENABLED is True (when I import app, it is called). If I set WTF_CSRF_ENABLED = False in the config file, it works as expected.
Is there anyway I can disable CSRF after it has already been enabled? Or am I barking up the wrong tree here?
You can disable it using the config variable WTF_CSRF_ENABLED,
for example
class TestConfig(Config):
TESTING = True
WTF_CSRF_ENABLED = False
...
or app.config['WTF_CSRF_ENABLED'] = False
See also flask-WTF documentation
Looking at the code for csrf_protect, it checks app.config['WTF_CSRF_METHODS'] every time a request comes in to see if this request type should be CSRF protected. By default the protected methods are:
app.config.setdefault('WTF_CSRF_METHODS', ['POST', 'PUT', 'PATCH'])
Because it actually checks the app.config every time, simply changing this to an empty list in my unit tests setUp resolves the issue:
from project import app, db
class ExampleTest(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
app.config['WTF_CSRF_METHODS'] = [] # This is the magic
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.app = app.test_client()
db.create_all()
Alternetly, it does register the csrf protection with app.before_request(), so I think it may be possible to unregister it by modifying the before request functions. But I think going that route would be more likely to see problems on future updates.
It wasn't too hard to keep the csrf_token if that is an option. I was able to successfully log into an application that used a csrf_token using some regular expressions and using the login function that is found in the Flask docs about testing.
def login(self, username, password):
rv = self.client.get('/login')
m = re.search(b'(<input id="csrf_token" name="csrf_token" type="hidden" value=")([-A-Za-z.0-9]+)', rv.data)
return self.client.post('/login', data=dict(
userName=username,
password=password,
csrf_token=m.group(2).decode("utf-8")
), follow_redirects=True)
So what I have done here is made the csrf_token be apart of the second capture group. This could easily be used to find a token all over the application.