How to call funcions with basic authentication enabled in Python - python

I am using basic authentication with the decorator in a flask application.
The code looks like:
from flask import Flask, Response, request
from functools import wraps
app = Flask(__name__)
app.config.from_object('settings')
def valid_credentials(username, password):
return username == app.config['USER'] and password == app.config['PASS']
def authenticate(f):
#wraps(f)
def wrapper(*args, **kwargs):
auth = request.authorization
if not auth.username or not auth.password or not valid_credentials(auth.username, auth.password):
return Response('Login!', 401, {'WWW-Authenticate': 'Basic realm="Login!"'})
return f(*args, **kwargs)
return wrapper
#app.route('/')
def index():
return 'Hello, world!'
#app.route('/secure')
#authenticate
def secure():
return 'Secure!'
#app.route('/check')
#authenticate
def check():
secure()
return 'checked'
if __name__ == '__main__':
app.run(debug=True)
But I am not able to call the secure function from check function due to the authentication. Now is it possible to call the function in the current scenario?

The usual method is to keep helper methods separate from views. For example:
def _secure():
return 'Secure!'
#app.route('/secure')
#authenticate
def secure():
return _secure()
You can then reuse the helper method (_secure()) from other places. Since it doesn't have a route associated it is not possible for a visitor to run it without authentication.
It is also a good idea to keep these helper methods in a separate module (such as helpers.py or utils.py).

Related

Flask: how to register a wrapper to all methods

I've been moving from bottle to flask. I'm the type of person that prefers writing my own code instead of downloading packages from the internet if I the code needed is 20 lines or less. Take for example support for Basic authentication protocol. In bottle I could write:
def allow_anonymous():
"""assign a _allow_anonymous flag to functions not requiring authentication"""
def wrapper(fn):
fn._allow_anonymous = True
return fn
return wrapper
def auth_middleware(fn):
"""perform authentication (pre-req)"""
def wrapper(*a, **ka):
# if the allow_anonymous annotation is set then bypass this auth
if hasattr(fn, '_allow_anonymous') and fn._allow_anonymous:
return fn(*a, **ka)
user, password = request.auth or (None, None)
if user is None or not check(user, password):
err = HTTPError(401, text)
err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
return err
return fn(*a, **ka)
return wrapper
...
app = Bottle()
app.install(middleware.auth_middleware)
The above code gave me full support for basic auth protocol for all methods unless explicitly decorated with the #allow_anonymous wrapper. I'm just a beginner with flask. I'm having a hard time accomplishing the bottle-compatible code above in flask without adding dependencies on more python packages or excessive boiler-plate. How is this handled directly and clearly in flask?
You can definitely some of the functionality of flask-httpauth yourself, if you wish :-P
I would think you will need to play some before_request games (not very beautiful), or alternatively call flask's add_url_rule with a decorated method for each api endpoint (or have a route decorator of your own that will do this). The add_url_rule gets a view function that is usually your api endpoint handler, but in your case, will be a wrapped method in a manner very much like the one you gave in the post (auth_middleware).
The gist of it:
from flask import Flask, make_response, request
app = Flask(__name__)
def view_wrapper(fn):
"""
Create a wrapped view function that checks user authorization
"""
def protected_view(*a, **ka):
# if the allow_anonymous annotation is set then bypass this auth
if hasattr(fn, '_allow_anonymous') and fn._allow_anonymous:
return fn(*a, **ka)
# consult werkzeug's authorization mixin
user, password = (request.authorization.username, request.authorization.password) if request.authorization else (None, None)
if user is None or not check(user, password):
err_response = make_response(text, 401)
err_response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm
return err_response
return fn(*a, **ka)
return protected_view
# An endpoint
def hello():
return 'hello there'
app.add_url_rule('/', 'hello', view_wrapper(hello))
Of course, this can (and should) be further enhanced with Blueprints, etc.
Note #1: this is cribbed from separate answers in SO.
Note #2: this doesnt use blueprints. Again, I'm new to flask and I appreciate that blueprints will help the app scale but one step at a time...
def allow_anonymous(decorated_function):
decorated_function.is_public = True
return decorated_function
#app.before_request
def auth_middleware():
fn = app.view_functions[request.endpoint]
if hasattr(fn, 'allow_anonymous') and fn.allow_anonymous:
# anonymous permitted
return
elif my_custom_authentication():
# anonymous not permitted authentication succeeded
return
else:
# anonymous not permitted authentication failed
err_response = make_response(text, 401)
err_response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm
return err_response
#app.route('/public_thing')
#allow_anonymous
def public_thing():
return 'yo'
#app.route('/regular_thing')
def regular_thing():
return 'if you can read this youre authenticated.'

How to apply decorator to all blueprint urls in flask

I have a blueprint and some url functions,
admin_bp = Blueprint('admin', __name__)
#admin_bp.route('/dashboard', methods=['GET', ])
#flask_login.login_required
def dashboard():
context = {}
page = 'admin/dashboard.html'
return render_template(page, **context)
#admin_bp.route('/deny', methods=['GET', ])
#flask_login.login_required
def deny():
return 'hey bro you dont belong here'
I don't want to copy paste #flask_login.login_required decorator for all url functions under this blueprint. Is there a better way that I can apply decorator for all blueprint urls?
You can add before_request() as a function that will run before each request in a view.
You will then want to add decorators to inject additional functionality to the before_request function. You will want to import the login_required decorator to ensure each endpoint requires a logged in user. This decorator is part of the flask_login library.
Since it looks like your views are part of an admin, I'd also recommend adding a custom decorator to your before_request function with something like #role_required('admin'). The functionality for that decorator will live somewhere else and be imported.
#admin_bp.before_request
#login_required
def before_request():
""" Protect all of the admin endpoints. """
pass
Subclass Blueprint and override the route method.
import flask
class MyBlueprint(flask.Blueprint):
def route(self, rule, **options):
def decorator(f):
# these lines are copied from flask.Blueprint.route
endpoint = options.pop("endpoint", f.__name__)
self.add_url_rule(rule, endpoint, f, **options)
# At this point flask.Blueprint.route simply returns f.
# But you can nest a decorator.
def inner(*args, **kwargs):
# stuff you want to do before each request goes here
try:
result = f(*args, **kwargs)
# stuff you want to do on successful responses (probing status, headers, etc.) goes here
except Exception as e:
# stuff you want to do on error responses goes here
raise
return inner
Now use the new subclass in your blueprints:
-v1_blueprint = Blueprint('v1', __name__)
+v1_blueprint = MyBlueprint('v1', __name__)
No changes needed to individual routes.
The drawback of this approach is that it copies code from inside Flask. If the implementation of flask.Blueprint.route were to change in a future version, you'd need to sync MyBlueprint with it when you upgrade Flask.
How about checking the user first:
from flask.ext.login import current_user
#admin_bp.before_request
def check_user():
if not current_user.is_authenticated():
abort(401)
# your other functions without `#flask_login.login_required`

How to implement login required decorator in Flask

I have 2 Flask apps (different projects) that work together . One implements some API which uses tokens for auth. The second one consumes the API and makes a web interface for it. Now I have a login function that sends the username and password to the API, and if correct, gets the auth token in return. Once I have the token, I save it to the session of the user and the user should now be considered as logged in/ autheticated. How can I implement the login_required decorator for such a case.
Here is my login function -
def login(self):
response = make_request(BASE_URL + 'login/', clean_data(self.data))
if response.status_code == 200:
session['auth_token'] = response.json().get('auth_token')
return True
return False
How can I make the login_required decorator?
Also I am using Redis to store sessions if that matters.
Have a look at the official flask docs regarding decorators:
https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/ or the python docs https://www.python.org/dev/peps/pep-0318/ as well.
Your decorator should look something like:
from functools import wraps
from flask import abort
import jwt
def authorize(f):
#wraps(f)
def decorated_function(*args, **kws):
if not 'Authorization' in request.headers:
abort(401)
user = None
data = request.headers['Authorization'].encode('ascii','ignore')
token = str.replace(str(data), 'Bearer ','')
try:
user = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])['sub']
except:
abort(401)
return f(user, *args, **kws)
return decorated_function
... and then in your app.py you may have:
#app.route('/api/game', methods=['POST'])
#authorize
def create(user):
data = json.loads(request.data)
....
In this particular case I have used JWT as token and your token can be different respectively the decoding of the token can be your custom implementation, but the basic mechanisms are pretty much as on the example above.
I would place the following decorator function in somewhere common
def validate_api_token(validation_func):
def decorator(f):
#wraps(f)
def decorated_function(*args, **kws):
api_token = request.headers.get('Authorization')
is_valid_api_token = validation_func(api_token)
if is_valid_api_token:
return f(*args, **kws)
return 'Invalid API Token', 401
return decorated_function
return decorator
For small POC flask apps, if you're ok with storing the tokens in a non-versioned file, the following can work:
# tokens are read from a non-versioned `.tokens` file and loaded into a set
api_tokens = load_api_tokens()
def simple_api_token_validation(api_token):
return api_token in api_tokens
#app.route("/v1/my/secret/function", methods=['POST'])
#validate_api_token(simple_api_token_validation)
def my_secret_function():
body = request.get_json()
# ...
Another simple option is to query against a database (e.g. redis):
redis_session = Redis(host=REDIS_HOST, password=REDIS_PASSWORD)
def redis_api_token_validation(api_token):
if not api_token:
return False
api_token_hash = hashlib.sha256(api_token.encode()).hexdigest()
return redis_session.exists(f'api:tokens:{api_token_hash}')
#app.route("/v1/my/secret/function", methods=['POST'])
#validate_api_token(redis_api_token_validation)
def my_secret_function():
body = request.get_json()
# ...
Best IMO as #Velin answered is to use jwt to validate the token
Given that each subsequent request will contain the API token, the decorator should do the following
Accept a generic request. You can use *args and **kargs for that
Extract the token from the header and compare it with the token stored in db (not Redis, but wherever the token generated is stored in the backend)
If authenticated, the *args and **kargs should be passed on to the decorated function
The output of the decorated function should then be returned as is
If the authentication failed, an error message should be returned.
For explanation on decorators, check out this link:
http://thecodeship.com/patterns/guide-to-python-function-decorators/

How to mock HTTP authentication in Flask for testing?

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)

Flask: Set session variable from URL Param

I have a website that needs to be rebranded depending on the URL that a visitor comes in on. For the most part, the content is the same but the CSS is different. I'm brand new to flask and relatively new to session cookies, but I think the best way to do this is to create a session cookie containing a "client" session variable. Then, depending on the client (brand), I can append a specific css wrapper to a template.
How can I access URL params and set one of the param values to a session variable? For example, if a visitor comes in on www.example.com/index?client=brand1, then I'd like to set session['client'] = brand1.
My app.py file:
import os
import json
from flask import Flask, session, request, render_template
app = Flask(__name__)
# Generate a secret random key for the session
app.secret_key = os.urandom(24)
#app.route('/')
def index():
session['client'] =
return render_template('index.html')
#app.route('/edc')
def edc():
return render_template('pages/edc.html')
#app.route('/success')
def success():
return render_template('success.html')
#app.route('/contact')
def contact():
return render_template('pages/contact.html')
#app.route('/privacy')
def privacy():
return render_template('pages/privacy.html')
#app.route('/license')
def license():
return render_template('pages/license.html')
#app.route('/install')
def install():
return render_template('pages/install.html')
#app.route('/uninstall')
def uninstall():
return render_template('pages/uninstall.html')
if __name__ == '__main__':
app.run(debug=True)
You could do so in a #flask.before_request decorated function:
#app.before_request
def set_client_session():
if 'client' in request.args:
session['client'] = request.args['client']
set_client_session will be called on each incoming request.

Categories

Resources