I am working on a small Pyramid web application (Python). One requirement is that the user resets the password after a fixed period of time, say, once a year. (I don't consider this requirement to help in terms of security, however, the customer's internal policy requires it.)
The password update should take place after a successful login. It is easy to add the logic to display the update password dialog after the login, but it is also possible that the user just navigates away using a bookmarked URL or the like.
What I would need to do is to overwrite the routing rules based on a property of the logged in user. However, the routing configuration seems to be just a set of static rules in Pyramid.
Is there a way to overwrite the routing rules based on the user's state, so that all requests to other views are forwarded/redirected to the password update dialog?
Assuming you have class based views and that you have set a session variable is_password_recent which is True if less than one year ago, you could put the following into each class's __init__():
class UserViews:
def __init__(self, request):
self.request = request
if not request.session["is_password_recent"]:
raise HTTPFound(
location=request.route_url(
"password_renew", _query={"return": request.path}
)
)
You could probably DRY it, too, by putting the logic into a method in a utils module and import it.
I found a solution using Pyramid events:
from pyramid.events import NewRequest, subscriber
#subscriber(NewRequest)
def intercept(event):
# use event.request to check if an interception/redirect is required
raise HTTPFound(location='route_to_form')
It is important to check the incoming request thoroughly. For example, paths including the route being used further in the process must be excluded, otherwise the application ends up in an infinite loop.
Related
I have a flask route this like:
#app.route('/product/<string:slug>')
def product(slug):
# some codes...
return render_template('product.html', product=product)
Different clients use the project (different websites, same infrastructure). And every customer wants the product URL to be different. Like;
asite.com/product-nike-shoe-323
bsite.com/nike-shoe
csite.com/product/nike-shoue
vs. vs
How do I set the URL structure to come from the database?
like:
url_config = "product-{product_name}-{product_id}"
or
url_config = "product-{product_id}"
Note: please without redirect.
I’m not 100% clear on what you refer to when you say “database” here. From context I infer you may be talking about the Flask Config object. If that’s the case, you can simply register your view function right after setting up the app configuration. Just call app.add_url_rule() to register the URL pattern from the configuration to point to your view function of choice.
If, however, you are talking about a SQL or NoSQL database and you have built a web UI to register routes, then don’t dispair. Flask routes can be registered with the app object at any point. There is no point in the Flask app lifecycle after which you can no longer register a route!
All that registering a route does, is create a mapping between a URL template and endpoint name, an opaque string. Most of the time, you also register a function to be called to handle the specific endpoint, and most of the time, Flask infers the endpoint name from the function. Once registered in the mapping any next incoming request can be routed to the function for the given endpoint.
So, Flask keeps two maps:
from url route -> endpoint name: Flask.url_map
from endpoint name -> function: Flask.view_functions
That said, there is API for removing or changing url registrations (other than restarting your server, of course). You can’t change the url route, the endpoint name for a given route or what endpoint maps to what function. The intention of the framework is that you register your routes early on when first starting your server, via code that runs directly when imported or when bound to the app (Blueprints and Flask extensions do the latter). The majority of Flask apps will create their Flask instance, register all their routes and extensions, then pass the instance to the WSGI server for request dispatch, and that’s it. But there is nothing in the implementation stopping you from registering more routes after this point.
If you want to register URL routes from database information, you have to take care of at least the following two things:
Register existing routes at start-up. Once you have a connection to your database established, retrieve the existing routes and register them.
If a new entry is added to the database, register a new route.
First of all: if I were to implement something like this I’d use one view function. You can always figure out what url rule was matched and what endpoint name this mapped to by looking at request.url_rule and request.endpoint, respectively.
Next, I’d explicitly generate endpoint names for each url rule from the database. Use the primary key in the name; you want to be able to find the database row from the endpoint name and vice versa. How you do this is up to you; let’s assume you know how to do this, and you have two functions for this named pk_from_endpoint() and endpoint_from_pk().
Your view function can then look like this:
from flask import request
def product_request(**kwargs):
key = pk_from_endpoint(request.endpoint)
row = database_query(key)
# … process request
You register a route for a given database row with:
app.add_url_route(row.url_config, endpoint_from_pk(row.id), product_request)
As mentioned, you can’t change URL registrations. But, as long as changes to these URLs are infrequent you could always add new registrations and for any old entries use abort(404) to return a 404 Not Found response.
That's not possible with Flask's routing system. The URL map is supposed to be defined at startup and not change after that.
However, if you have some specific path where you need the dynamic parts (e.g. /product/WHATEVER), then you can register a route for /product/<slug> and query the database within your view function.
That said, if you REALLY want URL rules in a DB, and do not mind connecting to your database during startup (usually that's ugly), then nothing stop you from querying the database at startup time and define the URL rules based on data from the DB. Quite ugly, but doable.
Example:
with app.app_context():
url_map = {u.endpoint: u.rule for u in URLRules.query}
#app.route(url_map['foo'])
def foo():
...
Of course doing so makes it harder to nicely structure your app unless you use app.add_url_rule() for all the endpoints in a single place instead of the #app.route() decorators.
Likewise with blueprints of course.
I’m new to django programming, not python, and could do with a hand.
I am attempting to make a website exclusive to a certain device. I have created a disallow page accessible by '/disallow/'. How do I go about running os/browser checks that then redirect in the event the os/browser is not on the verified list.
I know the information I am wanting to check will be in the request and I can use
request.META['HTTP_USER_AGENT']
However where do I write any logic required and how could I apply this to any page the user tries to access.
Any help would really be appreciated
Ed
You'll want a Django middleware. It sits in front of all response handlers, so it can capture every request and take any action on it that you require.
Simple example (untested), a function that returns a function:
your_app/middleware.py
def restrict_middleware(get_response):
def middleware(request):
if request.META['HTTP_USER_AGENT'] == ...:
return redirect('/disallow/')
else:
return get_response(request)
return middleware
Then register your_app.middleware.restrict_middleware in the MIDDLEWARE array in your settings file.
Note that such browser/OS detection relies on what the browser/OS itself is claiming that it is, and can easily be spoofed on the client. So don't use this for any security purposes.
I have a flask running at domain.com
I also have another flask instance on another server running at username.domain.com
Normally user logs in through domain.com
However, for paying users they are suppose to login at username.domain.com
Using flask, how can I make sure that sessions are shared between domain.com and username.domain.com while ensuring that they will only have access to the specifically matching username.domain.com ?
I am concerned about security here.
EDIT:
Later, after reading your full question I noticed the original answer is not what you're looking for.
I've left the original at the bottom of this answer for Googlers, but the revised version is below.
Cookies are automatically sent to subdomains on a domain (in most modern browsers the domain name must contain a period (indicating a TLD) for this behavior to occur). The authentication will need to happen as a pre-processor, and your session will need to be managed from a centralised source. Let's walk through it.
To confirm, I'll proceed assuming (from what you've told me) your setup is as follows:
SERVER 1:
Flask app for domain.com
SERVER 2:
Flask app for user profiles at username.domain.com
A problem that first must be overcome is storing the sessions in a location that is accessible to both servers. Since by default sessions are stored on disk (and both servers obviously don't share the same hard drive), we'll need to do some modifications to both the existing setup and the new Flask app for user profiles.
Step one is to choose where to store your sessions, a database powered by a DBMS such as MySQL, Postgres, etc. is a common choice, but people also often choose to put them somewhere more ephemeral such as Memcachd or Redis for example.
The short version for choosing between these two starkly different systems breaks down to the following:
Database
Databases are readily available
It's likely you already have a database implemented
Developers usually have a pre-existing knowledge of their chosen database
Memory (Redis/Memchachd/etc.)
Considerably faster
Systems often offer basic self-management of data
Doesn't incur extra load on existing database
You can find some examples database sessions in flask here and here.
While Redis would be more difficult to setup depending on each users level of experience, it would be the option I recommend. You can see an example of doing this here.
The rest I think is covered in the original answer, part of which demonstrates the matching of username to database record (the larger code block).
Old solution for a single Flask app
Firstly, you'll have to setup Flask to handle subdomains, this is as easy as specifying a new variable name in your config file. For example, if your domain was example.com you would append the following to your Flask configuration.
SERVER_NAME = "example.com"
You can read more about this option here.
Something quick here to note is that this will be extremely difficult (if not impossible) to test if you're just working off of localhost. As mentioned above, browsers often won't bother to send cookies to subdomains of a domain without dots in the name (a TLD). Localhost also isn't set up to allow subdomains by default in many operating systems. There are ways to do this like defining your own DNS entries that you can look into (/etc/hosts on *UNIX, %system32%/etc/hosts on Windows).
Once you've got your config ready, you'll need to define a Blueprint for a subdomain wildard.
This is done pretty easily:
from flask import Blueprint
from flask.ext.login import current_user
# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")
# Define our route
#deep_blue.route('/')
def user_index(username):
if not current_user.is_authenticated():
# The user needs to log in
return "Please log in"
elif username != current_user.username:
# This is not the correct user.
return "Unauthorized"
# It's the right user!
return "Welcome back!"
The trick here is to make sure the __repr__ for your user object includes a username key. For eg...
class User(db.Model):
username = db.Column(db.String)
def __repr__(self):
return "<User {self.id}, username={self.username}>".format(self=self)
Something to note though is the problem that arises when a username contains special characters (a space, #, ?, etc.) that don't work in a URL. For this you'll need to either enforce restrictions on the username, or properly escape the name first and unescape it when validating it.
If you've got any questions or requests, please ask. Did this during my coffee break so it was a bit rushed.
You can do this with the builtin Flask sessions, which are cookie-based client-side sessions. To allow users to login to multiple subdomains in '.domain.com', you need only to specify
app.config['SESSION_COOKIE_DOMAIN'] = '.domain.com'
and the client's browser will have a session cookie that allows him to login to every Flask instance that is at 'domain.com'.
This only works if every instance of Flask has the same app.secret_key
For more information, also see
Same Flask login session across two applications
I have just started looking at web2py, so many things in the approach are nwe, such as the use of defaults for views etc.
I spent many hours trying to figure out how to get people to log in and in the end put
#auth.requires_login()
at the start of a controller method. This magically brought up a registration form after which the new user was logged in.
I am not sure I fully understand what is going on behind the scenes, but one step at a time.
However, I would like to provide a logout button and have no idea how that might be achieved. Clearly I need to somehow call the default logout. So I need to add a url to the submit form/button and presumably create a controller to match the url, but what will the method to logout look like?
In the user method are the exposes statements, but no idea what they mean or how to use them. All those dots confuse me.
I spent many hours trying to figure out how to get people to log in
Did you read this section of the book? It explains the use of #auth.requires_login() and how the default user() function works to expose the login functionality (as well how to create a separate action specifically for login if desired). In the scaffolding application, the default.py controller includes the following user() function to expose all of the Auth actions:
def user():
return dict(form=auth())
In a URL like "/myapp/default/user/[action]", the requested Auth action (i.e., the last element of the URL) will be available in request.args[0]. When the above function calls auth(), the Auth __call__() method reads the action from request.args[0] and then calls the appropriate Auth method. If the URL is "/myapp/default/user/login", auth() will ultimately call the auth.login() method, which will return (and handle the processing of) the login form. You can also call auth.login() directly if you want to create your own custom login action.
Similarly, the URL "/myapp/default/user/logout" will ultimately call the auth.logout() method, which will log out the user. So, if you want to enable logout, just generate a link to the logout URL -- the best way is to use the URL() function:
A('Logout', _href=URL('default', 'user', args='logout'))
Note, in the scaffolding application, the layout.html view uses auth.navbar() to insert the Auth navbar in the upper right of the page -- when a user is logged in, that navbar automatically includes a "Logout" link like the one above.
Every time a user logs in to the application, I want to perform a certain task, say, record the time of login. So I wanted to know if a hook is fired on login by default? If yes, how can I make my module respond to it.
Edit - Assume there are multiple entry points in the application to login.
While there may well be multiple points of entry, it's crucial that your auth/session code conform to the DRY principle.
Once/if you're down to a single code path for logging in, you should be able to find an appropriate place in that code path to do something as simple as this:
user.last_login = time
user.num_logins++
user.save()
Additionally, you could use a memcache cooldown to make sure this only happens once every, say, 30 minutes:
cooldown_memcache_key = "login_cooldown_%s" % user.id
cooldown = memcache.get(cooldown_memcache_key)
if cooldown is None:
user.last_login = time
user.num_logins++
user.save()
memcache.add(cooldown_key, True, 1800)
I'm using Python on GAE (so it may be different for Java) but have seen no documentation about such a hook for a user logging in. If you used one of the session management frameworks you'd probably get some indication for that, but otherwise I do this kind of house keeping on my opening page itself which requires login. (What do you want to do about an already logged in user returning to your site a few days later... that is, do you really want to record logins or the start time of a visit/session??)
If I wanted to do this but with multiple landing pages, and without using a session framework,
I'd use memcache to do a quick check on every page request and then only write to the datastore when a new visit starts.