Flask: How to Authenticate with Flask-Redis and Redis - python

How can I send "AUTH" command to authenticate the connection using Flask-Redis in Flask app?
I know Flask-Redis is just a small wrapper for redis-py but I can't figure out how to handle the authorization.

Do you mean this Flask_Redis: https://pypi.python.org/pypi/Flask-Redis/0.0.5 ?
If so, there is a better way to handle this than placing it in the URL. According to the docs, in your Flask config place the following:
REDIS_HOST = "localhost"
REDIS_PASSWORD = "password"
REDIS_PORT = 6379
If placing the config in code, as in your example:
app.config["REDIS_PASSWORD"] = 'password'
Doing this in the config should be more maintainable and configurable without modifying code.

Ok I have found how to solve this.
You can pass the password in URL, example:
...
app.config["REDIS_URL"] = 'redis://:password#localhost/0'
redis_db = Redis(app, "REDIS")
...

Related

Flaskdance doesn't generate a HTTPS uri

I'm trying to set up Google sign-in using Flask dance for a flask based website:
from flask_dance.contrib.google import make_google_blueprint, google
blueprint = make_google_blueprint(
client_id= "CLIENT_ID",
client_secret="CLIENT_SECRET",
scope=[
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/userinfo.email",
]
)
app.register_blueprint(blueprint, url_prefix="/google_login")
And as the documentation suggests, I have the view set up like this:
#app.route('/google_login')
def google_login():
if not google.authorized:
return redirect(url_for("google.login"))
resp = google.get("/oauth2/v2/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
When I was testing I set the environment variable, OAUTHLIB_INSECURE_TRANSPORT to 1 by using
export OAUTHLIB_INSECURE_TRANSPORT=1
And now even after I've removed the environment variable, for some reason the Flaskdance seems to always resolve the URI to a http instead of HTTPS.
This is evident from the redirect uri mismatch error I'm getting (here website refers to the domain name):
The redirect URI in the request,
http://"website"/google_login/google/authorized, does not match
the ones authorized for the OAuth client.
And here are the authorized redirect URIs I've set up in my Google cloud console:
https://"website"/google_login/google/authorized
https://www."website"/google_login/google/authorized
I tried unsetting the environment variable using this command:
unset OAUTHLIB_INSECURE_TRANSPORT
What am I missing here? Any help would be appreciated.
If Flask-Dance is generating http URLs instead of https, that indicates that Flask (not Flask-Dance, but Flask itself) is confused about whether the incoming request is an https request or not. Flask-Dance has some documentation about how to resolve this problem, and the most likely cause is a proxy server that handles the HTTPS separately from your application server.
The fix is to use a middleware like werkzeug's ProxyFix to teach Flask that it's behind a proxy server. Here's how you can use it:
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
I had the same problem and in my case adding this to my Apache VirtualHost config solved it:
RequestHeader set X-Forwarded-Proto "https"
My Flask is running behind an Apache proxy but Nginx would also have similar issues, potentially.

Google App Engine flask SSL and OAuth2 problems

i'm trying to create flask web application on google app engine python 3 flexible env with oauth2 authentication on custom domain.
So, the problems are following :
1)I have added custom domain to my project, and also added SSL to that custom domain. In google cloud console everything seems fine, but SSL not showing/working on my custom domain.
Maybe problem is in my dispatch file?
dispatch:
- url: 'mycustomdomain.com/'
service: default
- url: 'www.mycustomdomain.com/'
service: default
2)I can't login in despite having SSL on https://[project-id].appspot.com. After pressing "Login with Google" i'm redirecting to /authorize, where i choose account from which i want to login. After that happens redirect to /oauth2callback, https mystically changes to http and i can't login, getting following error InsecureTransportError: (insecure_transport) OAuth 2 MUST utilize https.
Python authorize :
#app.route('/authorize')
def authorize():
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true')
flask.session['state'] = state
return flask.redirect(authorization_url)
Python oauth2callback:
#app.route('/oauth2callback')
def oauth2callback():
state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
flask.session['credentials'] = credentials_to_dict(credentials)
session = flow.authorized_session()
flask.session['username_output'] = session.get(
'https://www.googleapis.com/userinfo/v2/me').json()
return flask.redirect(flask.url_for('map'))
When testing locally i'm using os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' and locally it is working perfectly fineAny suggestions??
SOLUTION
Problem solved -
1)i forgot to add SSL certificate to domain(where domain is hosted), thats why SSL wouldn't show.. I know, so lame mistake.
2)First, I forced SSL with SSLify for flask, but that didn't solve the problem, which was in following line
authorization_response = flask.request.url
no matter what, this line of code gave me http://, i tried to change every scheme that i could find to https in flask.url_for(_scheme='https'), but that didn't help either, so for now, my workaround is
authorization_response = authorization_response.replace('http', 'https')
i know, not the best solution, but it works.
I ran into this issue as well and found out that the issue stems from authorization_response = flask.request.url because that URL defaults to HTTP rather than HTTPS.
My hacky solution was to run a quick regex substitution and force the URL to be HTTPS:
import re
....
authorization_response = request.url
new_auth = re.sub(
"http:",
"https:",
authorization_response
)
flow.fetch_new_token(new_auth)
There are certainly better ways to accomplish this, but it works.
I ran into this error because I was using nginx in front of gunicorn/flask.
I fixed it by adding
proxy_set_header X-Forwarded-Proto $scheme;
to my nginx config, and then adding
from werkzeug.middleware.proxy_fix import ProxyFix
application.wsgi_app = ProxyFix(application.wsgi_app, x_proto=1)
to the filing being called by gunicorn.
For your specific problem on the GAE Python 3 Flex environment, you'll need to configure gunicorn to do the same thing.
App Engine terminates the HTTPS connection at the load balancer and forwards the request to your application. Most applications do not need to know if the request was sent over HTTPS or not, but applications that do need this information should configure Gunicorn to trust the App Engine proxy in their gunicorn.conf.py:
forwarded_allow_ips = '*'
secure_scheme_headers = {'X-Forwarded-Proto': 'https'}
Gunicorn will now ensure that the wsgi.url_scheme to 'https', which most web frameworks will use as indication of the request is secure. If your WSGI server or framework doesn't support this, just check the value of the X-Forwarded-Proto header manually.
https://cloud.google.com/appengine/docs/flexible/python/runtime#recommended_gunicorn_configuration

Safe way to store mysql server credentials in flask?

I was wondering about the safety of some thing in my app.py flask app. First the database, I'm using mysql and currently I am connecting to it in the following way:
# Config MySQL
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'password'
app.config['MYSQL_DB'] = 'databasename'
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
And to me this feels very weird, just putting in your password in plain text etc. I've been searching online but have not found any other way of doing this other than putting it in a seperate python file and just importing it. Which kinda feels like doing nothing at all.. Is there a better way to do this security wise?
Then the secret key I use for password encoding. Which is also just stored in plain text in my code, is there also a way to make this more secure or make it less obvious?
Thanks in advance!
The computer which runs your code needs to know the password, so you can't secure against the owner of the computer (if that's not you). But if you are having the password in the sourcecode it can easily happen that you put it into version control and if you use a public github it can easily happen that you publish your key.
As alternative you can put the password in a config file (take care to not put it into version control e.g. via .gitignore) or you can use environmental variables.
I would suggest to store the credentials in the OS environment.
app.config['MYSQL_HOST'] = os.environ.get('HOST')
app.config['MYSQL_USER'] = os.environ.get('USER')
app.config['MYSQL_PASSWORD'] = os.environ.get('PASSWORD')
app.config['MYSQL_DB'] = os.environ.get('DB')
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
It will help you to get those information from a standalone application or as a dockerized application (using docker file).
Another way is .env file
pip install python-dotenv
from dotenv import load_dotenv
load_dotenv()
class Config:
SECRET_KEY = os.getenv("SECRET_KEY")
Remember to gitignore .env as well

Flask url_for generating http URL instead of https

I am using url_for to generate a redirect URL when a user has logged out:
return redirect(url_for('.index', _external=True))
However, when I changed the page to a https connection, the url_for still gives me http.
I would like to explicitly ask url_for to add https at the beginning of a URL.
Can you point me how to change it? I looked at Flask docs, without luck.
With Flask 0.10, there will be a much better solution available than wrapping url_for. If you look at https://github.com/mitsuhiko/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7, a _scheme parameter has been added. Which means you can do the following:
url_for('secure_thingy',
_external=True,
_scheme='https',
viewarg1=1, ...)
_scheme sets the URL scheme, generating a URL like https://.. instead of http://. However, by default Flask only generates paths (without host or scheme), so you will need to include the _external=True to go from /secure_thingy to https://example.com/secure_thingy.
However, consider making your website HTTPS-only instead. It seems that you're trying to partially enforce HTTPS for only a few "secure" routes, but you can't ensure that your https-URL is not changed if the page linking to the secure page is not encrypted. This is similar to mixed content.
If you want to affect the URL scheme for all server-generated URLs (url_for and redirect), rather than having to set _scheme on every call, it seems that the "correct" answer is to use WSGI middleware, as in this snippet: http://flask.pocoo.org/snippets/35/
(This Flask bug seems to confirm that that is the preferred way.)
Basically, if your WSGI environment has environ['wsgi.url_scheme'] = 'https', then url_for will generate https: URLs.
I was getting http:// URLs from url_for because my server was deployed behind an Elastic Beanstalk load balancer, which communicates with the server in regular HTTP. My solution (specific to Elastic Beanstalk) was like this (simplified from the snippet linked above):
class ReverseProxied(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
scheme = environ.get('HTTP_X_FORWARDED_PROTO')
if scheme:
environ['wsgi.url_scheme'] = scheme
return self.app(environ, start_response)
app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)
The Elastic Beanstalk-specific part of that is HTTP_X_FORWARDED_PROTO. Other environments would have other ways of determining whether the external URL included https. If you just want to always use HTTPS, you could unconditionally set environ['wsgi.url_scheme'] = 'https'.
PREFERRED_URL_SCHEME is not the way to do this. It's ignored whenever a request is in progress.
I tried the accepted answer with an url_for arg but I found it easier to use the PREFERRED_URL_SCHEME config variable and set it to https with:
app.config.update(dict(
PREFERRED_URL_SCHEME = 'https'
))
since you don't have to add it to every url_for call.
If your are accessing your website through a reverse proxy like Nginx, then Flask correctly dectects the scheme being HTTP.
Browser -----HTTPS----> Reverse proxy -----HTTP----> Flask
The easiest solution is to configure your reverse proxy to set the X-Forwarded-Proto header. Flask will automatically detect this header and manage scheme accordingly. There is a more detailed explanation in the Flask documentation under the Proxy Setups section. For example, if you use Nginx, you will have to add the following line in your location block.
proxy_set_header X-Forwarded-Proto $scheme;
As other mentionned, if you can't change the configuration of your proxy, you can either use the werkzeug ProxyFix or build your own fix as described in the documentation:
http://flask.pocoo.org/docs/0.12/deploying/wsgi-standalone/#proxy-setups
Setting _scheme on every url_for() call is extremely tedious, and PREFERRED_URL_SCHEME doesn't seem to work. However, mucking with what the request's supposed scheme is at the WSGI level seems to successfully convince Flask to always construct HTTPS URLs:
def _force_https(app):
def wrapper(environ, start_response):
environ['wsgi.url_scheme'] = 'https'
return app(environ, start_response)
return wrapper
app = Flask(...)
app = _force_https(app)
For anyone ending up here recently there is an official uwsgi fixer for this:
https://stackoverflow.com/a/23504684/13777925
FWIW this still didn't work for me since the header wasn't being set correctly so I augmented the ReversedProxied middleware to prefer https if found thusly:
class ReverseProxied(object):
"""
Because we are reverse proxied from an aws load balancer
use environ/config to signal https
since flask ignores preferred_url_scheme in url_for calls
"""
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
# if one of x_forwarded or preferred_url is https, prefer it.
forwarded_scheme = environ.get("HTTP_X_FORWARDED_PROTO", None)
preferred_scheme = app.config.get("PREFERRED_URL_SCHEME", None)
if "https" in [forwarded_scheme, preferred_scheme]:
environ["wsgi.url_scheme"] = "https"
return self.app(environ, start_response)
Called as:
app = flask.Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)
This way if you've set the environment var "PREFERRED_URL_SCHEME" explicitly or if the nginx/etc/proxy sets the X_FORWARDED_PROTO, it does the right thing.
I personally could not fix this problem with any of the answers here, but found that simply adding --cert=adhoc to the end of the flask run command, which makes the flask app run with https, solved the issue.
flask run --host=0.0.0.0 --cert=adhoc

couchDB , python and authentication

I have installed couchDB v 0.10.0, and am attempting to talk to it via python from Couch class downloaded from couchDB wiki. Problem is:
Create database 'mydb': {'error': 'unauthorized', 'reason': 'You are not a server admin.'}
I hand edited the local.ini file to include my standard osx login and password. I now have full access via futon but no joy WRT python. Is this an http header issue?
At a a loss - thanks!
To concour David's reply, (i.e. "This is how I do it using module CouchDB 0.8 in python 2.6 with couchdb 1.0.2")
couch = couchdb.Server(couch_server)
couch.resource.credentials = (USERNAME, PASSWORD)
You can also do:
db = couchdb.Database("http://your.url/yourdb")
db.resource.http.add_credentials(username, password)
after which all your requests should work.
The Couch class in the example does not pass any authentication information to the database, so it is not a miracle that it does not allow privileged operations. So your only options are:
disable authentication completely (as you mentioned)
pass the user name and password as part of the URI
pass the user name and password as an Authorization HTTP request header
If you want to pass a user name and a password, then you will need to change the Couch class. Sending an Authorization HTTP request header is easier, since the Couch class uses the httplib.HTTPConnection class. You can add such a header next to the Accept one this way:
headers = {
"Accept": "application/json",
"Authorization": "Basic " + 'username:password'.encode('base64')[:-1]}
Same for the other HTTP request methods.
The documentation on the basic authentication is here:
http://books.couchdb.org/relax/reference/security
Just pass it as part of the URI...python-couchdb will parse the user/pass out and use them:
http://user:pass#localhost:5984
Above are all nice; but I've found that for oauth validation methods versus basic auth, this works really well:
from couchdb import Server, Session
auth = Session()
auth.name = USERNAME
auth.password = PASSWORD
s = Server('http://localhost:5984/', session=auth)
db = s['dbname']
Note: This will not work with basic authentication; in such a case, fviktor has what I consider to be the best answer. You might also look into the security reference material he linked to if you're interested in persistent auth sessions.
There are several patches for python-couchdb that enable authentication. The code probably will be included in Version 0.7 but until then you can usr teh fork at http://github.com/mdornseif/couchdb-python - it allows you to use http://user:pass#127.0.0.1:5984/ type URLs.
http://blogs.23.nu/c0re/2009/12/running-a-couchdb-cluster-on-amazon-ec2/ (at the bottom) shows how to use CouchDB passwords.

Categories

Resources