I am running a Flask web app and using Apache basic authentication(with .htaccess and .htpasswd files) to password protect it. I want to password protect only one webpage in the app. When I password protect the html file for the webpage there is no effect and the webpage is still not password protected. Could this be because it is my python file that is calling the html file using render_template? I'm not sure how to fix this issue.
You need to restrict access to your endpoint. This snippet should get you started down the right path.
from functools import wraps
from flask import request, Response
def check_auth(username, password):
"""This function is called to check if a username /
password combination is valid.
"""
return username == 'admin' and password == 'secret'
def authenticate():
"""Sends a 401 response that enables basic auth"""
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
With this, you could decorate any endpoint you want to restrict with #requires_auth.
#app.route('/secret-page')
#requires_auth
def secret_page():
return render_template('secret_page.html')
Related
I've inherited a custom built web app built on python that currently utilizes our older LDAP instance that we are retiring, and I need to change it to use our MSAD LDAP schema for authorization instead. Anonymous bind is disabled in the MSAD, so I need to add a bind user/pass to connect to the MSAD LDAP instance, but don't know where/how. Below is the excerpt from the custom config.ini file and the authorization.py script
<config.ini>
ldap_server = ldap://ldap3.internaldomain.local
bind_organisation = cn=Company,ou=People
domain_components = dc=internaldomain,dc=local
allowed_groups =
allowed_users =
bind_timeout = 10.0
<authorization.py>
from flask import request, Response, render_template
from ldap_logging import LDAPLoggingClassWrapper
from global_config import config
import ldap
def check_auth(username, password):
try:
ldap_client = LDAPLoggingClassWrapper(ldap.initialize(config.get("LDAP", "ldap_server")))
ldap_client.set_option(ldap.OPT_NETWORK_TIMEOUT, config.getfloat("LDAP", "bind_timeout"), log_id=username)
basedn = "uid=%s,%s,%s" % (username, config.get("LDAP", "bind_organisation"), config.get("LDAP", "domain_components"))
ldap_client.simple_bind_s(basedn,password, log_id=username)
ldap_client.unbind_s(log_id=username)
return True
except (ldap.INVALID_CREDENTIALS, ldap.NO_SUCH_OBJECT) as e:
return False
def authenticate():
"""Sends a 401 response that enables basic auth"""
return Response(
'Authentication failed, you must sign in using your domain credentials to use this app.\n'
'Please contact ITS if you feel your details were correct and that this message is in error.', 401,
{'WWW-Authenticate': 'Basic realm="Please login with your domain credentials"'})
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
try:
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
except Exception as e:
return render_template('server-down.html',
message = e
)
return f(*args, **kwargs)
return decorated
I have a simple bokeh server application and I want to expose it on a Linux-based Azure node. The server is there up and running.
My question is: how to protect the content by username and password? I do not need necessarily authentication of users.
My ideas so far (not tried, may not work)
To create an extra bokeh server page with a text field.
On the callback for a button, to add the test if the password fits. If it does, to redirect to the original server page. Otherwise, inform the user about wrong credentials.
You can try to disable generation of session id's by bokeh server and generate them by external application only after user authentication:
(Based on this part of bokeh documentation)
Generate secret key with bokeh secret command:
$ bokeh secret
oIWDL7DVYCaBJG9eYQ2Wvf2f2uhOAIM8xNS8Kds3eizV
Set BOKEH_SECRET_KEY environment variable to generated value;
$ export BOKEH_SECRET_KEY=oIWDL7DVYCaBJG9eYQ2Wvf2f2uhOAIM8xNS8Kds3eizV
Set another environment variable:
$ export BOKEH_SIGN_SESSIONS=True
Run bokeh server with --session-ids external-signed argument:
$ bokeh serve myApp --session-ids external-signed
In this mode user should provide valid (signed) session id to access bokeh server.
Run simple external process to ask users for login and password and generate id's for them.
Here is the example based on snippet from Flask documentation:
from functools import wraps
from flask import request, Response, redirect, Flask
from bokeh.util import session_id
app = Flask(__name__)
def check_auth(username, password):
return username == 'valid_user' and password == 'valid_password'
def authenticate():
"""Sends a 401 response that enables basic auth"""
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
#app.route('/')
#requires_auth
def redirect_to_bokeh():
s_id = session_id.generate_session_id()
return redirect("http://<bokeh-server-addr>:<port>/?bokeh-session-id={}".format(s_id), code=302)
if __name__ == "__main__":
app.run()
Now to access bokeh server user should go to Flask application and specify login and password.
Right now, my webapp has a full auth system implemented through Flask-Login. But say that I have this controller:
#app.route("/search_query")
#login_required
def search_query():
query = request.args.get("query")
return jsonify(search(query))
This works great when you login to the page and make searches against it, if you're authenticated it goes through, otherwise it routes you back to the login page (through an unauthorized_handler function).
#lm.unauthorized_handler
def unauthorized():
return redirect(url_for("login"))
The problem is, I want to be able to use some form of simple HTTP authentication for this without having two separate auth systems so that I can make REST API calls against it. In short, I want to avoid having to use both Flask-Login and HTTPBasicAuth, and just use Flask-Login to achieve simple authentication, is this possible through Flask-Login?
Use the flask-login's request_loader to load from request auth header. You don't need to implement the decorator yourself, and current_user will point to the user loaded.
Example:
login_manager = LoginManager()
#login_manager.request_loader
def load_user_from_header():
auth = request.authorization
if not auth:
return None
user = User.verify_auth(auth.username, auth.password):
if not user:
abort(401)
return user
With that, you can mark a view as login required by:
#app.route('/user/info')
#login_required
def user_info():
return render_template('user_info.html', current_user.name)
If you looking for users to authenticate and make REST API calls they are going to have to send their credentials with every api call.
What you could do is a new decorator that checks for the auth header and authenticate the user.
Please refer to the entire app that I've posted on codeshare for more info:
http://codeshare.io/bByyF
Example using basic auth:
def load_user_from_request(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if current_user.is_authenticated:
# allow user through
return f(*args, **kwargs)
if auth and check_auth(auth.username, auth.password):
return f(*args, **kwargs)
else:
return bad_auth()
return decorated
With that code you can now decorate your views to check for the header.
#app.route("/search_query")
#load_user_from_request
def search_query():
query = request.args.get("query")
return jsonify(search(query))
you can curl my example app like this:
curl --user admin#admin.com:secret http://0.0.0.05000/success
I'm trying to allow users to login to my Flask app using their accounts from a separate web service. I can contact the api of this web service and receive a security token. How do I use this token to authenticate users so that they have access to restricted views?
I don't need to save users into my own database. I only want to authenticate them for a session. I believe this can be done using Flask-Security and the #auth_token_required decorator but the documentation is not very detailed and I'm not sure how to implement this.
EDIT:
Here's a code example:
#main.route("/login", methods=["GET", "POST"])
def login():
payload = {"User": "john", "Password": "password123"}
url = "http://webserviceexample/api/login"
headers = {'content-type': 'application/json'})
#login to web service
r = requests.post(url, headers=headers, json=payload)
response = r.json()
if (r.status_code is 200):
token = response['user']['authentication_token']
# allow user into protected view
return render_template("login.html", form=form)
#main.route('/protected')
#auth_token_required
def protected():
return render_template('protected.html')
Hey there Amedrikaner!
It looks like your use-case is simple enough that we can implement this ourselves. In the code below, I'll be storing your token in the users session and checking in a new wrapper. Let's get started by making our own wrapper, I usually just put these in a wrappers.py file but can you can place it where you like.
def require_api_token(func):
#wraps(func)
def check_token(*args, **kwargs):
# Check to see if it's in their session
if 'api_session_token' not in session:
# If it isn't return our access denied message (you can also return a redirect or render_template)
return Response("Access denied")
# Otherwise just send them where they wanted to go
return func(*args, **kwargs)
return check_token
Cool!
Now we've got our wrapper implemented we can just save their token to the session. Super simple. Let's modify your function...
#main.route("/login", methods=["GET", "POST"])
def login():
payload = {"User": "john", "Password": "password123"}
url = "http://webserviceexample/api/login"
headers = {'content-type': 'application/json'})
#login to web service
r = requests.post(url, headers=headers, json=payload)
response = r.json()
if (r.status_code is 200):
token = response['user']['authentication_token']
# Move the import to the top of your file!
from flask import session
# Put it in the session
session['api_session_token'] = token
# allow user into protected view
return render_template("login.html", form=form)
Now you can check the protected views using the #require_api_token wrapper, like this...
#main.route('/super_secret')
#require_api_token
def super_secret():
return "Sssshhh, this is a secret"
EDIT
Woah! I forgot to mention you need to set your SECRET_KEY in your apps config.
Just a config.py file with SECRET_KEY="SOME_RANDOM_STRING" will do. Then load it with...
main.config.from_object(config)
I'm just put HTTP authentication for my Flask application and my test is broken. How do I mock request.authentication to make the test pass again?
Here's my code.
server_tests.py
def test_index(self):
res = self.app.get('/')
self.assertTrue('<form' in res.data)
self.assertTrue('action="/upload"' in res.data)
self.assertEquals(200, res.status_code)
server.py
def check_auth(username, password):
"""This function is called to check if a username /
password combination is valid.
"""
return username == 'fusiontv' and password == 'fusiontv'
def authenticate():
"""Sends a 401 response that enables basic auth"""
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
#app.route("/")
#requires_auth
def index():
return render_template('index.html')
Referring to How do I mock dependencies of the views module of my Flask application in flask-testing?, you can mock it via the import chain.
Assuming server_tests imports application imports server, you probably want something like:
server_tests.py
def setUp(self):
application.server.request.authorization = MagicMock(return_value=True)