Django-cuser, request processing and threads - python

I'm using Apache mod_wsgi to serve django. I'v also use Django-cuser middleware that makes user information always available. And i can't really understand how this middleware works. The specific source code:
class CuserMiddleware(object):
#classmethod
def get_user(cls, default=None):
"""
Retrieve user info
"""
return cls.__users.get(threading.current_thread(), default)
#classmethod
def set_user(cls, user):
"""
Store user info
"""
if isinstance(user, str):
user = User.objects.get(username=user)
cls.__users[threading.current_thread()] = user
Why use threading.current_thread() as a key for user? As i underastand Apache does not create different theads for requests.

Related

How to test django apps when my authorization is remote?

Currently I am using some tests like this:
#pytest.mark.django_db(databases=["default"])
def test_list_boards(api_client):
baker.make(Board)
baker.make(Board, name="new")
url = reverse("v1:boards-list")
response = api_client().get(url)
assert response.status_code == 200
assert len(json.loads(response.content)) == 2
edit:
since I'm not using django User, this is my custom User:
class User:
def __init__(self, token, name, user_id, email, is_mega_user, is_authenticated):
self.token = token
self.name = name
self.user_id = user_id
self.email = email
self.is_mega_user = is_mega_user
self.is_authenticated = is_authenticated
But now, I've added new custom authorization and permission classes in my application, so currently my tests are failling because of it.
And I was thiking: What about when I dont have internet connection? I will not be able to test? How can I handle it?
If you don't need to test the authorization and permission classes, then switch them out for similarly-behaving, but offline, mocks.
Assuming you're using DRF (guessing because of the terminology of "authorization and permission class" and the use of api_client) (though settings changing applies to all frameworks of course), you can
(1) do this in your conftest.py's pytest_configure:
def pytest_configure():
from django.conf import settings
settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] = (...)
(2) or using the pytest-django settings fixture:
def test_without_internets(settings):
settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] = (...)
which you can naturally package into your own fixture (that could be autouse for convenience)
(3) or just plain old with override_settings().

How to access the request in models.py, django

#property
def get_maca(self, request):
if request.user.name == "example":
return self
I want to do something like this. If the user name is example return that object.
How to access the request like this?
The standard way is to pass the request, or in your case just the user object, from the view/router all the way down to the models.
This gets very quickly out of hand in a larger project, so my approach is to use thread local to save some of the request context that I like to have available across the whole project. The thread local storage will keep data available inside a single thread, without it being accessible from other threads - great if you're gonna run the Django app on a production server.
Start with the local storage:
from threading import local
_active_user = local()
def activate_user(user):
if not user:
return
_active_user.value = user
def deactivate_user():
if hasattr(_active_user, "value"):
del _active_user.value
def get_user():
"""Returns `(is_anonymous, user)` ."""
active_user = getattr(_active_user, "value", None)
if active_user and active_user is not AnonymousUser:
try:
return False, active_user
except AttributeError:
pass
return True, None
Now that's all good, you can use this manually. Calling activate_user will make you be able to call get_user in any place in your project. However, this is error prone - if you forget to call deactivate_user, the user object will still be available to the next coming request.
The rest of the answer is to show how to make things automatic.
Let's first make a middleware to clean up by calling deactivate_user after every single request.
class ThreadCleanupMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Executed for each request/response after
# the view is called.
deactivate_user()
return response
Add a path to the ThreadCleanupMiddleware to the end of your settings.MIDDLEWARE list.
Finish up with a view mixin that activates the user automatically (that's for class based views; if you're using functional views, it would be a decorator instead):
class ContextViewSetMixin:
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
if request.user.is_authenticated:
activate_user(request.user)
class ContextModelViewSet(ContextViewSetMixin, viewsets.ModelViewSet):
pass

Creating restricted pages in google app engine

I'm having trouble creating admin pages on my Python Google App Engine site. I think the answer should be pretty straightforward, but honestly, I've been trying to understand how classes inheriting from other classes, or using functions to wrap other functions, and I just can't seem to get a good understanding of it.
Basically, my site has two kinds of pages: the main page, and then some pages that allow the user to perform admin actions. The main page can be seen by anyone without signing in. The other pages are for admins. The only users with accounts are admins, so I've set up webapp2 sessions, and as long as
self.sessions.get('username')
returns something that's enough to be allowed access to the other pages.
Here are my handlers:
class BaseHandler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render(self, template, **kw):
self.response.out.write(render_str(template, **kw))
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
try:
# Dispatch the request.
webapp2.RequestHandler.dispatch(self)
finally:
# Save all sessions.
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
# Returns a session using the default cookie key.
return self.session_store.get_session()
class MainHandler(BaseHandler):
def get(self):
animals = Animal.query().fetch(100)
self.render('index.html',animals=animals)
class AdminHandler(BaseHandler):
def get(self):
if self.session.get('username'):
self.render('admin.html')
else:
self.render('signin.html')
class ReorderHandler(BaseHandler):
def get(self):
self.render('reorder.html')
def post(self):
#Change order of item display
self.write('OK')
class DeleteHandler(BaseHandler):
def get(self):
self.render('delete.html')
def post(self):
#Delete entry from db
self.write('OK')
class AddHandler(BaseHandler):
def get(self):
self.render('add.html')
def post(self):
#add entry to db
self.write('OK')
class SigninHandler(BaseHandler):
def post(self):
#Check username and password
if valid:
self.session['username'] = username
self.redirect('/admin')
else:
self.write('Not valid')
The AdminHandler lays out the basic logic of what these Admin pages should do.
If someone is trying to access an admin pages, the handler should checks to see if user is signed in, and if so, allow access to the page. If not, it renders the sign-in page.
Reorder, Delete, and Add are all actions I want admins to be able to do, but there might be more in the future. I could add the AdminHandler logic to all the GETs and POSTs of those other handlers, but that is extremely repetitive and therefore I am sure that it is the wrong thing to do.
Looking for some guidance on how to get the logic of AdminHandler incorporated into all of the other Handlers that cover "administrative" tasks.
Update: Brent Washburne pointed me in the right direction enough to get the thing working, although I still don't feel like I understand what the decorator function actually does. Anyway, the code seems to be working, and now looks like this:
def require_user(old_func):
def new_function(self):
if not self.session.get('username'):
self.redirect('/signin')
old_func(self)
return new_function
class AdminHandler(BaseHandler):
#require_user
def get(self):
self.render('admin.html')
class AddHandler(BaseHandler):
#require_user
def get(self):
self.render('add.html')
#require_user
def post(self):
name = self.request.get('name')
qry = Animal.query(Animal.name == name).get()
if not qry:
new_animal = Animal(name=name)
new_animal.put()
self.write('OK')
And so on for all the other "admin" Handlers.
Here's a brute-force way to ensure a user is logged in for every page (except the login page), or it redirects them to the login page:
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
if not self.session['username'] and self.request.get('path') != '/login':
return redirect('/login')
A better way is to add this code to the top of every get() and put() routine:
def get(self):
if not self.session['username']:
return redirect('/login')
An even better way is to turn that code into a decorator so all you need to add is one line:
#require_login
def get(self):
....

How to handle complex URL in a elegant way?

I'm writing a admin website which control several websites with same program and database schema but different content. The URL I designed like this:
http://example.com/site A list of all sites which under control
http://example.com/site/{id} A brief overview of select site with ID id
http://example.com/site/{id}/user User list of target site
http://example.com/site/{id}/item A list of items sold on target site
http://example.com/site/{id}/item/{iid} Item detailed information
# ...... something similar
As you can see, nearly all URL are need the site_id. And in almost all views, I have to do some common jobs like query Site model against database with the site_id. Also, I have to pass site_id whenever I invoke request.route_path.
So... is there anyway for me to make my life easier?
It might be useful for you to use a hybrid approach to get the site loaded.
def groupfinder(userid, request):
user = request.db.query(User).filter_by(id=userid).first()
if user is not None:
# somehow get the list of sites they are members
sites = user.allowed_sites
return ['site:%d' % s.id for s in sites]
class SiteFactory(object):
def __init__(self, request):
self.request = request
def __getitem__(self, key):
site = self.request.db.query(Site).filter_by(id=key).first()
if site is None:
raise KeyError
site.__parent__ = self
site.__name__ = key
site.__acl__ = [
(Allow, 'site:%d' % site.id, 'view'),
]
return site
We'll use the groupfinder to map users to principals. We've chosen here to only map them to the sites which they have a membership within. Our simple traversal only requires a root object. It updates the loaded site with an __acl__ that uses the same principals the groupfinder is setup to create.
You'll need to setup the request.db given patterns in the Pyramid Cookbook.
def site_pregenerator(request, elements, kw):
# request.route_url(route_name, *elements, **kw)
from pyramid.traversal import find_interface
# we use find_interface in case we improve our hybrid traversal process
# to take us deeper into the hierarchy, where Site might be context.__parent__
site = find_interface(request.context, Site)
if site is not None:
kw['site_id'] = site.id
return elements, kw
Pregenerator can find the site_id and add it to URLs for you automatically.
def add_site_route(config, name, pattern, **kw):
kw['traverse'] = '/{site_id}'
kw['factory'] = SiteFactory
kw['pregenerator'] = site_pregenerator
if pattern.startswith('/'):
pattern = pattern[1:]
config.add_route(name, '/site/{site_id}/' + pattern, **kw)
def main(global_conf, **settings):
config = Configurator(settings=settings)
authn_policy = AuthTktAuthenticationPolicy('seekrit', callback=groupfinder)
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(ACLAuthorizationPolicy())
config.add_directive(add_site_route, 'add_site_route')
config.include(site_routes)
config.scan()
return config.make_wsgi_app()
def site_routes(config):
config.add_site_route('site_users', '/user')
config.add_site_route('site_items', '/items')
We setup our application here. We also moved the routes into an includable function which can allow us to more easily test the routes.
#view_config(route_name='site_users', permission='view')
def users_view(request):
site = request.context
Our views are then simplified. They are only invoked if the user has permission to access the site, and the site object is already loaded for us.
Hybrid Traversal
A custom directive add_site_route is added to enhance your config object with a wrapper around add_route which will automatically add traversal support to the route. When that route is matched, it will take the {site_id} placeholder from the route pattern and use that as your traversal path (/{site_id} is path we define based on how our traversal tree is structured).
Traversal happens on the path /{site_id} where the first step is finding the root of the tree (/). The route is setup to perform traversal using the SiteFactory as the root of the traversal path. This class is instantiated as the root, and the __getitem__ is invoked with the key which is the next segment in the path ({site_id}). We then find a site object matching that key and load it if possible. The site object is then updated with a __parent__ and __name__ to allow the find_interface to work. It is also enhanced with an __acl__ providing permissions mentioned later.
Pregenerator
Each route is updated with a pregenerator that attempts to find the instance of Site in the traversal hierarchy for a request. This could fail if the current request did not resolve to a site-based URL. The pregenerator then updates the keywords sent to route_url with the site id.
Authentication
The example shows how you can have an authentication policy which maps a user into principals indicating that this user is in the "site:" group. The site (request.context) is then updated to have an ACL saying that if site.id == 1 someone in the "site:1" group should have the "view" permission. The users_view is then updated to require the "view" permission. This will raise an HTTPForbidden exception if the user is denied access to the view. You can write an exception view to conditionally translate this into a 404 if you want.
The purpose of my answer is just to show how a hybrid approach can make your views a little nicer by handling common parts of a URL in the background. HTH.
For the views, you could use a class so that common jobs can be carried out in the __init__ method (docs):
from pyramid.view import view_config
class SiteView(object):
def __init__(self, request):
self.request = request
self.id = self.request.matchdict['id']
# Do any common jobs here
#view_config(route_name='site_overview')
def site_overview(self):
# ...
#view_config(route_name='site_users')
def site_users(self):
# ...
def route_site_url(self, name, **kw):
return self.request.route_url(name, id=self.id, **kw)
And you could use a route prefix to handle the URLs (docs). Not sure whether this would be helpful for your situation or not.
from pyramid.config import Configurator
def site_include(config):
config.add_route('site_overview', '')
config.add_route('site_users', '/user')
config.add_route('site_items', '/item')
# ...
def main(global_config, **settings):
config = Configurator()
config.include(site_include, route_prefix='/site/{id}')

Store objects at "request scope" in google app engine

I would like to store some information at the "request scope" when using google app engine (python). What I mean by this is that I would like to initialize some information when a request is first received, and then be able to access it from anywhere for the life of the request (and only from that request).
An example of this would be if I saved the current user's name at request scope after they were authenticated.
How would I go about doing this sort of thing?
Thanks!
A pattern used in app engine itself seems to be threading.local which you can grep for in the SDK code. Making os.environ request local is done like that in runtime/request_environment.py for example.
A rough example:
import threading
class _State(threading.local):
"""State keeps track of request info"""
user = None
_state = _State()
From elsewhere you could authenticate early on in handler code.
from state import _state
if authentication_passed:
_state.user = user
and provide convenience that can be used in other parts of your code
from state import _state
def get_authenticated_user():
user = _state.user
if not user:
raise AuthenticationError()
return user
You need something like this:-
class BaseHandler(webapp2.RequestHandler):
#A function which is useful in order to determine whether user is logged in
def initialize(self, *a, **kw):
#Do the authentication
self.username = username
class MainHandler(BaseHandler):
def get(self):
print self.username
Now if you inherit BaseHandler class all the request will first go through the initialize method of BaseHandler class and since in the BaseHandler class you are setting the username
and MainHandler inherits form BaseHandler you will have the self.username defined and all the request wil go through initialize method.

Categories