Using threading.local() in a kubernetes container - python

I have a middleware in my app that sets the currently logged in user. On my local machine, get_current_user() works fine, but it seems to return None when the app is run in a kubernetes container. What am I missing?:
USER_ATTR_NAME = getattr(settings, "LOCAL_USER_ATTR_NAME", "_current_user")
_thread_locals = local()
def _do_set_current_user(user_fun):
setattr(_thread_locals, USER_ATTR_NAME, user_fun.__get__(user_fun, local))
def _set_current_user(user=None):
"""
Sets current user in local thread.
Can be used as a hook e.g. for shell jobs (when request object is not
available).
"""
_do_set_current_user(lambda self: user)
class SelfServeCurrentUserMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# request.user closure; asserts laziness;
# memorization is implemented in
# request.user (non-data descriptor)
_do_set_current_user(lambda self: getattr(request, "user", None))
response = self.get_response(request)
return response
def get_current_user():
current_user = getattr(_thread_locals, USER_ATTR_NAME, None)
if callable(current_user):
return current_user()
return current_user
def get_current_authenticated_user():
current_user = get_current_user()
if isinstance(current_user, AnonymousUser):
return None
return current_user

all what you do shows me - you don't really understand, how Django works.
What i mean:
You need User model, somewhere, probably it is form. You don't understand, how to get a user there, and you try to use locals. You made a import of functions get_current_user, get_current_authenticated_user. Now you can achieve a User. This is wrong for Django, but you can do it.
i have a small trick for you in this case:
from django.utils.translation.trans_real import _active as _thread_locals
# _thread_locals = local() you don't need it.
... # other staff
this is what you want. See the commentary about _active in django code

Related

Django APIView to log out of a user session

I have a Django module which is called from an SSO service. The service has a single signout function which makes a single GET request to a URL given to it during login.
I'm trying to set up an APIView in Django to handle this logout. The origin service never checks the response; it only calls the GET URL once.
I'm trying something like this for the APIView but keep getting session.DoesNotExist exceptions:
class LogoutApi(APIView):
def get(self, request, *args, **kwargs):
s = Session.objects.get(session_key=kwargs.get('sk', ''))
s.delete()
return Response({'result': True})
I know I have a valid session but even when I try iterating through the Session.objects I can't find it.
I also tried pulling the key from the SessionStore:
class LogoutApi(APIView):
def get(self, request, *args, **kwargs):
sk = request.GET.get('sk', '')
try:
s = SessionStore(sk)
del s[sk]
return Response({'result': True})
except:
self.logger.error(sys.exc_info()[0])
return Response({'result': False})
It still wasn't successful. Is there a way I can set up a GET API call to terminate a specific session?
Turns out the issue was that the session engine was set to use signed cookies. After I removed the following line from my configuration, all worked as expected:
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" # Removed this line
For reference, this is the logout code that worked with the above setting:
class LogoutApi(APIView):
def get(self, request, *args, **kwargs):
sk = request.GET.get('sk', '')
if sk:
s = SessionStore(session_key=sk)
s.delete()
return Response({'result': True})
return Response({'result': False})

Django Middleware and threading to catch the current user

I'm trying to use catch the Django user in the Middleware but without success. Using Python 3.6 and Django 1.11.
from threading import local
_user = local()
class CurrentUserMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
_user.value = request.user
return self.get_response(request)
def get_current_user():
return _user.value
I need to save the request.user outside the class, in the get_current_user(), but it is not working.
Can someone give me a clue why I can't have the _user.value in the get_current_user() ?
The reason I'm doing this is to import to a model
from current_user import get_current_user
Thanks,

Django: How to extend SessionBase to have access to the Request Object?

I have a custom session class that I've built to extend the Django SessionBase. I did this in order to reuse a legacy Session table, so that sessions can pass between our Django pages and our PHP pages without having the user to log in and back out.
Everything's working perfectly so, far with one huge BUT.
I wrote some custom middleware in order to let the SessionStore.start() function have access to the Request Object. Unfortunately, in order to do that I used this answer: Access request.session from backend.get_user in order to remedy my problem.
I have learned that using the above answer (Essentially binding the request object to the settings, so you can access using import settings* and then settings.request) is totally horrible and the absolutely worst way to do this.
My core problem, is I don't understand how I can access the request from within the custom session backend I've written.
Maybe in middleware you could pass request to your custom SessionStore like this:
request.session = engine.SessionStore(session_key,request)
and in SessionStore:
class SessionStore(SessionBase):
def __init__(self, session_key=None, request):
self.request = request
super(SessionStore, self).__init__(session_key)
Later you can access request as self.request.
Django's SessionMiddleware does this:
class SessionMiddleware(object):
def process_request(self, request):
engine = import_module(settings.SESSION_ENGINE)
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
request.session = engine.SessionStore(session_key)
can't you do this?
import mycustomsessionbackend as myengine
class MyCustomSessionMiddleware(object):
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
request.session = myengine.SessionStore(session_key, request)
...
# mycustomsessionbackend.py
class SessionStore(SessionBase):
def __init__(self, session_key=None, request=None):
super(SessionStore, self).__init__(session_key)
self.request = request

Layout bug with my templates

On all my template rendering for a particular app, the output ends with None:
...</html>None
This must be a bug and probably in my code and I've spent days trying to track it down. There's nothing special about my app and this bug appears on every page I use template rendering, whether I use a seperate template engine or not. There is nothing special about my code:
class Objectives(NewBaseHandler):
#user_required
def get(self):
user = auth_models.User.get_by_id(long(self.auth.get_user_by_session()['user_id']))
if user:
self.render_template('objectives.html', {'user': user})
else:
self.render_template('/', {})
class NewBaseHandler(BaseHandler):
"""
........BaseHandler for all requests
........Holds the auth and session properties so they are reachable for all requests
...."""
def dispatch(self):
"""
............Save the sessions for preservation across requests
........"""
# self.session_store = sessions.get_store(request=self.request)
# if self.request.host.find('localhost') > 0: # for a Swedish domain that uses Swedish
# or lang = os.environ.get("HTTP_ACCEPT_LANGUAGE")
i18n.get_i18n().set_locale('sv')
lang_code_get = self.request.get('hl', None)
if lang_code_get:
#self.session['i18n_language'] = lang_code_get
i18n.get_i18n().set_locale(lang_code_get)
try:
response = super(NewBaseHandler, self).dispatch()
self.response.write(response)
finally:
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def auth(self):
return auth.get_auth()
#webapp2.cached_property
def session_store(self):
return sessions.get_store(request=self.request)
#webapp2.cached_property
def auth_config(self):
"""
............Dict to hold urls for login/logout
........"""
return {'login_url': self.uri_for('login'),
'logout_url': self.uri_for('logout')}
class BaseHandler(webapp2.RequestHandler):
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_template(self, file, template_args):
path = os.path.join(os.path.dirname(__file__), 'templates',
file)
self.response.out.write(template.render(path, template_args))
def render_jinja(self, filename, **template_args):
self.response.write(self.jinja2.render_template(filename,
**template_args))
How can I check where the output None is coming from? It's probably not coming from the template and it doesn't seem to be coming from the handlers and there is no other code.
Thank you
In Objectives.get() you must return a value. Since you don't do this Python assumes the result is None. This value you get in NewBaseHandler.dispatch() when calling to base dispatch implementation and then write it to response.
If I get your app correctly returning empty string in get method will solve the problem.

WebTest: Testing with decorators + datastore calls

I have a Google App Engine application and my request hadnler has a decorator that does authentication. With WebTest I found out yesterday how you can set a logged in user and administrator.
Now today my authentication decorator got a little more complex. It's also checking if a user has a profile in the database and if he doesn't he'll get redirected to the 'new user' page.
def authenticated(method):
#functools.wraps(method)
def wrapper(self, *args, **kwargs):
user = users.get_current_user()
if not user:
self.redirect(users.create_login_url(self.request.uri))
return
profile = Profile.get_by_key_name(str(user.user_id))
if not profile:
self.redirect( '/newuser' )
return method(self, *args, **kwargs)
return wrapper
Now adding the profile part breaks my unit test that checks if a user is logged in and gets a status code 200(assertOK).
def user_ok(self):
os.environ['USER_EMAIL'] = 'info#example.com'
os.environ['USER_IS_ADMIN'] = ''
response = self.get( '/appindex' )
self.assertOK(response)
So now I need to be able to somehow inject the profile functionality into the decorator so I can set it in my tests. Does anybody got an idea how to do this I've been trying to think of a way but I keep getting stuck.
You should create a profile during the test, to be used by the decorator:
def user_ok(self):
key_name = 'info#example.com'
new_user = Profile(key_name=key_name)
new_user.put()
os.environ['USER_EMAIL'] = key_name
os.environ['USER_ID'] = key_name
os.environ['USER_IS_ADMIN'] = ''
response = self.get( '/appindex' )
self.assertOK(response)
# Now let's reset it to check that the user will be redirected.
new_user.delete()
response = self.get( '/appindex' )
self.assertEqual(response.headers['Location'], 'http://localhost/newuser')

Categories

Resources