How to make a page not directly accessible in Flask? - python

I'm developing a web application using flask microframework.
I'd like to have a view that is accessible only when it is redirected from another view and not directly from users.
To make it more clear:
#app.route('/', methods=('GET', 'POST'))
#app.route('/home', methods=('GET', 'POST'))
def home():
#Some code
return redirect(url_for('inProgress', parameter)
#app.route('/path/<parameter>')
def inProgress(parameter):
return render_template(...)
The view inProgress should be accessible only when it's "called" from the home view.
Is it possible?

Before you issue the redirect, set a flag in the session object. The "inprogress" view should check that flag. if it's set, groovy, render the page. If it's not, then redirect them to another page (and flash a warning about trying to access that page, optionally).

Related

Why does inserting a function inside a route differ from inserting the code inside the function in Flask?

I am trying to make a web app with a login system. I want to make it so that a user can't access certain pages unless they are logged in.
What I want is that when you click to go to another page while not logged in, you get redirected to the login page and on it you get a message flash.
This is what works:
#app.route("/home", methods=['GET', 'POST'])
def home():
#some form
if not current_user.is_authenticated:
flash('You need to be logged in to access this page.', 'info')
return redirect(url_for('login'))
#rest of the code
But I would need to add all of this to other routes as well. So I created the function and added it to the routes instead:
#app.route("/home", methods=['GET', 'POST'])
def home():
#some form
require_login()
#rest of the code
def require_login():
if not current_user.is_authenticated:
flash('You need to be logged in to access this page.', 'info')
return redirect(url_for('login'))
But this does not work as I want it to. It instead redirects to the home page and then flashes the message. How do I fix this?
The problem is that the redirect(...) doesn't itself do the redirect. It returns a value to Flask telling flask that it needs to do the redirect.
In your first piece of code, you handle this correctly. You take the result of redirect(...) and return it to flask. In your second piece of code, you take the redirection returned by require_login and ignore it in home.
You might try something like:
value = require_login()
if value:
return value
You need to return the function
return require_login()
But be aware, after that u cant have code. You should create an decorator for this. There are examples online just Google "flask authorized decorator"
Your Advantage of this that u can move the auth Logic out of the Views and you can easily decorate your Views and dont have this Stuff in each View/route

login_required decorator not redirecting to correct endpoint

Using Flask-security extension, I was trying to protect some views, for example:
from flask_security import login_required
#auth.route('/signin', methods=['GET', 'POST'])
def signin():
#...
#...
#auth.route('/change-password', methods=['GET', 'POST'])
#login_required
def change_password():
#...
With the following config.py for Flask-Security:
SECURITY_URL_PREFIX = '/auth'
SECURITY_LOGOUT_URL = '/signout'
SECURITY_UNAUTHORIZED_VIEW = '/signin'
And yet, when I try to access a protected view, I get the following werkzeug.routing.BuildError message:
werkzeug.routing.BuildError: Could not build url for endpoint 'auth.login'.
Did you mean 'auth.signin' instead?
What could be the reason behind this error?
Flask-security uses Flask-login's login_required decorator and as per documentation:
flask_login.login_required(func)
If you decorate a view with this, it will ensure that the current
user is logged in and authenticated before calling the actual view.
(If they are not, it calls the LoginManager.unauthorized callback.)
While one can use LoginManager.unauthorized callback to setup a response:
unauthorized_handler(callback)
This will set the callback for the unauthorized method, which among
other things is used by login_required. It takes no arguments,
and should return a response to be sent to the user instead of their
normal view.
So, I ended up defining the callback myself:
#app.login_manager.unauthorized_handler
def unauth_handler():
if request.is_xhr:
return jsonify(success=False,
data={'login_required': True},
message='Authorize please to access this page.'), 401
else:
return redirect(url_for('auth.signin'))

Flask View Testing General and with Subdomains

I'm working on testing the views that I have for a Flask application and I'm having some issues. I use a csrf token for my form submissions and I also use subdomains for logging in and I was wondering how to test for that as well. If anyone has any experience or could give me an example or some direction, I would appreciate it because I've already gone through all of the guides and documentation I could fine.
Below is what I'm trying to test and the test I'm running.
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
try:
#acts as redirect, but with subdomain
return redirect_subdomain('login', request.url, request.form['subdomain'])
except OrganizationDoesNotExistException:
return render_template('login.html', subdomain=False)
return render_template('login.html', subdomain=False)
These are the tests I'm running just to get started. It only accepts a subdomain (organization) that will be used to redirect the user to the subdomain specific login, which works, I just want to know how to write tests for the future:
def test_login(self):
rv = self.login('test')
print(rv.data.decode('utf-8'))
pass
def login(self, organization):
return self.app.post('/login', data=dict(
organization=organization
), follow_redirects=True)
I know these are basic tests and don't really test much, but I still get errors and I want to know how to go about testing views in general and when there are subdomains involved, like in my case.
In order to be able to create unittests for a flask app using subdomain routing, the test_client() function can be used. For this to work, you will need to set the allow_subdomain_redirects attribute on the test_client object to True.
Here is an example test case for a flask app using subdomain routing.
import yourapp
import unittest
class FlaskTestCase(unittest.TestCase):
def setUp(self):
self.app = yourapp.app.test_client()
self.app.allow_subdomain_redirects = True
def test_request(self):
r = self.app.get('http://subdomain.domain.com/route/', query_string='key=value')
self.assertEquals(r.data, known_result)

Redirect to one Flask view from another

I have a view that is accessed by clicking a link on the index page. The view will perform an action and should then show the index page again. My view does that, but the url is still the url of the action view, not the index view. How can I redirect back to the index view?
#app.route('/')
def index():
return render_template('index.html')
#app.route('/u1/')
def u1():
# do something
return render_template('index.html')
Use url_for to build a url to another endpoint in the app. Use redirect to create a redirect response with that url.
from flask import url_for, redirect
#app.route('/u1/')
def u1():
# do something
return redirect(url_for('index'))

How To Handle Python Flask Redirect

I want to know what the best way is to serve up a redirect in Flask. I have a delete button which looks like this:
<button class="btn btn-danger btn-mini" type="button">Delete</button>
Which calls this app.route:
#app.route('/elastic_ips/<region>/delete/<ip>')
def delete_elastic_ip(region=None,ip=None):
creds = config.get_ec2_conf()
conn = connect_to_region(region, aws_access_key_id=creds['AWS_ACCESS_KEY_ID'], aws_secret_access_key=creds['AWS_SECRET_ACCESS_KEY'])
ip = ip.encode('ascii')
elis = conn.get_all_addresses(addresses=ip)
for eli in elis:
result = []
r = eli.release()
result.append(r)
return Response(json.dumps(result), mimetype='application/json')
I rather not return the result as json. I'm not sure what the "proper" way to return to the page with the delete button. Either I can put in an HTML page that just does a redirect to the refer, or is there a built in way in Flask to have return be an app.route?
Well if you want to return the url of the delete_elastic_ip, it is easy to do with url_for function (more about this function)
Don't know if this endpoint is in some blueprint, but if not, this is simple as that:
from flask import url_for, redirect
.... your code ...
return url_for('delete_elastic_ip', region=None, ip=None)
You can replace Nones also with the values you need of course :) And this will return you the url to the endpoint. Btw this is also a way to go with the urls in templates. Do not hardcode them, use url_for function in jinja templates to generate urls to the views for you. The function is available as a standart global variable in templates (more)
Also if you want just to redirect directly to some other endpoint and do not return anything, there is a function redirect in flask. Use it combined with url_for and you are good to go ;)
from flask import url_for, redirect
... your code...
return redirect(url_for('delete_elastic_ip', region=None, ip=None))
It will refresh the page, so not the best way for ajax redirect though if you want that. For ajax, just return json with the url_for result and do the stuff with it.
Here is another method using render_template
app.py code
from flask import Flask, request,render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/v_timestamp')
def v_timestamp():
return render_template('v_timestamp.html')
then can redirect to v_timestamp page. If you want this to be done via a button click event. Inside template folder, in your v_timestamp.html have this bit of code
<p align="center">v timestamp</button></p>
define button element and an a href inside the same paragraph element, in my case a href v_timestamp means v_timetsamp.html write the respective .html page you want to redirect towards.
File structure
-app.py
-templates
index.html
v_timestamp.html

Categories